ncc-2.8/0000755000175000001440000000000011074666537012271 5ustar stanusers00000000000000ncc-2.8/doc/0000755000175000001440000000000011074670231013020 5ustar stanusers00000000000000ncc-2.8/doc/NCC0000644000175000001440000002543010665117564013364 0ustar stanusers00000000000000ncc 2.7 - see doc/CHANGES for the new stuff WHATIS ====== Basically, ncc is a tool for hackers designed to provide program analysis data of C source code. That is program flow and usage of variables. Some big programs out there are by default obfuscated, either due to extreme size, programming style, hacks upon hacks and other crazyness. In order to do program analysis correctly, there has to be compilation of expressions, and thus ncc is really a compiler (supporting zero architectures). At the same time, ncc is small and easy to understand so you can hack it and add custom features and extensions at any stage of the compilation, to match what you expect and consider useful as output. Most common GNU extensions are supported and there has been an effort be practically useful in the GNU system (which is not easy because the GNU system is very gcc-friendly). The goal is to be able to replace 'ncc' in Makefiles and work with the big open source projects. In the latest versions of ncc, some advanced analysis features have been implemented and they are about pointer-to-function variables and the values assigned to them. The sample files doc/fptrs.c and doc/farg.c can be analysed with ncc to demonstrate what can be done there. In version 2.0 ncc can emulate the linker behavior to join ncc output files and can also act as a wrapper for some binutils programs that are used for linking. INSTALL ======= Either 'make install' as root, or: 1. 'make' and the file ncc should appear in objdir/ 2. Copy the file doc/nognu to /usr/include. This file is used to fix some madness of libc header files and remove some GNU extensions which violate the C grammar and can be removed without problems. If you don't want to copy it to /usr/include, edit config.h and recompile. 3. With make the file 'nccnav' should also appear in nccnav. Copy this file to /usr/bin and make a symbolic link nccnavi -> nccnav if you want auto-indent Invoking ncc ------------ USAGE ===== The simple way to use ncc on a C program hello.c, is with the command : ncc hello.c The default output (at stdout) is that for every function defined in hello.c the following things are reported : - function calls - pointer to function assigned values - use of global variables - use of members of structures The latter has been proved very useful in understanding what a function actually does. The options of ncc start with "-nc". with "-ncmv" each use of global variable, function or member of structure is reported multiple times as used. This is a way to understand better how the code works, by looking the use of variables between function calls. -nccnav can't display that- with "-nchelp" help is displayed on the -nc options. with "-ncoo" the output goes to a file sourcefile.c.nccout with "-ncld" the output goes to a file sourcefile.o.nccout or to the file specified with the command line option "-c" and the extension nccout. Moreover in this mode ncc does linker emulation. Described below. with "-ncspp" the preprocessed file is kept in sourcefile.i with "-ncfabs" absolute pathnames are reported. with "-ncnoerror" if there are syntax errors in expressions ncc will link the function with a special "NCC:syntax_error()" function instead of terminating the compilation. STUDYING THE OUTPUT & PROGRAM ANALYSIS ======================================== ncc is a very useful tool in exploring programs but equally useful is the included program `nccnav'. The output of ncc is such that can be easilly loaded by some other graphical viewer. nccnav can do this and has some nice features like the recursion detector. So once you learn HOW to run ncc on programs, make sure to read nccnav/README. The output can also be viewed using the graphviz program. See the section GRAPHVIZ below. GCC-PREPROCESSING ================= ncc uses gcc for preprocessing because the standard library headers eventually need some other architecture specific header which are somewhere where gcc knows where. Any options starting with -D and -I will be passed to gcc for preprocessing. Generally, because ncc should be able to work from makefiles instead of gcc, all options unless starting with '-nc' produce no error (and may be even passed to gcc in a special mode). The files compiled with ncc, will have the __GCC__ macro defined, because many programs are written for gcc and take some gcc extensions for granted. ncc additionally defines __NCC__ macro. (example) HACKING mpg123 ======================== This one is easy (because it's done "the right way", programs are exponential: the number of tasks a program can do is N^2 if N are the lines of code. Thus any program of more than 50000 lines has probably design flaws (unless it's device drivers)) Anyway, to view the calls of mpg123, the command is: for a in *.c; do ncc $a -ncoo; done cat *.nccout > Code.map nccnav or alternativelly: ncc *.c > Code.map HACKING BIGGER PROGRAMS ======================= The obvious way is to use make with ncc, so that the required -D and -I options are invoked, and only the right files are compiled (if there are depenencies). Normally, changing "CC=gcc" to "CC=ncc -ncoo" would be enough. But alas! some times it isn't. Sometimes the make procedure expects object files which ncc does not produce and it may fail. This can be prevented with 'make -i' where make ignores errors. Some other programs compile and run helpers during make. If all else fails, the last resort that always works, is using the "-ncgcc" option. with "-ncgcc", ncc will also run gcc in parallel with all it's options except the -nc ones. So nobody will understand that ncc was even run and the makefiles will be happy. It takes 1000% more time, but computers do get faster every day. In this case, it is generally a good idea to remove any '-O2 -g' options. Make sure you read doc/TROUBLES if you're going to use ncc in big GNU projects (and not only). hacking.LINUX-KERNEL, hacking.GCC, hacking.GLIBC, and other files in doc/ have specific instructions on using ncc on some *really big* programs which may need a couple of tricks to get it done. LINKER EMULATION ================ The best job is done by using ncc in the makefile in parallel with the normal compilation. For serious work, use CC=ncc -ncgcc -ncld -ncspp -ncfabs The option "-ncld" instructs ncc to attempt to link ncc output files as gcc would create object files. For example, the command ncc -ncld -ncgcc -c file1.c will generate the files "file1.o" and "file1.o.nccout" The command ncc -ncld -ncgcc file2.c file1.o -o myprog will generate the files "myprog" and "myprog.nccout", where the second file will be the analysis of file2.c plus the file file1.o.nccout. Moreover, ncc can be called as "nccar", "nccld", "nccg++" and "nccc++". In this case, the corresponding program will be called normally with all the options passed and then ncc will attempt to locate object files with the extension 'nccout' and link them in a bigger nccout file. So it's even better to say AR = nccar LD = nccld CXX = nccg++ The advantage of this method is that we don't have to collect the nccout files afterwards. Usually all the right files will have been concatenated into one big nccout file at the end of compilation. This method works best when gcc is used to link the object files, and also works pretty well with ar and ld. However, there are projects out there that use crazy Makefiles and libtool, that need special hacks to achieve this. Output Details -------------- POINTERS TO FUNCTIONS ===================== ncc does not only report which pointer to function variables are called, but also which values are assigned to them. Pointers to functions are reported as pseudo-functions which call *all* the values assigned to them. So the caller -> callee relation is different. While the code of a normal function calls all its callees, a pointer to function will call any one of its callees. RELEVANT NOTES ============== - Anonymous typedef'd structures are automatically named after the typedef. For example: typedef struct { int x, y; } Point; int main () { Point P; P.x = 3; } ncc will report that 'main' uses member 'x' of structure 'Point', although the structure is really anonymous. Conflicts *should* be extremely rare. TROUBLESHOOTING =============== Thanks to open source, there are infinitive test cases. ncc has been tested with: linux kernel 2.2 (partial according to depend), Imagemagick, gcc, Doom (you gotta see this source!), xanim, mpg123, bladeenc, bzip2, gtk, gnu-fileutils, less, mpeg_play, nasm, ncftp, vim, sox, bind, gdb, flames, xrick (Rick Dangerous!), inform (Text adventure language), linux kernel 2.4.20, zsh, zile, emacs, gnuchess, bash, python, elinks, linux kernel 2.6.9, pygame, jamvm, perl5, linux 2.17, gcc 4.2, x.org 7.1, git, qemu, ffmpeg, libjpeg, openssh although these programs are correct and ncc lacks testing on finding errors on programs with syntax errors. In the case ncc complains about syntax errors or segfaults, check out the file TROUBLES for the way to hunt down the bug. Sometimes just adding something to nognu macros is enough to continue the compilations. Viewers ------- CODEVIZ ======= CodeViz is a collection of C/C++ analysis tools by Mel Gorman. See http://freshmeat.net/projects/codeviz The CodeViz perl scripts recognize the .nccout format of ncc. If you have concatenated all the .nccout files of a projects to say 'everything.nccout' You can do: genfull -g cncc -f everything.nccout to generate the full.graph file. Then: gengraph -f some_function -d depth to generate a nice postscript graph of the call tree. GRAPHVIZ ======== Jose Vasconcellos has sent a python script which converts ncc output to dot data. The dot command is part of the Graphviz package and it's used by codeviz to generate the graph. More about graphviz at: http://www.graphviz.org The script scripts/gengraph.py can be called like this: gengraph.py code.map main | dot -Tsvg -o graph.svg to generate a nice SVG graph of the callgraph. The zgrviewer at http://zvtm.sourceforge.net/zgrviewer.html is great for viewing large graphs. Still, it's a good idea to specify depth because some graphs are **really** big:) THEREST ======= Program written & Copyright (C) Stelios Xanthakis 2002, 2003, 2004, 2005, 2006, 2007 Distributed under the terms of the Artistic License. e-mail: sxanth@ceid.upatras.gr ncc latest download: http://students.ceid.upatras.gr/~sxanth/ncc/ Many contributions and feedback from Scott McKellar who now is the other person who knows `how ncc works'. Additional hacks for kernel 2.6 by Ben Lau gengraph.py is Copyright (C) 2004 Jose Vasconcellos and GPL. Also thanks to: Sylvain Beucler, Anuradha Weeraman, Florian Larysch, Deepak Ravi, Thomas Petazzoni, Adam Shostack, Hakon Lovdal, Awesome Walrus ncc-2.8/doc/hacking.README0000644000175000001440000000017507520255374015316 0ustar stanusers00000000000000 Did you just manage to hack a major program ? Share the knowledge! Send me a hacking.PROGRAM file with the instructions. ncc-2.8/doc/LICENSE0000644000175000001440000001213410311314734014022 0ustar stanusers00000000000000The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: * "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. * "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. * "Copyright Holder" is whoever is named in the copyright or copyrights for the package. * "You" is you, if you're thinking about copying or distributing this Package. * "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) * "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End ncc-2.8/doc/nognu0000644000175000001440000000142211074670023014067 0ustar stanusers00000000000000 #define __attribute__(x) #define __const const #define __signed__ signed #define __volatile__ volatile #define __volatile volatile #define __extension__ #define asm __asm__ #define __asm __asm__ #define __const__ const #define __typeof(x) __typeof__(x) // what is this? Not documented in gcc.info, and // used in the linux kernel source. let's make it 1. #define __builtin_constant_p(x) 1 #define __alignof__ sizeof #define __alignof sizeof #define __attribute(x) #define __PRETTY_FUNCTION__ __func__ // ISOC99 #define _Bool int // New stuff #define __restrict #define __restrict__ #define __builtin_va_list char* #define typeof(x) __typeof__(x) // What is this? #define __builtin_va_arg(x,y) (y)x #define __builtin_types_compatible_p(x, y) 1 #define __builtin_offsetof(x,y) 1 ncc-2.8/doc/ABANDONWARE_GAMES0000644000175000001440000000203607457264645015423 0ustar stanusers00000000000000 How many games must have been written since 1987 ? I'd say at least > 50,000 Who can forget the awesome "Solomon's Key" which enlightened us ? Well, back then I did something really interesting. I connected my Amiga with a tape recorder and recorded the music of some of my favorite games. Now I came across the same tape again and with nostalgy listened to the great music of Lotus Turbo Esprit, Captive and Turrican I. Any game that was written 10 years ago for a 8/16-bit architecture with 256kB of memory at 1MHz has no practical value today. But don't throw it away! Programming is an art and all these games will always have a value for those who like the code (the collector code-ants). If you have any source code of old games or know any author who wrote a game back then or have any links, please, please send me a mail. I'm thinking about making a database of abandonware games. The dream is, that one day ALL the games ever written will be in the database and not a single bit will be lost in oblivion. Save the abandonware games! ncc-2.8/doc/int8_t.redefined.error0000644000175000001440000000076010505305444017226 0ustar stanusers00000000000000You may get this error in a modern GNU system. Check out the preprocessed source (-ncspp option), and search for `int8_t'. If you see a line like typedef int int8_t __attribute__ ((__mode__ (QI))); You've got it. This is serious braindamage from glibc (again). Please edit the file /usr/include/sys/types.h. It sais: #if !__GNU_PREREQ (2,7) ... good typedefs #else ... braindamaged typedefs #endif Change the if to #if !__GNU_PREREQ (2,7) || defined __NCC__ That's perfectly safe. ncc-2.8/doc/hacking.BIND0000644000175000001440000000062207520255063015065 0ustar stanusers00000000000000Hacking Bind (9.1.2) -------------------- As if on purpose, the program bind could not be any more obfuscated. Anyway, our computer can do the job for us. ./configure Run the following script: -------------------------------------- #!/bin/sh for a in $(find . -name Makefile) do cp $a TMP cat TMP | sed 's/gcc/ncc -ncgcc -ncoo/' > $a done -------------------------------------- 'make -i' Voila! ncc-2.8/doc/hacking.QEMU0000644000175000001440000000037210665114755015130 0ustar stanusers00000000000000For qemu 0.9 ------------ run configure with the option ./configure --cc='ncc -ncgcc -ncld -ncfabs' [other options] make nccnav i386-softmmu/qemu.nccout 164 Files 3260 Functions 879 Global variables 335 Structs 2445 Members 42040 Total links ncc-2.8/doc/hacking.GLIBC0000644000175000001440000000217507603744776015216 0ustar stanusers00000000000000glibc (2.0.x) ============= The simplest way to hack glibc is to create a file configparms including: prefix = /trashcan/place CC := ncc -ncgcc -ncoo -ncspp default_flags := and then ./configure PROBLEMS -------- glibc very often uses a macro weak_alias. weak_alias (__strdup, strdup) is expanded to: extern __typeof__(__strdup) strdup __attribute__((weak)); Now, that is strange in many aspects. The __attribute__ keyword disappears from the nognu macros and eventually ncc sees: extern __typeof__(__strdup) strdup; __strdup is a function, and K&R sais that type "function" is converted to "pointer to function" in certain cases in expressions, and typeof gives the type of an expression. So we have a problem here. This is how I did it: - edit libc-symbols.c - add the following lines at the end: #ifdef __NCC__ #undef weak_alias #define weak_alias(x,y) #endif or using ncc keys to show that strdup actually calls __strdup: #ifdef __NCC__ #undef weak_alias #define weak_alias(x,y) \ char x##y##_weakalias [] = \ "ncc-key D: "#y"() F: "#x"() "; #endif - the same thing probably for strong_alias and similar macros ncc-2.8/doc/farg.c0000644000175000001440000000151307661542476014123 0ustar stanusers00000000000000/* run ncc on this file, with : ncc farg.c to see how pointer-to-function function arguments are reported. The new thing is the new output directive 'R' R: A B Should be interpreted by nccnav that the callers of 'A' do not really call 'A' but 'B'. This is a "replacement" directive. */ int f1(){} int f2(){} int f3(){} int f4(){} struct CALLBACK { int (*fp) (); } OBJ; int test () { OBJ.fp (); } int foo (int (*f)()) { f (); } int bar (int (*a)(), int (*b)()) { OBJ.fp = a; foo (a); b (); } int main () { bar (f1, f2); bar (f3, f2); bar (f4, 0); // this is not implemented //bar (OBJ.fp, 0); } // // sample qsort // int strcmp (char*, char*); int qsort (void *v, int n, int (*comp)(void*, void*)) { qsort (v/2, n/2, comp); comp (v[0], v [1]); } void do_sort () { char v [100]; qsort (v, 100, strcmp); } ncc-2.8/doc/fptr.c0000644000175000001440000000222107607356354014152 0ustar stanusers00000000000000// // "analyse *this" // This is a sample dummy program which demonstrates how can // ncc study pointers to functions to report a nicely connected // call graph. // typedef int (*initcall_t)(void); int init_ext2_fs (void) {} static initcall_t __init_module = init_ext2_fs; struct fs_callbacks { int (*open_file)(); int (*close_file)(); int (*read_bytes)(); int (*write_bytes)(); struct fs_callbacks *next; } FileSystems [10]; int open_ext2 () {} int close_ext2 () {} int read_ext2 () {} int write_ext2 () {} int open_ffs () {} int close_ffs () {} int read_ffs () {} int write_ffs () {} struct fs_callbacks FS = { close_file: close_ext2, open_file: open_ext2, read_bytes: read_ext2, write_bytes: write_ext2 }; struct redirector { int (*foo)(); } R = { FS.read_bytes }; int main () { struct fs_callbacks *f = &FileSystems [0]; f->open_file = open_ffs; f->close_file = close_ffs; f->read_bytes = read_ffs; f->write_bytes = write_ffs; application (); } void application () { void *p; FileSystems [2].open_file (); FileSystems [3].next->read_bytes (); (FileSystems [2].next)->write_bytes (); ((struct fs_callbacks*)p)->next->close_file (); } ncc-2.8/doc/KEYS.txt0000644000175000001440000000231107602143067014335 0ustar stanusers00000000000000 ncc KEYS ======== Some times, it is useful to pass additional information to the ncc output. For example resolving handlers or providing the call flow of assembly code. This can be done with the -nckey option. With this option ncc will scan the string literals of the source file for the ones starting with "ncc-key" and print everything the rest of this string to the output. An example, in linux kernel, every one jiffie, the do_timers() is called which marks the timers bottom half handler active. Then, the next time the scheduler will run, it will run do_bottom_half(). do_bottom_half() will then call timer_bh() which will call update_times(), etc. Here is the text resolving that and adding it to the ncc output nicely for nccnav format. /* somewhere inside sched.c .... */ #ifdef __NCC__ char some_unused_name [] = "ncc-key" " D: do_bottom_half() F: timer_bh() "; #endif ------ keys can have names. For instance, -nckey-special-assembly-output-3D will search for the strings starting with: "ncc-key-special-assembly-output-3D" ----- This feature is a bit irrelevant with the compiler and on the other hand can be greatly improved. We keep it as a "hack" to join manually nodes in the call flow graph. ncc-2.8/doc/hacking.LINUX-KERNEL0000644000175000001440000000202011074661774016231 0ustar stanusers00000000000000Hacking linux-kernel 2.6 ************************ Edit the root Makefile to use ncc HOSTCC = ncc -ncgcc -ncfabs -ncld CC = $(CROSS_COMPILE)ncc -ncfabs -ncgcc -ncld AR = $(CROSS_COMPILE)nccar LD = $(CROSS_COMPILE)nccld Also edit the file `scripts/Makefile.build` and search for the line 'cmd_modversions =' and modify it in such a way: ######################################################################### rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \ else \ mv -f $(@D)/.tmp_$(@F) $@; \ + mv -f $(@D)/.tmp_$(@F).nccout $@.nccout; \ fi; endif ######################################################################### Then compile your kernel. If all goes well, the file vmlinux.nccout should appear. Then do nccnav vmlinux.nccout to hack it. If you are building into another object directory (like `make O=/tmp/kernobj`), then it is much better to invoke the nccnav viewer from the root of the source tree. As nccnav /tmp/kernobj/vmlinux.nccout so that pathnames are relative -- and small. ncc-2.8/doc/hacking.GCC0000644000175000001440000000165310502253173014744 0ustar stanusers00000000000000Hacking gcc (almost died from recursion) ---------------------------------------- Analysing gcc works properly by replacing the variables CC = gcc AR = ar to CC = ncc -ncgcc -ncld -ncfabs AR = nccar In the Makefile in the objdir, near the comment --------------------------------------------- Programs producing files for the HOST machine --------------------------------------------- HOWEVER, this does not work right away. What I did was. - first compile gcc with "make bootstrap" with the original CC and AR values (without ncc) - then edit the Makefile and add ncc/nccar - then go to the gcc source inside the gcc/ directory and do "touch *.h" - then go to the objdir and redo "make" That seemed to work but it takes ages. The problem was libiberty. If anyone improves that, send mail. nccnav prev-gcc/cc1.nccout 366 Filez 13132 functions 3442 global variables 773 structs 4424 members 422857 Total links ncc-2.8/doc/TROUBLES0000644000175000001440000000455407607367003014221 0ustar stanusers00000000000000 GENERAL BUG-HUNTING PROCEDURE ----------------------------- ``every new program, reveals more bugs'' For big and complex projects, it's generally a good idea to run the compilation in "script". see "man script". This way you can detect ncc-errors and segfaults. Supposing you see that there was a problem in file drivers/ide/ide-cd.c 1. Run ncc with the -ncspp option 2. Go to the directory with the problematic file and "cp ide-cd.i TR.c" 3. Now you can work with "ncc TR.c" w/o worring about -I/-D options - In the case ncc Segfaulted you may jump "YOU DIDN'T FIND IT" below, instead of working with gdb. - Do "gcc TR.c" to see if the file is indeed wrong C due to nognu macro expansions. If this works then it's an ncc problem. - If you can go to the line where the error occured and see if this is "normal C" or some really unusual syntactic trick. - Running "ncc -nccc TR.c" is a good way to see the last expression compiled before the bug. SO YOU'VE FOUND IT: You can either edit ncc or edit the problematic source file to something that gives the same output with more correct code. That depends on whether you think the compiler should adapt to the program or the program should comply with the rest of the world. YOU DIDN'T FIND IT/YOU ARE BORED TO DO THIS: Please send me the TR.c of the offending file at Any program that uses glibc sockets, will probably have the following problem with glibc header files in the GNU system: socket.h troubles in GNU libc ----------------------------- The libc header files, are "compiler dependant". There is no way around. Either you don't define __GCC__ and half of the things are missing (go figure which), or you define __GCC__ and be prepared to come against a "transparent union" argument to getsockname(). Do we have to rewrite a clean libc? For the good-hearted hacker, some additional work will have to be done. Please edit /usr/include/socket.h See the mess. This has nothing to do with programming, and it is the dreadful perfectionism some people who get excited about their system, get into and screw everything up. Find 'transparent union' and where it sais "#if (!defined __GNUC__ || ..." make it "#if __NCC__ || (!defined __GNUC__ || .." ``people who code too much, tend to get authoritative'' =========================================================================== ncc-2.8/doc/CHANGES0000644000175000001440000003774111074670231014027 0ustar stanusers00000000000000New in 2.8 ========== - ncc now will automatically strip nccout files (this will work if Python is installed). Except from less space, in the previous versions under certain circumstances the nccout files could grow exponentially. - fixed a segfault in link mode without any .c or .o files provided (just an .s file for example) - The temporary filename that is used to view functions with "vi" (or your favorite highlighting editor), is now different depending on the current function. So, 'vi' can store the current position of the cursor for every function and that is more functional. - Robustified parsing of aggregate array initializers that caused many problems with linux 2.6.27. Some assignments to pointers to functions may be missed though. (help from Luiz Fernando Capitulino) - work around __builtin_types_compatible_p() that has a syntax with type arguments confusing the parser. Same for __builtin_offsetof() - ncc now understands __inline__, __inline and __FUNCTION__ so no need to fix them in nognu. New in 2.7 ========== - In the case where in a Makefile we had a target like: gcc foo.c bar.c zoo.c -o program using ncc instead of gcc would fail. Now this case is handled correctly. If -ncgcc is specified gcc will be executed as above. ncc instead will analyse each file and then link the generated .nccout files to program.nccout. (qemu) - Fixed leaving ncurses mode -> xterm -> re-enter ncurses in nccnav. Patch by Sylvain Beucler. - New command line option -nccpp=, useful for using ncc in cross compilation, from Jose Vasconcellos New in 2.6 ========== - '-m*' flags are passed to the preprocessor. For example if '-mmmx' is not used in preprocessing, the macro __MMX__ is not declared and importing will fail. (X11R71) - If nccnav is executed from the root of the source tree, it will show relative file paths (good). - Updated instructions for kernel hacking. Some object files were missed. - Fixed segfault with __gu_val. - Speed up linking nccout object files. - nccnav crashed if it couldn't open the temporary file. New in 2.5 ========== - The nccnav viewer can show functions and files with "vi", thus with syntax coloring. This will happen if you press 'v' on a function. - The nccnav viewer can start from the list of global variables (by pressing 'g' in the initial screen). - nccar will understand the "r" option and replace members in the archive instead of just appending the data. That's the right thing because with the old version, linking archives into bigger archives would result in geometric explosion of the size of the files. - Better type propagation in conditional expressions. (x ? (void*)0 : (struct A*)a)->x; now works (gcc/LIM_DATA) - support for anonymous structures and unions (GNUC): struct X { union { int a, b, c; } }; void f (struct X *x) { x->a = 1; } (linux 2.17) - Fixes to hack linux 2.17: - The "__typeof__ (function)" can be used to declare another function - forward enum declarations - char X[] = ("hello"); // was error - The expression in a Variable Length Array is not constant (and in fact it can call functions). int X [y = foo()]; // was error - Debian package and Doc fixes by Anuradha Weeraman. New in 2.4 ========== - Bugfix (segfault with gcc 4.x) from Florian Larysch. - More configury for 'alloca()' in dbstree. - new command line option '-ncnoerr'. When there are errors in expressions, ncc will not terminate the compilations. Instead it will link all functions that had errors with the special pseudo-function "NCC:syntax_error()". New in 2.3 ========== - Bugfix (segfault with gcc 4.0) from Deepak Ravi. - a typedef name used as a label would cause a syntax error (JamVM) - Applying '*' on a function has no effect. (pygame) void (*F)(); (**F)(); // used to be error - Support for '_Complex' as a declaration specifier. New in 2.2 ========== - Included a LICENSE text in doc so ncc can be part of Debian. - Bugfix/crash in nccnav. Pressing the up-arrow in functions mode caused a segmentation violation because isalpha(KEY_UP) is true. - if the last statement of a compound statement in expression was an __asm__ and that was the first expression of the program we had a segfault. Thanks to Thomas Petazzoni. - Header file fixes for MacOS. (Adam Shostack) - __inline__ is accepted as 'inline'. gcc-3.4.4 broke things again. - more pointer-to-function cases caught: fptr = flag ? p->tp_call : 0; now detects that calling fptr() may call p->tp_call() (python) New in 2.1 ========== - ncc reports access of arrays. For example, in the code int *X; int foo () { X [12] = 1; } it will be reported that foo modifies 'X[]' and uses 'X'. - fix for cygwin. In cygwin read() converts \r\n to \n and for that the bytes read are fewer than the size of the file as reported by stat() and ncc thought there was an error. Thanks to Hakon Lovdal. - comlicated segfault in cygwin due to a small bug. (eu). - even better support for reporting use of members of structures. For example, in: struct X { int i, j }; int foo () { struct X x = { 1, }; } int bar () { struct X x = { .y = 1 }; } int zoo () { (struct X) { .x = 1, .y = 2 }; } it will be reported that foo modifies member 'x' and that bar modifies member 'y' and that zoo modifies both. New in 2.0 ========== - linker emulation mode with the command line switch "-ncld". In this mode ncc generates "nccout object files" as gcc generates object files and then attempts to link them into bigger nccout files just as gcc links object files. - binutils wrapper mode. ncc can be called as 'nccar', 'nccld', 'nccg++' and 'nccc++'. It will normally call the corresponding program and then attempt to collect and link "nccout object files". - gengraph.py: a script that can produce dot data from ncc output. Sent by Jose Vasconcellos - Many small fixes for linux-kernel 2.6.9. ncc is now more relaxed about some cases in which it reported errors. We can assume that ncc is used to analyse correct programs and worry not about semantic checks. The file hacking.LINUX-KERNEL has been updated with instructions to make ncc working again. - Assigning non functions to pointers to functions would cause an error: int (*F)(); int x; F = x; // error F = (int(*)())x; // error too (readline) - Casts would prevent reporting pointer to function variables passed as arguments to other functions. For example: int (*FN)(); foo (FN); // reported ok foo ((int(*)())FN); // missed (elinks) - Local pointer to function variables are named after their function. For example, in: int foo() { int (*fn)()=f1; fn(); } int bar() { int (*fn)()=f2; fn(); } the 'fn' variables are reported as 'foo/fn()' and 'bar/fn()'. In the previous version they were both reported as '*fn()' and one couldn't tell that in reallity foo() calls f1() and bar() calls f2(). New in 1.9 ========== - the statement following switch() need not be compound (lwc). - nccnav will also display any comments preceeding functions when asked to display the text of a function (struct too). They are supposed to be essential in understanding what happens... - /usr/include/nognu macros handle the C99 keyword '_Bool'. /use/include/nognu macros handle '__asm' to be treated syntactically like '__asm__'. Same for __volatile as found in . The crapness never ends. ---Don't forget to Copy the new doc/nognu over the old one--- - Bugfix: typedef int x; int f () { int x; x = 1; } didn't work as the line starting with 'x' was considered a declaration. In other words, the Bugfix from version 1.7 broke more things than it fixed! (discovered in python source). - Bugfix: typedef int (*func)(); int f () {} int main () { func x, y; x = f; // reported ok! y = (func) f; // not reported. BUG } A cast would prevent ncc from reporting pointer to function assignments as pseudo calls. (python source) New in 1.8 ========== - It is very useful to report whether a function *just reads* or *modifies* a variable. ncc now reports such information when it is certain that a variable *is* modified by a function. Read 'README.1.8.rw' for info. - ncc can now handle the case: (x ? F1 : F2) (args) it used to report that just a "virtual call" is there. Now such constructs are converted to: x ? F1 (args) : F2 (args) reported data is much better for hacking and less confusing New in 1.7 ========== - From Ben Lau : - A fix for gcc syntax where __asm__() can be specified in a declaration before initialization. Fix for M68K kernel from uClinux - Made ncc work with 2.6 kernel! There was a problem with declaration initializers and was triggered in fs/afs/super.c (thanks!) - More stuff with gcc-3.2 and preprocessing. Now we pass -O* options to the preprocessor because it enables the definition of the __OPTIMIZE__ macro, without which kernel can't be compiled. - nccnav can regenerate its input file without including dupes (functions of header files reported multiple times). - More features with pointer to functions passed as arguments to other functions. - Bugfix: typedef int x; struct foo { x x; x y; // error! }; Now fixed (linux kernel, fs/ntfs/inode.h) - In the case ncc is compiled with gcc, we define 'alloca' to be '__builtin_alloca' and avoid lots of portability issues on non GNU systems. New in 1.6 ========== - While "structure->member ()" was detected, "(*structure->member) ()" was not. Now it is. (tkDvi) - A SIGPIPE would terminate nccnav if requested to view a very big file which didn't fit in the buffer of "less" and the user pressed 'q'. - New way to view the call graph with pop-ups in nccnav. History mode revised with '<' and '>'. - Analysis of function addresses passed as arguments to other functions. For example, ncc can now report that qsort() calls whichever comp() function is passed to it. See the file doc/farg.c for more. New in 1.5 ========== - The locations of structure declarations are emitted in the output of ncc and nccnav can extract and display them (extremely useful). - ncc now works on systems that don't have mmap. - Adapted to work with the kernel sources without having to edit the weirdness found in ide-cd.h (__u8 short) and parport_pc.c (multiple defininitions of function) - nccnav will indent the source if called with a name other than "nccnav", like "nccnavi". - Created man page so ncc can be included in distributions. New in 1.4 ========== - nccnav now provides "The Recursion Detector". - ncc does no longer complain if something is declared as just void. It seems this is perfectly valid (valgrind source) New in 1.3 ========== - This release includes some changes in the viewer nccnav: It's possible to view the text of functions and entire files with an external viewer. This by default is "indent -kr -st | less". - Internal changes where the dbstrees were changed to templates instead of polymorphic classes. New in 1.2 ========== - Bugfix. Negative floating point values in initializers would cause a segmentation violation. - The preprocessed source may also include #pragma directives. This should've caused some problems, sometimes. - In GNUC extensions, goto may be followed by an expression (tcctest). - `__alignof__' is now simply replaced by `sizeof'. - `__typeof__(type)' now supported - nccnav fixes: very long lines would appear on the next line (because ncurses counts tab as one character). Also the request to display the text of the last function of a file which does not end in a newline would cause a segmentation violation. New in 1.1 ========== - Major Feature: ncc now does full analysis on the pointers to functions and the values assignmed to them; The result is incredibly amazing when working with projects like the linux kernel where there are lots of callbacks in structure members. See the file doc/fptr.c which demonstrates the new features. Also now aggregate initalizers are parsed to catch function calls and values assigned to pointer-to-function members. Several internal changes/cleanups to implement the above features. - Patch to work correctly when in -I,-D the argument is in the next argv[] (Awesome Walrus) - Anonymous structures are named by typedef or by first declared object. - Wide character and string constants L'x' (bash2 source) New in 1.0 ========== - Bugfix. The conditional with omitted operand caused segfault in the usage report mode. - ISOC99 additions. Declarations can appear anywhere in a block (as in C++) Hexadecimal floating point constants supported (problems with HUGE_VAL, which is 0x1.0p2048 in glibc) - Declaration specifiers may be after storage class specifiers. For example, "uint32 static X;" is now acceptable (quicktime source). - If "-nc00" is used together with "-nckey", string constants are included. The option to leave out line numbers is removed. - Function prototypes are not checked anymore. There is a rare problem (discovered in quake source) and since ncc is typically a source code analyser, checking function prototypes is useless. New in 0.9 ========== - New output format and new viewer. Old formats REMOVED (check your options) - Reporting absolute pathnames of source files with "-ncfabs" - Many new things in /usr/include/nognu. __FUNCTION__ now defined as the string literal "__FUNCTION__" because many people use the invalid syntax: printf ("This is " __ FUNCTION__ "\n"); - Fixed critical bug discovered with gcc 3.2. Reallocation in expression parser was broken. - GNUC variable size arrays pseudo-supported (no error). - labeled-statement= identifier:statement and now "if(x)Label:foo();" is working properly (zsh source). - Misc fixes. Lots of testing (an entire CD of sources). New in 0.8 ========== - All fixes in this version were contributed/inspired by Scott McKellar. Include: - Removed comment parsing which was unused. - Made file reporting "right" (off by one) - Made line reporting "right" as well (off by one) - inttree possible signedness bug fixed. - Cleanups at introduce_{anon|named}_struct () - Redundant. Lookup() was called twice in a row for members. - Misc corrections, optimizations and cleanups everywhere. New in 0.7 ========== - Line number information finally included and can be optionally left out with "-ncl0". Only used when reporting syntax errors for now. - Except from, -D, -I options, "-ixxx file" options should also be passed to the preprocessor. - The source file was not unmapped after the lexical analysis, while it should. Also as found by Scott McKellar string literals were allocated twice. Now expect 50% less memory usage. - Implemented in-file user output with keys (see doc/KEYS) - Many "do it right" fixes from Scott McKellar. - Added -ncspp option to keep sourcefile.i preprocessed C files. Extremely useful for debugging failures. - In the case of : typedef struct { int x, y; } zoo; if reporting use of members, the anonymous structure is automatically named `struct zoo'. New in 0.6 ========== - In usage report mode, constant expressions (in array sizes and bitfield sizes), were not calculated if they were not as simple as just a number. So in: extern int foo [32/2]; int foo [16]; It failed because of redefining foo as an array of different size. This now fixed (thanks to Doom source code) - Type of the result of the last subexpression of a compound statement in expression (GNUC), implemented. - More information about calling functions through pointers to functions. If the pointer to function is a simple symbol variable, it is converted to a pseudo-function, calling all the functions assigned to it. Otherwise there is a report that a handler is called. - An __asm__ statement in global used to confuse ncc which reported (ABSENT symbol). Now __asm__ statements in global are ignored (linux kernel source). - A function definition may also be a single __asm__ statement (not compound). Parsed w/o error and ignored. (linux kernel) ncc-2.8/doc/dbstree.aux0000644000175000001440000000160410346073402015166 0ustar stanusers00000000000000\relax \@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}} \@writefile{toc}{\contentsline {section}{\numberline {2}Description of the tree}{1}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Balancing the tree}{1}} \@writefile{toc}{\contentsline {section}{\numberline {3}Characteristics of the tree}{2}} \@writefile{toc}{\contentsline {section}{\numberline {4}Database initialization}{2}} \newlabel{init}{{4}{2}} \@writefile{toc}{\contentsline {section}{\numberline {5}Conclusions}{3}} \@writefile{toc}{\contentsline {section}{\numberline {6}Sample code}{4}} \@writefile{toc}{\contentsline {subsection}{\numberline {6.1}Removing a node}{4}} \newlabel{app:rmv}{{6.1}{4}} \@writefile{toc}{\contentsline {subsection}{\numberline {6.2}Balancing}{5}} \newlabel{code:balance}{{6.2}{5}} \@writefile{toc}{\contentsline {subsection}{\numberline {6.3}Inserting a node}{7}} ncc-2.8/doc/dbstree.dvi0000644000175000001440000004251010346073402015154 0ustar stanusers00000000000000÷ƒ’À;è TeX output 2005.12.08:2010‹ÿÿÿÿ y ý£ ? þ>UYŸÅª©‘ysÛóDÓítG®G®cmr17¹The–7tDynamic“Binary“searcŒqh“treeŽŸŸ’¼ã€óX«Q cmr12ºDecem¬rbSŽer–ê¨8,“2005ŽŽ Áª§ þ\UYóÂÖN ff cmbx12Ä1Ž‘LËInŒÌtros3ductionŽŸß}óKñ`y cmr10²Binary–W¯trees“are“an“impGortan¸ãt“data“organizationޤ in–ú¨whicš¸ãh“searc˜hing“for“an“elemen˜t“is“pGerformed“inŽ¡ó  b> cmmi10µOG²(logŽ‘•Tµn²)–Œ²steps.‘.æDynamic“trees“wš¸ãere“new“elemen˜ts“canŽ¡bGe–~÷added“and“remo•¸ãv“ed›~÷ma“y˜bGecome˜un“balanced˜andŽ¡thš¸ãus–Ømore“sophisticated“binary“tree“implemen˜tationsŽ¡ha•¸ãv“e–ÿsbšGeen“suggested“to“k¸ãeep“the“tree“in“µO˜²(logŽ‘•Tµn²)“lim-Ž¡its.Ž© …v‘ Our–çgoal“is“conš¸ãtrolling“the“heigh˜t“of“a“tree“to“smallŽ¡v‘ÿqÇalues.‘ ÷A‘3balanced–3ºtree“ful lls“this“goal“but“a“treeŽ¡need–7ýnot“necessarily“bGe“balanced“to“ha•¸ãv“e–7ýits“heigh¸ãtŽ¡con¸ãtrolled.ަ‘ The–¡*algorithm“for“an“ecienš¸ãt“implemen˜tation“ofŽ¡dynamic–Íìtree“is“describGed,‘,along“with“comparisonsŽ¡with–UUexisting“dynamic“tree“implemen¸ãtations.ŽŸ#ÕØÄ2Ž‘LËDescription–ffof“the“treeŽŸß}²The–Iybasic“idea“is“using“a“simple“binary“searc¸ãh“treeŽ¡and–gsetting“a“limit“on“it's“heigh¸ãt.‘"üLet“us“use“for“theŽ¡rest–Äof“this“study“the“n•¸ãum“bGer–Ä32“as“heigh¸ãt“limit.‘A…ThisŽ¡restricts–¥êthe“total“n•¸ãum“bšGer–¥êof“no˜des“to“no“more“thanŽ¡2Ÿü^ÿóÙ“ Rcmr7±32Ž‘ #²as–ª§long“as“duplicate“elemenš¸ãts“are“not“allo˜w˜ed.‘8ãW‘ÿ*ªeŽ¡should–5notice“that“due“to“the“logarithmic“nature“ofŽ¡complexit¸ãy‘ÿ*ª,‘¶in–o|real-life“applications“a“balanced“treeŽ¡will–UUnevš¸ãer“reac˜h“suc˜h“v‘ÿqÇalues“for“it's“heigh˜t.ަ‘ SuppGosing–mwš¸ãe“can“ecien˜tly“ful ll“this“condition,Ž¡lošGcating–«ran“elemen¸ãt“in“the“tree“will“b˜e“p˜erformed“inŽ¡no–’more“than“32“steps.‘'ÑT¸ãypically“µOšG²(32)–,:µ<“O˜²(logŽ‘•Tµn²).Ž¡Actually–Lñit“ma¸ãy“result“in“a“few“more“steps.‘¦In“practiceŽ¡it–Ž6is“an“unimpGortan¸ãt“di erence“and“therefore“it“is“asŽ¡fast–UUas“an¸ãy“balanced“tree.ަ‘ Remoš¸ãving–ïan“elemen˜t“from“the“tree“can“bGe“easilyŽ¡done–Ì,without“the“necessit¸ãy“of“a“balancing“opGerationŽŽŽ þ\UY’ï€as–“0long“as“bš¸ãy“remo˜ving“an“elemen˜t“the“heigh˜t“of“theޤ ’ï€tree–-€is“not“increased.‘úIThis“condition“can“bGe“imple-Ž¡’ï€men¸ãted.Ž© ãX’ù€If–½Ûthe“nošGde“to“b˜e“remo•¸ãv“ed–½Ûhas“t•¸ãw“o›½Ûc“hild˜noGdes˜thenŽ¡’ï€the–proGcedure“is“to“ nd“the“biggest“elemen¸ãt“from“theŽ¡’ï€smaller–ÕRones“or“the“smallest“from“the“bigger“ones“andŽ¡’ï€replace–âthe“nošGde“to“b˜e“remo•¸ãv“ed–âwith“this“elemen¸ãt.‘]KAŽ¡’ï€sample–UUcoGde“is“illustrated“in“óò"V cmbx10Å??ŽŽ¦’ù€²Adding–šMa“new“elemen¸ãt“to“the“tree“is“the“most“im-Ž¡’ï€p•Gortan¸ãt›op“eration.‘ÏWhile˜inserting˜the˜elemen¸ãt˜theŽ¡’ï€n•¸ãum“bšGer–aÄof“comparisons“p˜erformed“is“coun¸ãted.‘—TheŽ¡’ï€simple–¸srule,‘:when“this“n•¸ãum“bGer›¸sreac“hes˜32˜the˜treeŽ¡’ï€mš¸ãust–UUbGe“rebalanced,“is“v˜ery“ecien˜t.ަ’ù€Balancing– the“tree“is“accomplished“in“µn“²steps.Ž¡’ï€Ho•¸ãw“ev“er,‘Ë1the–€lfrequency“at“whic¸ãh“the“tree“needs“toŽ¡’ï€bGe–_#balanced“again“is“a“function“ofŸü ‘ ±1Ž‘’VŸ£&‰feãߟ¿˜2ŸþóO Ú\cmmi5³nŽŽŽŽŽ‘©h².‘0As“a“resultŽ¡’ï€adding–[a“new“elemenš¸ãt“is“in“the“great“ma‘Ž8jorit˜y“of“theŽ¡’ï€cases–/done“in“less“than“32“steps;‘but“rebalancing“isŽ¡’ï€more–UUrare“as“the“n•¸ãum“bšGer–UUof“no˜des“increases“Ÿü^ÿ±1ŽŽ‘ÑȲ.ŽŸ ´e’ï€óÂÖN  cmbx12Ç2.1Ž’@Balancing–€the“treeŽŸo’The–‚•binary“tree“can“bGe“en¸ãtirely“reconstructed“in“µnŽ¡’steps–¾Ôusing“a“simple“algorithm.‘®EFirst,‘Ù4an“arra¸ãy“of“µnŽ¡’pšGoin¸ãters–%êis“allo˜cated“an“all“the“elemen¸ãts“of“the“treeŽ¡’ï€are–-placed“in“this“arra¸ãy“in“sorted“order.‘øßLet“us“re-Ž¡’ï€memš¸ãbGer–‹[than“an“alw˜a˜ys-balanced“tree“implemen˜ta-Ž¡’ï€tion–Hrequires“extra“memory“for“the“noGde“structuresŽ¡’ï€and–‰Ätherefore“the“extra“alloGcation“of“the“arra¸ãy“isŽ¡’ï€y•¸ãet›Hòecien“t;‘ÂÁfurthermore,‘…Ùin˜moGdern˜m“ultithreadedŽ¡’ï€database–=ôsystems“an“opGeration“whic¸ãh“alters“part“ofŽ¡’ï€the–ýÛdata“is“givš¸ãen“full“con˜trol“of“the“hardw˜are“and“theŽ’ï€Ÿ å‰ff[ÌqŸ J=‘ "5Ÿý-:ó¹Aa¨cmr6¼1ŽŽŽ‘LÜó|{Ycmr8»A‘u”w•ÃŽosre-case›u½bof“no˜des“µn“²divided“with“the“n•¸ãum“b˜er‘å>ofŽ¡’€steps–õ2spšGen¸ãt“for“balancing,‘)tends“to“b˜e“indep˜en-Ž¡’€den¸ãt–UUof“µn².ŽŸ1Ã’ù€T‘ÿ*ªests›«rha•¸ãv“e˜sho“wn˜that˜the˜tree˜will˜need˜to˜balanceŽ¡’ï€2–ÛŠtimes“for“1,000,000“elemen¸ãts“arriving“in“random“or-Ž¡’ï€der,‘XUonce–ñVafter“20,000“elemen¸ãts“and“once“again“atŽ¡’ï€abGout‘UU400,000.ŽŸ –’ï€Ä4Ž’ÌËDatabase‘ffinitializationŽŸç’One–$ocase“wš¸ãere“the“suggested“tree“w˜ould“pro˜v˜e“inef-Ž¡’ï€ cienš¸ãt–B\is“inserting“a“lot“of“elemen˜ts“in“sorted“orderŽ¡’ï€in–fan“óý': cmti10Èempty‘ÿã²tree.‘ÉúIf“the“tree“is“not“empt¸ãy“and“hasŽ¡’ï€bšGeen–\nbalanced“b˜efore“then“the“arriving“elemen¸ãts“willŽ¡’ï€bšGe–UUdistributed“on“the“external“no˜des.Ž¡’ù€This–Ois“a“case“whicš¸ãh“w˜ould“oGccur“when“ini-Ž¡’ï€tially–ºloading“the“data“from“storage“devices.‘ ÷TheŽ¡’ï€wš¸ãork‘ÿqÇaround–±to“this“problem“is“similar“to“the“w˜a˜y“theŽ¡’ï€tree–Ñis“balanced.‘äõThe“-sorted-“elemenš¸ãts“will“ha˜v˜e“toŽ¡’ï€bGe–ÄBinserted“in“the“tree“in“the“order“that“they“willŽ¡’ï€makš¸ãe–8Èa“balanced“tree,‘>~ rst“the“middle“elemen˜t,‘>~µNAÅ=²4,Ž¡’ï€3–8ณµNAÅ=²4–UUand“so“on.Ž¡’ù€Once––Va“pGerfectly“balance“tree“is“constructed“thereŽ¡’ï€should–J§bGe“no“wš¸ãorry“for“this“w˜orse-case“oGccurring“un-Ž¡’ï€less–UUmost“of“the“elemenš¸ãts“are“remo˜v˜ed.Ž¡’ù€Generally‘ÿ*ª,‘tin–L{cases“wš¸ãere“a“long“sequence“of“elemen˜tsŽ¡’ï€is–£to“bšGe“inserted“in“the“tree,‘ Çif“the“tree“has“few“no˜desŽ¡’ï€it–|maš¸ãy“bGe“preferable“to“sort“the“sequence“of“elemen˜tsŽ¡’ï€and–œothen“insert“the“righš¸ãt“elemen˜ts“ rst.‘GThis“proGce-Ž¡’ï€dure–Ø{is“wš¸ãorth“the“tradeo “if“the“arriving“elemen˜ts“areŽ¡’ï€almost–ÿýsorted,‘*¦and“in“this“case“spGecial“sorting“algo-Ž¡’ï€rithms–UUcan“bGe“applied.Ž¡’ù€Another– solution“is“to“makš¸ãe“the“elemen˜ts“reallyŽ¡’ï€random,‘ì±b¸ãy–Îlusing“a“random“tree.‘Ý In“a“Èr–ÿ}'andom‘Ntr“e“e²,ŽŽŽŽŽŸ’è2ŽŽŒ‹J y ý£ ? ýä²the–1comparison“of“elemen¸ãts“is“heads“or“tails.‘óAfterޤ the–~isequence“of“elemen¸ãts“has“bGeen“inserted“in“a“ran-Ž¡dom–0Útree“(whicš¸ãh“will“nev˜er“bGe“un˜balanced),‘8&they“canŽ¡bGe–UUread“in-order“and“sen¸ãt“to“the“dbs“tree.ŽŸ!ÄÄ5Ž‘LËConclusionsŽŸç²The–7Ütree“structure“with“the“cš¸ãharacteristics“that“ha˜v˜eŽ¡bGeen–Ø_presenš¸ãted“is“ecien˜t“for“spGeci c“database“re-Ž¡quiremen¸ãts.Ž¡‘ When–šÛthe“database“is“idle“or“the“spGeci c“tree“isŽ¡not– bused,‘úis“consumes“the“least“memory“compared“toŽ¡other–Ãðbalanced“tree“structures.‘½™That“is“the“primaryŽ¡reason–UUfor“the“design“of“this“structure.Ž¡‘ When–~ithe“tree“is“just“accessed“for“loGcating“an“ele-Ž¡menš¸ãt–UUthis“is“as“fast“as“an˜y“balanced“tree.Ž¡‘ Theoretically‘ÿ*ª,‘ -èbalanced– hÿtree“implemen¸ãtationsŽ¡whic¸ãh–N5pšGerform“rotations“after“a“no˜de“is“inserted“orŽ¡remo•¸ãv“ed–>nfrom“the“tree“are“bGetter.‘-In“practice,‘x´andŽ¡more–¿ûspGeci cly“in“a“database“en•¸ãvironmen“t‘¿ûlimitingŽ¡the–Ÿýheighš¸ãt“of“the“tree“to“a“`small'“n˜um˜bGer“is“moreŽ¡than–ì)adequate.‘6CExcept“from“memory“eciency‘ÿ*ª,‘ÞtheŽ¡suggested–ö€tree“structure“ma¸ãy“pšGossibly“b˜eha•¸ãv“e‘ö€fasterŽ¡as–úwš¸ãell;‘bÌthe“w˜orse“case“scenario“will“either“oGccur“atŽ¡the–Üoinitialization“of“the“database“or“else“is“can“bGeŽ¡easilly‘UUprev•¸ãen“ted.ŽŽŽŽŽŸ’è3ŽŽŒ‹'ày ý£ ? ýäÄ6Ž‘LËSample‘ffcos3deŽŸç²A›iãsample–iéimplemen¸ãtation“in“C˜of“the“presenš¸ãted“tree“is“pro˜vided.‘¯‚F‘ÿ*ªor“the“sample“coGde“the“simple“de nitionޤ of–÷¹a“nošGde“with“a“p˜oin¸ãter“to“data“is“used.‘R“The“function“óßêÿÜif–?ý(compare“(np->data,“n->data)“>“0)Ž¡‘SÿÐif–?ý(np->less“==“n)“break;Ž¡‘SÿÐelse–?ýnp“=“np->less;Ž¡‘>ÿÜelseŽ¡‘SÿÐif–?ý(np->more“==“n)“break;Ž¡‘SÿÐelse–?ýnp“=“np->more;Ž¡¡‘ÿôif–?ý(!(n->less“&&“n->more))Ž¡‘ÿô{Ž¡‘)ÿèif–?ý(isroot)“root“=“(n->less)“?“n->less“:“n->more;Ž¡‘)ÿèelseŽ¡‘>ÿÜif–?ý(np->less“==“n)“np->less“=“(n->less)“?“n->less“:“n->more;Ž¡‘>ÿÜelse–?ýnp->more“=“(n->less)“?“n->less“:“n->more;Ž¡‘)ÿèreturn;Ž¡‘ÿô}Ž¡‘ÿôfor–?ý(i“=“0,“nlp“=“NULL,“nl“=“n->less;“nl->more;“i++)Ž¡‘)ÿènlp–?ý=“nl,“nl“=“nl->more;Ž¡‘ÿôfor–?ý(j“=“0,“nrp“=“NULL,“nr“=“n->more;“nr->less;“j++)Ž¡‘)ÿènrp–?ý=“nr,“nr“=“nr->less;Ž¡‘ÿôif–?ý(i“>=“j)ŽŽŸ’è²4ŽŽŒ‹-‡ y ý£ ? ýä‘ÿôÉ{ޤ ‘)ÿèif–?ý(isroot)“root“=“nl;Ž¡‘)ÿèelseŽ¡‘>ÿÜif–?ý(np->less“==“n)“np->less“=“nl;Ž¡‘>ÿÜelse‘N¿Ónp->more–?ý=“nl;Ž¡‘)ÿèif‘?ý(nlp)Ž¡‘)ÿè{Ž¡‘D?Ùnlp->more–?ý=“nl->lessŽ¡‘D?Ùnl->less–?ý=“n->less;Ž¡‘)ÿè}Ž¡‘)ÿènl->more–?ý=“n->more;Ž¡‘ÿô}–?ýelse“{Ž¡‘)ÿèif–?ý(isroot)“root“=“nr;Ž¡‘)ÿèelseŽ¡‘>ÿÜif–?ý(np->less“==“n)“np->less“=“nr;Ž¡‘>ÿÜelse‘N¿Ónp->more–?ý=“nr;Ž¡‘)ÿèif‘?ý(nrp)Ž¡‘)ÿè{Ž¡‘>ÿÜnrp->less–?ý=“nr->moreŽ¡‘>ÿÜnr->more–?ý=“n->more;Ž¡‘)ÿè}Ž¡‘)ÿènr->less–?ý=“n->less;Ž¡‘ÿô}Ž¡}ŽŸlGÇ6.2Ž‘ÀBalancingŽŸõ½²The–UUalgorithm“for“reconstructing“a“balanced“tree.‘qÇÉalloca“²is“a“stacš¸ãk“alloGcation“mec˜hanism.ŽŸ Dÿ‘ SpšGecial–E:care“should“b˜e“takš¸ãen“that“the“v‘ÿqÇariables“Éi,–?ýj,“D,“k–E:²in“Ébalance()“²are“64“bit“in˜tegers.‘liIf“not“thenŽ¡the–UUalgorithm“will“fail“for“trees“with“more“than“abšGout“65000“no˜des.ŽŸXüÉunsigned–?ýint“suint;Ž¡node‘?ý**snpp;Ž¡¡void–?ýcount_nodes“(node“*n)Ž¡{Ž¡‘ÿô++suint;Ž¡‘ÿôif–?ý(n->less)“count_nodes“(n->less);Ž¡‘ÿôif–?ý(n->more)“count_nodes“(n->more);Ž¡}Ž¡void–?ýtree_to_array“(node“*n)Ž¡{Ž¡‘ÿôif–?ý(!n)“return;Ž¡‘ÿôtree_to_array‘?ý(n->less);Ž¡‘ÿô*snpp++–?ý=“n;Ž¡‘ÿôtree_to_array‘?ý(n->more);ŽŽŸ’è²5ŽŽŒ‹4S y ý£ ? ýäÉ}ޤ void–?ýbalance“()Ž¡{Ž¡‘ÿônode‘?ý**npp;Ž¡‘ÿôlong–?ýint“i,“j,“D,“k,“k2,“k3;Ž¡¡‘ÿô/**–?ýGenerate“a“sorted“array“**/Ž¡‘ÿôsuint–?ý=“0;Ž¡‘ÿôcount_nodes‘?ý(root);Ž¡‘ÿônpp–?ý=“snpp“=“(node**)“alloca“(sizeof“(node*)“*“suint);Ž¡‘ÿôtree_to_array‘?ý(root);Ž¡‘ÿô/**–?ýInsert“the“top“2^k“-1“nodes“**/Ž¡‘ÿôroot–?ý=“npp“[suint“/“2];Ž¡‘ÿôfor–?ý(i“=“4;“i“<=“suint“+“1;“i“*=“2)Ž¡‘)ÿèfor–?ý(j“=“2;“j“<“i;“j“+=“4)Ž¡‘)ÿè{Ž¡‘>ÿÜk2–?ý=“suint“*“j“/“i;Ž¡‘>ÿÜnpp–?ý[k2]->less“=“npp“[suint“*“(j“-“1)“/“i];Ž¡‘>ÿÜnpp–?ý[k2]->more“=“npp“[suint“*“(j“+“1)“/“i];Ž¡‘)ÿè}Ž¡‘ÿô/**–?ýTest“whether“there“are“nodes“left“**/Ž¡‘ÿôif–?ý((k“=“suint“+“1“-“i“/“2))“==“0)Ž¡‘ÿô{Ž¡‘)ÿèfor–?ý(i“/=“2,“j“=“1;“j“<“i;“j“+=“2)Ž¡‘>ÿÜk2–?ý=“suint“*“j“/“i,Ž¡‘>ÿÜnpp–?ý[k2]->less“=“npp“[k2]->more“=“NULL;Ž¡‘)ÿèreturn;Ž¡‘ÿô}Ž¡‘ÿô/**–?ýProceed“normally“but“for“specific“nodes“**/Ž¡‘ÿôfor–?ý(j“=“2;“j“<“i;“j“+=“4)Ž¡‘ÿô{Ž¡‘)ÿèk3–?ý=“suint“*“j“/“i;Ž¡‘)ÿèD–?ý=“(k2“=“suint“*“(j“-“1)“/“i)“*“(i“/“2)“%“suint;Ž¡‘)ÿèif–?ý(D“>=“k“||“D“==“0)‘ÿô/*“k2“Excluded“*/Ž¡‘>ÿÜnpp–?ý[k3]->less“=“NULL;Ž¡‘)ÿèelse–?ý{‘c¿Ç/*“k2“Inserted“*/Ž¡‘>ÿÜnpp–?ý[k3]->less“=“npp“[k2];Ž¡‘>ÿÜnpp–?ý[k2]->less“=“npp“[k2]->more“=“NULL;Ž¡‘)ÿè}Ž¡‘)ÿèD–?ý=“(k2“=“suint“*“(j“+“1)“/“i)“*“(i“/“2)“%“suint;Ž¡‘)ÿèif–?ý(D“>=“k“||“D“==“0)Ž¡‘>ÿÜnpp–?ý[k3]->more“=“NULL;Ž¡‘)ÿèelse‘?ý{Ž¡‘>ÿÜnpp–?ý[k3]->more“=“npp“[k2];Ž¡‘>ÿÜnpp–?ý[k2]->less“=“npp“[k2]->more“=“NULL;Ž¡‘)ÿè}ŽŽŸ’è²6ŽŽŒ‹9l y ý£ ? ýä‘ÿôÉ}ޤ ‘ÿôinsert–?ý(npp“[0]);Ž¡}ŽŸþ6Ç6.3Ž‘ÀInserting–€a“no`deŽŸuT²The–UUproGcedure“is“similar“to“that“of“a“simple“binary“searc¸ãh“tree.ŽŸÉint–?ýinsert“(node“*n)Ž¡{Ž¡‘ÿônode‘?ý*np;Ž¡‘ÿôint‘?ýh;Ž¡¡‘ÿôn->less–?ý=“n->more“=“NULL;Ž¡‘ÿôif–?ý(!(np“=“root))Ž¡‘ÿô{Ž¡‘)ÿèroot–?ý=“n;Ž¡‘)ÿèreturn‘?ý0;Ž¡‘ÿô}Ž¡‘ÿôfor–?ý(h“=“0;;“h++)Ž¡‘)ÿèif–?ý(compare“(np->data,“n->data)“>“0)Ž¡‘>ÿÜif–?ý(!np->less)“{“np->less“=“n;“break;“}Ž¡‘>ÿÜelse–?ýnp“=“np->less;Ž¡‘)ÿèelseŽ¡‘>ÿÜif–?ý(!np->more)“{“np->more“=“n;“break;“}Ž¡‘>ÿÜelse–?ýnp“=“np->more;Ž¡‘?ñif–?ý(h“==“MAX_HEIGHT)Ž¡‘?ñ{Ž¡‘/?åbalance‘?ý();Ž¡‘/?åreturn‘?ý0;Ž¡‘?ñ}–?ýelse“if“(h“>“MAX_HEIGHT)“{Ž¡‘/?å//–?ýIf“that“ever“happens“please“send“me“a“mail“and“I'll“sendŽ¡‘/?å//–?ýyou“10.000$“for“having“a“tree“with“4bill“nodesŽ¡‘/?å//–?ýor“make“MAX_HEIGHT“34“and“go“to“16billsŽ¡‘/?åprintf–?ý("Tree“full.“Reached“2^%i“nodes\n",“MAX_HEIGHT);Ž¡‘/?åreturn‘?ý-1;Ž¡‘?ñ}Ž¡‘?ñreturn‘?ýh;Ž¡}ŽŽŸ’è²7ŽŽŒø?{ƒ’À;èyÕ óßê cmmi10ó 0e—rcmmi7óO Ú\cmmi5óKñ`y cmr10óÙ“ Rcmr7óú±u cmex10ùC´ßßßßßßßncc-2.8/doc/dbstree.log0000644000175000001440000000653410346073402015161 0ustar stanusers00000000000000This is TeX, Version 3.14159 (Web2C 7.4.5) (format=latex 2004.10.4) 8 DEC 2005 20:10 **dbstree.tex (./dbstree.tex LaTeX2e <2001/06/01> Babel and hyphenation patterns for american, french, german, ngerman, n ohyphenation, loaded. (/usr/share/texmf/tex/latex/base/article.cls Document Class: article 2001/04/21 v1.4e Standard LaTeX document class (/usr/share/texmf/tex/latex/base/size10.clo File: size10.clo 2001/04/21 v1.4e Standard LaTeX file (size option) ) \c@part=\count79 \c@section=\count80 \c@subsection=\count81 \c@subsubsection=\count82 \c@paragraph=\count83 \c@subparagraph=\count84 \c@figure=\count85 \c@table=\count86 \abovecaptionskip=\skip41 \belowcaptionskip=\skip42 \bibindent=\dimen102 ) No file dbstree.aux. \openout1 = `dbstree.aux'. LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 6. LaTeX Font Info: ... okay on input line 6. LaTeX Font Info: External font `cmex10' loaded for size (Font) <12> on input line 8. LaTeX Font Info: External font `cmex10' loaded for size (Font) <8> on input line 8. LaTeX Font Info: External font `cmex10' loaded for size (Font) <6> on input line 8. LaTeX Font Info: External font `cmex10' loaded for size (Font) <7> on input line 13. LaTeX Font Info: External font `cmex10' loaded for size (Font) <5> on input line 13. LaTeX Warning: Reference `app:rmv' on page 1 undefined on input line 48. LaTeX Warning: Reference `init' on page 1 undefined on input line 61. [1 ] LaTeX Warning: Reference `code:balance' on page 2 undefined on input line 96. LaTeX Font Info: Try loading font information for OMS+cmr on input line 106. (/usr/share/texmf/tex/latex/base/omscmr.fd File: omscmr.fd 1999/05/25 v2.5h Standard LaTeX font definitions ) LaTeX Font Info: Font shape `OMS/cmr/m/n' in size <10> not available (Font) Font shape `OMS/cmsy/m/n' tried instead on input line 106. Underfull \hbox (badness 1354) in paragraph at lines 142--147 []\OT1/cmr/m/n/10 This is a case which would oc-cur when ini- [] [2] Underfull \hbox (badness 7649) in paragraph at lines 175--182 []\OT1/cmr/m/n/10 Theoretically, bal-anced tree im-ple-men-ta-tions [] [3 ] [4 ] [5] [6] [7] (./dbstree.aux) LaTeX Warning: There were undefined references. LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. ) Here is how much of TeX's memory you used: 254 strings out of 95847 2672 string characters out of 1195962 50529 words of memory out of 1000001 3256 multiletter control sequences out of 10000+50000 8718 words of font info for 31 fonts, out of 500000 for 1000 14 hyphenation exceptions out of 1000 23i,8n,19p,163b,301s stack positions out of 1500i,500n,5000p,200000b,5000s Output written on dbstree.dvi (7 pages, 17736 bytes). ncc-2.8/doc/dbstree.tex0000644000175000001440000003224107455545034015206 0ustar stanusers00000000000000\documentclass[twocolumn]{article} \title{The Dynamic Binary search tree} \author{} \begin{document} \maketitle \section{Introduction} Binary trees are an important data organization in which searching for an element is performed in $O(\log{n})$ steps. Dynamic trees were new elements can be added and removed may become unbalanced and thus more sophisticated binary tree implementations have been suggested to keep the tree in $O(\log{n})$ limits. Our goal is controlling the height of a tree to small values. A balanced tree fulfills this goal but a tree need not necessarily be balanced to have its height controlled. The algorithm for an efficient implementation of dynamic tree is described, along with comparisons with existing dynamic tree implementations. \section{Description of the tree} The basic idea is using a simple binary search tree and setting a limit on it's height. Let us use for the rest of this study the number 32 as height limit. This restricts the total number of nodes to no more than $2^{32}$ as long as duplicate elements are not allowed. We should notice that due to the logarithmic nature of complexity, in real-life applications a balanced tree will never reach such values for it's height. Supposing we can efficiently fulfill this condition, locating an element in the tree will be performed in no more than 32 steps. Typically $O(32)data, n->data) > 0) if (np->less == n) break; else np = np->less; else if (np->more == n) break; else np = np->more; if (!(n->less && n->more)) { if (isroot) root = (n->less) ? n->less : n->more; else if (np->less == n) np->less = (n->less) ? n->less : n->more; else np->more = (n->less) ? n->less : n->more; return; } for (i = 0, nlp = NULL, nl = n->less; nl->more; i++) nlp = nl, nl = nl->more; for (j = 0, nrp = NULL, nr = n->more; nr->less; j++) nrp = nr, nr = nr->less; if (i >= j) { if (isroot) root = nl; else if (np->less == n) np->less = nl; else np->more = nl; if (nlp) { nlp->more = nl->less nl->less = n->less; } nl->more = n->more; } else { if (isroot) root = nr; else if (np->less == n) np->less = nr; else np->more = nr; if (nrp) { nrp->less = nr->more nr->more = n->more; } nr->less = n->less; } } \end{verbatim} \subsection{Balancing} \label{code:balance} The algorithm for reconstructing a balanced tree. {\tt alloca} is a stack allocation mechanism. Special care should be taken that the variables {\tt i, j, D, k} in {\tt balance()} are 64 bit integers. If not then the algorithm will fail for trees with more than about 65000 nodes. \begin{verbatim} unsigned int suint; node **snpp; void count_nodes (node *n) { ++suint; if (n->less) count_nodes (n->less); if (n->more) count_nodes (n->more); } void tree_to_array (node *n) { if (!n) return; tree_to_array (n->less); *snpp++ = n; tree_to_array (n->more); } void balance () { node **npp; long int i, j, D, k, k2, k3; /** Generate a sorted array **/ suint = 0; count_nodes (root); npp = snpp = (node**) alloca (sizeof (node*) * suint); tree_to_array (root); /** Insert the top 2^k -1 nodes **/ root = npp [suint / 2]; for (i = 4; i <= suint + 1; i *= 2) for (j = 2; j < i; j += 4) { k2 = suint * j / i; npp [k2]->less = npp [suint * (j - 1) / i]; npp [k2]->more = npp [suint * (j + 1) / i]; } /** Test whether there are nodes left **/ if ((k = suint + 1 - i / 2)) == 0) { for (i /= 2, j = 1; j < i; j += 2) k2 = suint * j / i, npp [k2]->less = npp [k2]->more = NULL; return; } /** Proceed normally but for specific nodes **/ for (j = 2; j < i; j += 4) { k3 = suint * j / i; D = (k2 = suint * (j - 1) / i) * (i / 2) % suint; if (D >= k || D == 0) /* k2 Excluded */ npp [k3]->less = NULL; else { /* k2 Inserted */ npp [k3]->less = npp [k2]; npp [k2]->less = npp [k2]->more = NULL; } D = (k2 = suint * (j + 1) / i) * (i / 2) % suint; if (D >= k || D == 0) npp [k3]->more = NULL; else { npp [k3]->more = npp [k2]; npp [k2]->less = npp [k2]->more = NULL; } } insert (npp [0]); } \end{verbatim} \subsection{Inserting a node} The procedure is similar to that of a simple binary search tree. \begin{verbatim} int insert (node *n) { node *np; int h; n->less = n->more = NULL; if (!(np = root)) { root = n; return 0; } for (h = 0;; h++) if (compare (np->data, n->data) > 0) if (!np->less) { np->less = n; break; } else np = np->less; else if (!np->more) { np->more = n; break; } else np = np->more; if (h == MAX_HEIGHT) { balance (); return 0; } else if (h > MAX_HEIGHT) { // If that ever happens please send me a mail and I'll send // you 10.000$ for having a tree with 4bill nodes // or make MAX_HEIGHT 34 and go to 16bills printf ("Tree full. Reached 2^%i nodes\n", MAX_HEIGHT); return -1; } return h; } \end{verbatim} \end{document} ncc-2.8/Makefile0000644000175000001440000000375511074134575013733 0ustar stanusers00000000000000 # these are set by config CC = g++ DESTDIR ?= /usr LCFLAGS = -g -O2 BINDIR = ${DESTDIR}/bin MANDIR = ${DESTDIR}/share/man INCLUDEDIR = ${DESTDIR}/include NOGNU = /usr/include/nognu # CFLAGS = $(LCFLAGS) -c tout: objdir/ncc nccnav/nccnav @echo Salut. install: tout cp objdir/ncc $(BINDIR)/ncc cp scripts/nccstrip2.py $(BINDIR)/nccstrip2.py ln -sf $(BINDIR)/ncc $(BINDIR)/nccar ln -sf $(BINDIR)/ncc $(BINDIR)/nccld ln -sf $(BINDIR)/ncc $(BINDIR)/nccc++ ln -sf $(BINDIR)/ncc $(BINDIR)/nccg++ cp nccnav/nccnav $(BINDIR)/nccnav ln -fs $(BINDIR)/nccnav $(BINDIR)/nccnavi cp ncc.1 $(MANDIR)/man1 cp nccnav/nccnav.1 $(MANDIR)/man1 cp doc/nognu $(INCLUDEDIR) uninstall: rm -f $(BINDIR)/ncc $(BINDIR)/nccnav $(BINDIR)/nccnavi $(MANDIR)/man1/ncc.1 $(INCLUDEDIR)/nognu rm -f $(BINDIR)/nccar $(BINDIR)/nccld $(BINDIR)/nccc++ $(BINDIR)/nccg++ nccnav/nccnav: nccnav/nccnav.C @echo Compiling nccnav viewer. @cd nccnav && make objdir/ncc: objdir/dbstree.o objdir/inttree.o objdir/lex.o objdir/space.o objdir/cexpand.o objdir/cdb.o objdir/parser.o objdir/ccexpr.o objdir/preproc.o objdir/usage.o main.C $(CC) $(LCFLAGS) main.C objdir/*.o -o objdir/ncc objdir/cexpand.o: cexpand.C $(CC) $(CFLAGS) cexpand.C @mv cexpand.o objdir/ objdir/parser.o: parser.C $(CC) $(CFLAGS) parser.C @mv parser.o objdir/ objdir/inttree.o: inttree.[Ch] $(CC) $(CFLAGS) inttree.C @mv inttree.o objdir/ objdir/dbstree.o: dbstree.[Ch] $(CC) $(CFLAGS) dbstree.C @mv dbstree.o objdir/ objdir/lex.o: lex.C $(CC) $(CFLAGS) lex.C @mv lex.o objdir/ objdir/cdb.o: cdb.C $(CC) $(CFLAGS) cdb.C @mv cdb.o objdir/ objdir/space.o: space.C $(CC) $(CFLAGS) space.C @mv space.o objdir/ objdir/usage.o: usage.C $(CC) $(CFLAGS) usage.C @mv usage.o objdir/ objdir/ccexpr.o: ccexpr.C $(CC) $(CFLAGS) ccexpr.C @mv ccexpr.o objdir/ objdir/preproc.o: preproc.C $(CC) $(CFLAGS) preproc.C @mv preproc.o objdir/ wc: wc *.[Ch] nccnav/*.C | sort -n clean: rm -f objdir/*.o distclean: rm -f objdir/* objdir/ncc @cd nccnav && make clean ncc-2.8/inttree.C0000644000175000001440000000620607661511532014042 0ustar stanusers00000000000000/****************************************************************************** The Integer Tree We want to store things whos comparison key is an integer value. Whether a node is less or more compared to another node will be just a matter of the n-th bit of the integer, where n the current depth. For example the Least Significant Bit will be used to specify less (0) or more (1) for the comparison with the root node. Each integer therefore carries its tree path (0 - go less, 1 - go more) For any possible order of the nodes the tree will NEVER exceed the depth of 33 (for 32-bit integers). Even though it could be unbalanced, its logarithmic unbalance. We will always be able to find something in less than 32 steps. O(32)=O(1) < O(log(n)) < O(n) < O(n^2) < O(\infty) Moverover, if the numbers are in a range 0..2^n the depth will always be <= n+1. For a database with 65536 ID-elements, finds will be a matter of 17 steps. In order to remove an element from the tree any node can replace the node to be removed as long as: - It is below it - It is a terminal node Because it has the same path and no other node's postition is changed in the tree. ******************************************************************************/ /************************************************************************** Copyright (C) 2000, 2001, 2002 Stelios Xanthakis **************************************************************************/ #include #include "inttree.h" unsigned int intTree::Query; intNode **intTree::FoundSlot; intTree::intTree () { cnt = 0; root = NULL; FoundSlot = NULL; } intNode::~intNode () { if (less) delete less; if (more) delete more; } intNode *intTree::intFind (unsigned int q) { Query = q; intNode *n; if (!(n = root)) { FoundSlot = &root; return NULL; } FoundSlot = NULL; for (unsigned int bt = 1; bt; bt *= 2) { if (n->Key == q) return n; if (q & bt) if (n->less) n = n->less; else { FoundSlot = &n->less; return NULL; } else if (n->more) n = n->more; else { FoundSlot = &n->more; return NULL; } } fprintf (stderr, "intTree FUBAR. Segmentation Fault. sorry\n"); return NULL; } intNode::intNode (intTree *i) { if (i->FoundSlot) addself (i); less = more = NULL; } void intNode::addself (intTree *i) { *i->FoundSlot = this; ++i->cnt; i->FoundSlot = NULL; Key = i->Query; } void intNode::intRemove (intTree *i) { unsigned int isroot, bt = 0; intNode *n = i->root; if (!(isroot = n == this)) for (bt = 1; bt; bt *= 2) if (Key & bt) // avoid braces like hell if (n->less != this) n = n->less; else break; else // yes but why? if (n->more != this) n = n->more; else break; if (!less && !more) if (isroot) i->root = NULL; else if (Key & bt) n->less = NULL; else n->more = NULL; else { intNode *r = this, *rp = NULL; while (r->more || r->less) { rp = r; r = (r->more) ? r->more : r->less; } if (isroot) i->root = r; else if (Key & bt) n->less = r; else n->more = r; if (rp->more == r) rp->more = NULL; else rp->less = NULL; r->more = more; r->less = less; } i->cnt--; less = more = NULL; } ncc-2.8/inttree.h0000644000175000001440000000122207661510137014100 0ustar stanusers00000000000000/****************************************************************************** Interger Key Binary Tree. O(1) ******************************************************************************/ class intNode; class intTree { friend class intNode; static unsigned int Query; static intNode **FoundSlot; public: intNode *root; int cnt; intTree (); intNode* intFind (unsigned int); }; class intNode { friend class intTree; friend class foreach_intNode; friend void enter_intNode (intNode*); void addself (intTree*); protected: intNode *less, *more; public: unsigned int Key; intNode (intTree*); void intRemove (intTree*); ~intNode (); }; ncc-2.8/cdb.C0000644000175000001440000010647311074137321013120 0ustar stanusers00000000000000/***************************************************************************** data collected from the parser *****************************************************************************/ #include #include #include #include #include #include "global.h" #include "dbstree.h" #include "inttree.h" #include "mem_pool.h" /***************************************************************************** # # the most complex part in a C compiler is the data structures # storing what is introduced from the declarations. # # the base to understanding how the program works, is understanding # the way data is stored and retrieved from this cdb. # # what this file provides is described in global.h at # `CDB interface' # # this is where all the money went... *****************************************************************************/ /***************************************************************************** # # In many parts of the program, there is multiplexing of # different kind of pointers in integers. # Thats a thing we can do in 32-bit architectures # ==> A register can have values up to 4 billion # and thus, when used as an index of arrays, it # can actually store much more. # # For example. Suppose a program is using foo[]s and # zoo[]s and at some point we have something that may # be either a foo or a zoo. That can be multiplexed # into an integer 'x', where: # if it is a foo, x = index in foo [] # if it is a zoo, x = 10000000 + index in zoo [] # # This situation is common in many programs and 32-bit # architectures are not only about "4Gb of memory" # # Here are basic indexes used: # # typeID: integer describing index in the types [] table of # basetype+pointers/arrays/arguments # ObjPtr: integer which multiplexes possible base-types into one # Bitfield: if < -32 # Builtin: if < _BTLIMIT # Structure: if < TYPEDEF_BASE && positive # Typedef: if > TYPEDEF_BASE # in the latter case typeID = ObjPtr - TYPEDEF_BASE # RegionPtr: integer describing index in regions [] table # NormPtr: integer describing index in the CODE[] normalized C source # ArglPtr: integer describing index in arglists[] table # Symbol: integer describing value of CODE[] # *****************************************************************************/ bool INGLOBAL, INSTRUCT; typeID VoidType, SIntType; ArglPtr NoArgSpec; /***************************************************************************** # type pool (typeID) # # here we allocate types. Types of X are: # int X # int **X # int * X [32][64][] # struct foo *X # int X (int, char*, struct bar**) # struct foo **(*X[3]) (int, char) # # abstract declarations for the above are described with a # 'struct type' object, which includes basetype (ObjPtr) # and specifications (pointer, array-size, function-ArglistPtr). # For example, the latter foo has specifications: # '[', 3, '*', '(', , '*', '*', -1 # which sais: array 3 of pointer to function with arguments # at returning pointer to pointer to basetype # # the types[] table, does not include typedef basetypes. # typedefs are expanded before things are inserted into types[] # with use of the gettype(type&) function. *****************************************************************************/ static earray types; class utype; static dbsTree typetree; struct utype { utype *less, *more; static type Query; utype (); typeID ID; int compare (utype*); int compare (); }; utype::utype () { typetree.addself (this); ID = types.alloc (); types.x [ID].base = Query.base; types.x [ID].spec = intdup (Query.spec); } int base_of (typeID i) { return types.x [i].base; } int *spec_of (typeID i) { return types.x [i].spec; } type utype::Query; int utype::compare (utype *u) { RegionPtr p = base_of (ID); RegionPtr p2 = base_of (u->ID); if (p == p2) return intcmp (spec_of (ID), spec_of (u->ID)); return p < p2 ? -1 : 1; } int utype::compare () { RegionPtr p = base_of (ID); RegionPtr p2 = Query.base; if (p == p2) return intcmp (spec_of (ID), Query.spec); return p < p2 ? -1 : 1; } typeID newtype (type &t) { utype::Query.base = t.base; utype::Query.spec = t.spec; utype *u = (utype*) typetree.dbsFind (); if (!u) u = new utype; return u->ID; } typeID gettype (type &t) { if (t.base < TYPEDEF_BASE) return newtype (t); type nt; typeID td = t.base - TYPEDEF_BASE; int newspec [MSPEC]; // check incomplete typedef intcpycat (newspec, t.spec, types.x [td].spec); nt.base = base_of (td); nt.spec = newspec; return newtype (nt); } typeID gettype (int base, int *spec) { type t; t.base = base; t.spec = spec; return gettype (t); } void opentype (typeID ti, type &t) { t.base = base_of (ti); t.spec = spec_of (ti); } /***************************************************************************** # argument lists. # # Array of typeIDs terminated at -1. # also stored in a dbstree for log(N) insert *****************************************************************************/ static earray arglists; class argNode; static dbsTree argTree; class argNode { public: argNode *less, *more; static Vspec Query; argNode (); int compare (argNode*); int compare (); ArglPtr ID; }; Vspec argNode::Query; int argNode::compare (argNode *d) { return intcmp (arglists.x [ID], arglists.x [d->ID]); } int argNode::compare () { return intcmp (arglists.x [ID], Query); } argNode::argNode () { argTree.addself (this); ID = arglists.alloc (); arglists.x [ID] = intdup (Query); } ArglPtr make_arglist (typeID *t) { argNode::Query = t; argNode *a = argTree.dbsFind (); if (!a) a = new argNode; return a->ID; } typeID *ret_arglist (ArglPtr n) { return arglists.x [n]; } /***************************************************************************** # identifier lookup # # for each identifier, there is a list including: # what this identifier is, and # which region (scope) this applies to # there is also an array of regions, each with the # index of its parent region. # # with these, here we deal with lookups. # Introducing new names in regions is done later. # # RECORD uses info.rp which shows which region it is about # ENUMCONST uses info.eval for the value # ENUMTAG is only useful for its existance *****************************************************************************/ static RegionPtr current_region; enum ITYPE { RECORD, ENUMTAG, TYPEDEF, OBJECT, EOBJECT, ENUMCONST, EFUNCTION, IFUNCTION }; #define ISTAG(x) (x <= ENUMTAG) static struct lookup_t { lookup_t *next; RegionPtr cp; lookup_t (Symbol, ITYPE, RegionPtr); union { RegionPtr rp; typeID tdf; int eval; bool fwd; } info; int placement; char kind, incomplete, defspec; } **lookup_table; lookup_t::lookup_t (Symbol s, ITYPE i, RegionPtr r) { next = lookup_table [s - SYMBASE]; lookup_table [s - SYMBASE] = this; kind = i; cp = r; } struct memb_li { Symbol s; memb_li *next; }; enum REGION { GLOBAL, CCODE, FUNCTIONAL, RECORD_S, RECORD_U }; struct region { RegionPtr parent; REGION kind; int aligned_top, nn; char used, bits, incomplete; NormPtr dcls, dcll; memb_li *first, *last; int add_object (typeID); int add_field (typeID, typeID&); void add_member (Symbol); }; #define ISRECORD(x) (x >= RECORD_S) int region::add_object (typeID t) { if (usage_only) return nn++; if (kind == RECORD_U) { int i = sizeof_typeID (t); if (i > aligned_top) aligned_top = i; return 0; } int r = aligned_top; int i = sizeof_typeID (t); nn++; if (i == 0) ; else if (i == 1) aligned_top++; else if (i == 2) aligned_top += aligned_top % 2 ? 3 : 2; else { int a = aligned_top % 4; if (a == 0) aligned_top += i; else aligned_top += 4 - a + i; } return r; } int region::add_field (typeID ti, typeID &ret) { type t = types.x [ti]; type nt; int spec [3] = { -1, 0, -1 }, r = aligned_top; if (t.spec [1] == 0) { bits = 0; nt.base = S_INT; spec [0] = '['; nt.spec = spec; ret = gettype (nt); if (kind != RECORD_U) aligned_top += 4; return r; } if ((bits += t.spec [1]) > BITFIELD_Q) { bits = 0; if (kind != RECORD_U) aligned_top += 4; } nt.base = -(bits + 32 * t.spec [1]); nt.spec = spec; ret = gettype (nt); return r; } static mem_pool regions; void struct_location (typeID t, NormPtr s, int l) { regions [t].dcls = s; regions [t].dcll = l; } static struct { RegionPtr cc [32]; int cci; } p_region; RegionPtr open_region (RegionPtr r) { p_region.cc [p_region.cci++] = current_region; current_region = r; INGLOBAL = false; INSTRUCT = ISRECORD (regions [r].kind); return r; } RegionPtr new_region (REGION kind, RegionPtr r) { RegionPtr n; regions [n = regions.alloc ()].parent = r; regions [n].kind = kind; regions [n].nn = regions [n].aligned_top = regions [n].bits = 0; regions [n].incomplete = 1; regions [n].used = 0; regions [n].dcll = 0; regions [n].first = regions [n].last = NULL; return n; } RegionPtr open_region (REGION kind, RegionPtr r) { return open_region (new_region (kind, r)); } void close_region () { regions [current_region].incomplete = 0; current_region = p_region.cc [--p_region.cci]; INGLOBAL = current_region == 0; INSTRUCT = !INGLOBAL && ISRECORD (regions [current_region].kind); } lookup_t *Lookup (Symbol s, bool tagged, RegionPtr r) { lookup_t *t = lookup_table [s - SYMBASE]; if (tagged) for (; t; t = t->next) { if (t->cp == r && ISTAG(t->kind)) { regions [r].used = 1; return t; } } else for (; t; t = t->next) if (t->cp == r && !ISTAG(t->kind)) { regions [r].used = 1; return t; } return NULL; } lookup_t *Lookup_foruse (Symbol s, bool tagged, RegionPtr r) { lookup_t *t; for (;;) { if ((t = Lookup (s, tagged, r))) return t; if (r == 0) break; r = regions [r].parent; } return NULL; } /***************************************************************************** # small utility : comparison of types # # Normally, comparison one by one would be plentyly enough, but a # function declaration w/o arguments is a wildcard that should # match with any other argument list # Moreover, incomplete arrays match with complete arrays *****************************************************************************/ static Ok speccmp (Vspec s1, Vspec s2) { while (*s1 != -1) { if (*s1 != *s2) return false; if (*s1 == '(') { ++s1, ++s2; // In the very rare case of: // "int foo (float*);" // "typedef float v3d [3];" // "int foo (v3d x) { ... }" // we will fail. // Instead of fixing, comment out. //if (*s1 != *s2 && *s1 != NoArgSpec && *s2 != NoArgSpec) //return false; } else if (*s1 == '[') { ++s1, ++s2; // why bother? //if (*s1 != *s2 && *s1 != 0 && *s2 != 0) //return false; } ++s1, ++s2; } return *s1 == *s2; } static Ok typecmp (typeID ti1, typeID ti2) { type t1 = types.x [ti1]; type t2 = types.x [ti2]; return t1.base == t2.base && speccmp (t1.spec, t2.spec); } /****************************************************************************** # functions # # functions are compiled after global has been parsed # so that in expressions we know which variables are external. # # At each function definition, we store the location to the # function body and the function arguments in CODE[]. # # after the entire translation unit, function_no() # will be called for each function, which will return the # pointers to argument list, function body and open a # region ready to receive the argument objects. ******************************************************************************/ struct cfunc { Symbol name; NormPtr args, body, ends, txt; }; static earray functions; Ok function_definition (Symbol name, NormPtr args, NormPtr body, NormPtr ends, NormPtr txt) { int i; lookup_t *L = Lookup (name, false, 0); if (L->kind != EFUNCTION) return false; L->kind = IFUNCTION; i = functions.alloc (); functions.x [i].name = name; functions.x [i].args = args; functions.x [i].body = body; functions.x [i].ends = ends; functions.x [i].txt = txt; L->placement = i; return true; } Ok function_no (int i, NormPtr *ra, NormPtr *rb) { if (i >= functions.nr) return false; *ra = functions.x [i].args; *rb = functions.x [i].body; open_region (FUNCTIONAL, 0); ncc->new_function (functions.x [i].name); return true; } void open_compound () { open_region (CCODE, current_region); } void functions_of_file () { int prevfile = -1; PRINTF ("\n\n"); for (int i = 0; i < functions.nr; i++) { if (cfile_of (functions.x [i].body) != prevfile) PRINTF ("\nP: %s\n", C_Files [prevfile = cfile_of (functions.x [i].body)].file); PRINTF ("L: %s() %i %i\n", expand (functions.x [i].name), cline_of (functions.x [i].txt), cline_of (functions.x [i].ends)); } } /***************************************************************************** # # Structure member list. This was not included until 1.1 # It's needed in initializers where we have to assign values # to members in their natural order or by designators # *****************************************************************************/ void region::add_member (Symbol s) { memb_li *m = new memb_li; m->s = s; m->next = NULL; if (!first) { first = last = m; } else { last->next = m; last = m; } } /***************************************************************************** # declaration of new names to the program # # which are: objects (variables and functions), typedefs # enumeration tags, enumeration constants and structure tags. # # structure tag can be declared at declaration or at forward # declaration (use). Check for recursion (incomplete base type), is # done at the sizeof calculation elsewhere. # # the items are semantically correct, and the only error that # can happen here is if something is already declared # in the same region *****************************************************************************/ static Ok function_declaration (Symbol s, typeID t, VARSPC v) { lookup_t *L; if ((L = Lookup (s, false, 0))) { if (L->kind < EFUNCTION) return false; typeID tt = L->info.tdf; if (!typecmp (t, tt)) return false; if (L->kind == EFUNCTION && spec_of (tt)[1] == NoArgSpec) L->info.tdf = t; return true; } L = new lookup_t (s, EFUNCTION, 0); L->info.tdf = t; L->defspec = v; return true; } static Ok field_member (Symbol s, typeID t) { typeID nt; int p; if (s != -1 && Lookup (s, false, current_region)) return false; p = regions [current_region].add_field (t, nt); if (s != -1) { lookup_t *L = new lookup_t (s, OBJECT, current_region); L->info.tdf = nt; L->placement = p; } return true; } Ok introduce_obj (Symbol s, typeID t, VARSPC v = DEFAULT) { if (ISFUNCTION (types.x [t])) return function_declaration (s, t, v); if (INSTRUCT) regions [current_region].add_member (s); if (spec_of (t)[0] == ':') return field_member (s, t); lookup_t *L; if ((L = Lookup (s, false, current_region))) { if (L->kind != EOBJECT && L->kind != OBJECT) return false; typeID et = L->info.tdf; if (!typecmp (t, et)) return false; //if (current_region == 0) return true; if (spec_of (et)[0] == '[' && spec_of (et)[1] == 0) { //if (L->kind == OBJECT) { L->info.tdf = t; return true; //} //L->kind = OBJECT; //L->placement = regions // [current_region].add_object (t); //return true; } //if (L->kind == OBJECT) return v == EXTERN; if (v != EXTERN) { // instantiation L->kind = OBJECT; L->info.tdf = t; L->placement = regions [current_region].add_object (t); } return true; } if (v == EXTERN) { L = new lookup_t (s, EOBJECT, current_region); L->info.tdf = t; } else { L = new lookup_t (s, OBJECT, current_region); L->info.tdf = t; L->placement = regions [current_region].add_object (t); } return true; } Ok introduce_tdef (Symbol s, typeID t) { lookup_t *L = Lookup (s, false, current_region); if (L) return L->kind == TYPEDEF && typecmp (t, L->info.tdf); L = new lookup_t (s, TYPEDEF, current_region); L->info.tdf = t; return true; } ObjPtr lookup_typedef (Symbol s) { lookup_t *t = Lookup_foruse (s, false, current_region); if ((t) && t->kind == TYPEDEF) return TYPEDEF_BASE + t->info.tdf; RegionPtr r = current_region; do { r = regions [r].parent; t = Lookup_foruse (s, false, r); if ((t) && t->kind == TYPEDEF) return TYPEDEF_BASE + t->info.tdf; } while (r); return -1; } Ok is_typedef (Symbol s) { lookup_t *t = Lookup_foruse (s, false, current_region); return (t) && t->kind == TYPEDEF; } Ok introduce_enumconst (Symbol s, int value) { RegionPtr r = current_region; while (ISRECORD (regions [r].kind)) r = regions [r].parent; if (Lookup (s, false, r)) return false; lookup_t *L = new lookup_t (s, ENUMCONST, r); L->info.eval = value; return true; } Ok introduce_enumtag (Symbol s, bool fwd) { lookup_t *L = Lookup (s, true, current_region); if (L) if (!L->info.fwd) return false; else L->info.fwd = fwd; else { L = new lookup_t (s, ENUMTAG, current_region); L->info.fwd = fwd; } return true; } Ok valid_enumtag (Symbol s) { lookup_t *t = Lookup_foruse (s, true, current_region); return (t) && t->kind == ENUMTAG; } /****************************************************************************** # records (struct, union) # # Normally, structure by name is not needed. But in the # case of usage report the user wants to know which # structure is structure #234 # Thus the inttree. ******************************************************************************/ static intTree struct_names; class sname : public intNode { public: int symbol; sname (int s) : intNode (&struct_names) { symbol = s; } }; Symbol struct_by_name (RegionPtr p) { sname *s = (sname*) struct_names.intFind (p); return !s ? -2 : s->symbol; } bool rename_struct (typeID t, Symbol s) { sname *sn = (sname*) struct_names.intFind (base_of (t)); if (sn && sn->symbol == -1) sn->symbol = s; return true; } static inline void name_struct (RegionPtr p, Symbol s) { if (usage_only) if (!struct_names.intFind (p)) new sname (s); } //# //# Show structure declaration locations //# void structs_of_file () { int i, n = regions.nr (); int prevfile = -1; PRINTF ("\n"); for (i = 0; i < n; i++) if (regions [i].used && regions [i].dcll) { if (cfile_of (regions [i].dcls) != prevfile) PRINTF ("\nP: %s\n", C_Files [prevfile = cfile_of (regions [i].dcls)].file); PRINTF ("Y: %s %i %i\n", expand (struct_by_name (i)), cline_of (regions [i].dcls), cline_of (regions [i].dcls + regions [i].dcll)); } } // // // RegionPtr introduce_anon_struct (bool isst) { RegionPtr r; for (r = current_region; ISRECORD (regions [r].kind); r = regions [r].parent); r = open_region (new_region (isst ? RECORD_S : RECORD_U, r)); name_struct (r, -1); return r; } RegionPtr introduce_named_struct (Symbol s, bool isst) { RegionPtr r; lookup_t *L; for (r = current_region; ISRECORD (regions [r].kind); r = regions [r].parent); L = Lookup_foruse (s, true, r); if (L) { if (L->kind == ENUMTAG) return -1; name_struct (L->info.rp, s); return open_region (L->info.rp); } L = new lookup_t (s, RECORD, r); L->incomplete = 0; L->info.rp = open_region (isst ? RECORD_S:RECORD_U, r); name_struct (L->info.rp, s); return L->info.rp; } RegionPtr use_struct_tag (Symbol s, bool isst) { RegionPtr r; lookup_t *L; for (r = current_region; ISRECORD (regions [r].kind); r = regions [r].parent); L = Lookup_foruse (s, true, r); if (L) return (L->kind == ENUMTAG) ? -1 : L->info.rp; L = new lookup_t (s, RECORD, r); L->incomplete = 1; return L->info.rp = new_region (isst ? RECORD_S : RECORD_U, r); } RegionPtr fwd_struct_tag (Symbol s, bool isst) { RegionPtr r; lookup_t *L; for (r = current_region; ISRECORD (regions [r].kind); r = regions [r].parent); L = Lookup (s, true, r); if (L) return (L->kind == ENUMTAG) ? -1 : L->info.rp; L = new lookup_t (s, RECORD, r); L->incomplete = 1; return L->info.rp = new_region (isst ? RECORD_S : RECORD_U, r); } /***************************************************************************** # lookup_object # # this is the kind of lookup needed in expressions. # expressions do not need: structure tags, typedefs, etc # but only objects or enumeration constants # # for functions there is the additional rule that if a # declaration does not exist, it is implictly declared as # extern int f (); # # for structure members it is the same but the lookup is # restricted only inside the scope and it does not do it's best # to find a matching declaration # # have_function is like lookup_function except the function # is not declared if it doesn't exist *****************************************************************************/ lookup_object::lookup_object (Symbol s, bool f2ptr) { lookup_t *L; if (!(L = Lookup_foruse (s, false, current_region))) half_error ("Undefined object", expand (s)); if ((enumconst = L->kind == ENUMCONST)) { ec = L->info.eval; return; } typeID t = L->info.tdf; base = types.x [t].base; if (L->kind == OBJECT) { FRAME = L->cp; displacement = L->placement; intcpy (spec, spec_of (t)); } else if (L->kind == EOBJECT) { FRAME = -1; displacement = 0; intcpy (spec, spec_of (t)); } else if (L->kind == IFUNCTION || L->kind == EFUNCTION) { FRAME = -1; displacement = L->placement; if (f2ptr) { spec [0] = '*'; intcpy (spec + 1, spec_of (t)); } else intcpy (spec, spec_of (t)); } else half_error ("Undefined object", expand (s)); } lookup_function::lookup_function (Symbol s, bool fatalerror) { lookup_t *L; found = true; ARGFUNC = fptr = false; if (!(L = Lookup_foruse (s, false, current_region))) { type t; spec [0] = '(', spec [1] = NoArgSpec, spec [2] = -1; base = t.base = S_INT; t.spec = spec; introduce_obj (s, gettype (t), EXTERN); FRAME = -1; displacement = 0; } else if (L->kind == EFUNCTION) { FRAME = -1; displacement = 0; typeID t = L->info.tdf; base = base_of (t); intcpy (spec, spec_of (t)); } else if (L->kind == IFUNCTION) { FRAME = -1; displacement = L->placement; typeID t = L->info.tdf; base = base_of (t); intcpy (spec, spec_of (t)); } else if (L->kind == EOBJECT || L->kind == OBJECT) { typeID t = L->info.tdf; if (!(types.x [t].spec [0] == '*' && types.x [t].spec [1] == '(')) if (fatalerror) half_error ("Not a pointer to function", expand (s)); else intcpy (spec, spec_of (t)); else intcpy (spec, spec_of (t) + 1); base = base_of (t); FRAME = L->cp; ARGFUNC = FRAME && !regions [FRAME].parent; displacement = L->placement; fptr = true; } else if (fatalerror) half_error ("Not a function", expand (s)); else found = false; } lookup_member::lookup_member (Symbol s, RegionPtr r) { lookup_t *L = Lookup (s, false, r); if (!L) half_error ("Undefined member", expand (s)); displacement = L->placement; typeID t = L->info.tdf; base = base_of (t); intcpy (spec, spec_of (t)); } void spill_anonymous (RegionPtr r) { memb_li *m; for (m = regions [r].first; m; m=m->next) { lookup_t *L = Lookup (m->s, false, r); if (L->kind == OBJECT) introduce_obj (m->s, L->info.tdf); } } bool have_function (Symbol s) { lookup_t *L = Lookup_foruse (s, false, current_region); return L && (L->kind == EFUNCTION || L->kind == IFUNCTION); } /***************************************************************************** # sizeof # # - esizeof_objptr (), is used for the case the sizeof the # base declarator is needed to compute the size of an incomplete # type from initializer. For example: # # typedef struct { int x, y } foo; # foo bar [] = { 1, 2, 3, 4, 5, 6 }; # were bar is array [3] of struct foo. # # - sizeof_typeID (), is used by sizeof (typename) # # - the function ptr_incremenet () take as an argument a type # which MUST be a pointer to something, and return how # much will the ++ operator on the pointer increase it. ******************************************************************************/ static bool just_count; static const int sizez [] = { sizeof (char), sizeof (char), sizeof (short int), sizeof (short int), sizeof (int), sizeof (int), sizeof (long int), sizeof (long int), sizeof (long long), sizeof (long long), sizeof (float), sizeof (double), 0 }; static inline int sizeof_btype (BASETYPE b) { return (just_count) ? 1 : sizez [b - S_CHAR]; } static inline int sizeof_ptr () { return (just_count) ? 1 : sizeof (void*); } static int sizeof_struct (RegionPtr p) { if (regions [p].incomplete) syntax_error (ExpressionPtr, "incomplete structure"); return (just_count) ? regions [p].nn : regions [p].aligned_top; } static int sizeof_type (type &t) { int i, na = 1, st, *spec = t.spec; if (spec [0] == '(') return 0; for (i = 0; spec [i] == '['; i += 2) na *= spec [i + 1]; //if (!na) half_error ("sizeof Incomplete type attempted"); st = (spec [i] == '*') ? sizeof_ptr () : (T_BASETYPE (t)) ? sizeof_btype ((BASETYPE) t.base) : sizeof_struct (t.base); return na * st; } int sizeof_typeID (typeID ti) { type t = types.x [ti]; return sizeof_type (t); } // ************************************************* int esizeof_objptr (ObjPtr o) { if (o < _BTLIMIT) return 1; just_count = true; int r = (o < TYPEDEF_BASE) ? sizeof_struct (o) : sizeof_typeID (o - TYPEDEF_BASE); just_count = false; return r; } int sizeof_type (int base, Vspec spec) { type t; t.base = base; t.spec = spec; return sizeof_type (t); } int ptr_increment (int b, Vspec spec) { type t; int tspec [50]; if (spec [0] == -1) half_error ("invalid pointer arithmetic"); t.base = b; intcpy (tspec, spec [0] == '*' ? spec + 1: spec + 2); t.spec = tspec; return sizeof_type (t); } /***************************************************************************** # # initialization of various things # *****************************************************************************/ void init_cdb () { lookup_table = new lookup_t* [C_Nsyms]; for (int i = 0; i < C_Nsyms; i++) lookup_table [i] = NULL; // global, region 0, parent of self, etc open_region (GLOBAL, 0); INGLOBAL = true; // Void type int x [1]; x [0] = -1; type t = { VOID, &x[0] }; VoidType = newtype (t); t.base = S_INT; SIntType = newtype (t); // argument list with no arguments --old style typeID ta [2]; ta [0] = ARGLIST_OPEN; ta [1] = -1; NoArgSpec = make_arglist (ta); ta [0] = SIntType; ArglPtr sizet_arg = make_arglist (ta); #define DEFBUILTIN(B, S1, S2, S3, S4, T) \ if (ccbuiltins.bt ## B!= -1) {\ int s [] = { S1, S2, S3, S4 }; type t;\ t.base = T; t.spec = s;\ introduce_obj (ccbuiltins.bt ## B, gettype (t), STATIC);\ } DEFBUILTIN(__FUNCTION__, '*', -1, -1, -1, S_CHAR) DEFBUILTIN(__func__, '*', -1, -1, -1, S_CHAR) DEFBUILTIN(__PRETTY_FUNCTION__, '*', -1, -1, -1, S_CHAR) DEFBUILTIN(__builtin_return_address, '(', NoArgSpec, '*', -1, VOID) DEFBUILTIN(__builtin_alloca, '(', sizet_arg, '*', -1, VOID) } // // this routine here, shows what is included in the lookup table --- // useful for debugging cdb // void show_lookups () { lookup_t *L; int i; for (i = 0; i < C_Nsyms; i++) { PRINTF ("* * * * * Symbol [%s]\n", C_Syms [i]); for (L = lookup_table [i]; L; L = L->next) { PRINTF ("- Inside %i: ", L->cp); switch (L->kind) { case RECORD: PRINTF ("Record no %i\n", L->info.rp); break; case ENUMTAG: PRINTF ("Enumeration tag\n"); break; case TYPEDEF: PRINTF ("typedef\n"); printtype (L->info.tdf); break; case OBJECT: case EOBJECT: PRINTF ("variable %i\n", L->placement); printtype (L->info.tdf); break; case ENUMCONST: PRINTF ("enumeration constant %i\n", L->info.eval); break; case EFUNCTION: case IFUNCTION: PRINTF ("function %i\n", L->placement); printtype (L->info.tdf); break; default:; } } } } /************************************************************************** # expand the fields of an aggregate type for initializer # # The reason for the complexity of the code below, is the Standard # P = { .x.y { 2, [1].e.[1] { 32 }, 27 }, }; # is a valid initializer. But where shall '27' be assigned? # # ISOC99 sais: # ``30. Note that the fully bracketed and minimally bracketed # form of initalization are, in general, less likely to cause # confusion'' # which is translated to : # ``Don't do initializations like P because we are not sure # if your compiler can implement this correctly'' # # Hopefully this works ***************************************************************************/ class aeqn { Symbol txt [1024]; int txti; void prstruct (RegionPtr); void prarr (int,int*); public: aeqn (Symbol); Symbol *dclstr; }; #define UNI '/' void aeqn::prstruct (RegionPtr p) { int base, *spec; lookup_t *L; memb_li *m; if (regions [p].kind == RECORD_U) txt [txti++] = UNI; for (m = regions [p].first; m; m=m->next) { L = Lookup (m->s, false, p); txt [txti++] = m->s; base = base_of (L->info.tdf); spec = spec_of (L->info.tdf); txt [txti++] = '{'; if (spec [0] == '[') prarr (base, spec); else if (spec [0] == -1 && base > _BTLIMIT) prstruct (base); else txt [txti++] = '!'; txt [txti++] = '}'; } } void aeqn::prarr (int base, int *spec) { txt [txti++] = INUMBER + spec[1]; txt [txti++] = '['; if (spec [2] == '[') prarr (base, spec+2); else if (spec [2] == -1 && base > _BTLIMIT) prstruct (base); else txt [txti++] = '!'; txt [txti++] = ']'; } aeqn::aeqn (Symbol s) { lookup_t *L = Lookup (s, false, current_region); int base, *spec; base = base_of (L->info.tdf); spec = spec_of (L->info.tdf); txt [0] = s; txt [1] = '{'; txti = 2; if (spec [0] == '[') prarr (base, spec); else if (spec [0] == -1 && base > _BTLIMIT) prstruct (base); txt [txti++] = '}'; txt [txti++] = -1; memcpy (dclstr = new int [txti], txt, txti * sizeof (txt [0])); } Symbol *mk_dclstr (Symbol s) { aeqn A (s); if (0) { int i; PRINTF ("[XYZZY]: "); for (i = 0; A.dclstr [i] != -1; i++) PRINTF ("%s", expand (A.dclstr[i])); PRINTF ("\t\t(txtlen=%i,i=%s)\n", i,expand(s)); } return A.dclstr; } dcle::dcle (Symbol s) { dclstr = mk_dclstr (s); p = 0; ni = -1; } dcle::~dcle () { delete [] dclstr; } #define PRFUN(x) \ //printf("Entering "#x"\t at %i [%s]",p,expand(dclstr[p]));printf(" [%s]",expand(dclstr[p+1]));printf(" [%s]\n",expand(dclstr[p+2])); NormPtr dcle::skipbracket (NormPtr p) { PRFUN(skipbracket) // ts is '[' or '{' Symbol ts = dclstr [p++]; Symbol tc = ts == '[' ? ']' : '}'; int c; for (c = 1; c; p++) if (dclstr [p] == ts) c++; else if (dclstr [p] == tc) c--; return p; } void dcle::openarray () { PRFUN(openarray) nests [++ni].max = dclstr [p++] - INUMBER; nests [ni].c = 0; nests [ni].p = p++; nests [ni].marked = false; } void dcle::openstruct () { PRFUN(openstruct) nests [++ni].s = dclstr [p++]; nests [ni].p = p++; nests [ni].max = -1; nests [ni].marked = false; } bool dcle::opennest () { PRFUN(opennest) // nested types open with '2[' or 'x{' // for arrays and structures if (dclstr [p] >= INUMBER) openarray (); else if (ISSYMBOL (dclstr [p])) openstruct (); else if (dclstr [p] == '[') p++; else return false; if (dclstr [p] == UNI) p++; return true; } bool dcle::open_bracket () { PRFUN(open_bracket) // marks the nested type if (!opennest ()) return false; nests [ni].marked = true; return true; } bool dcle::close_bracket () { PRFUN(close_bracket) // takes you at the closing bracket of the // topmost marked nested type while (ni >= 0 && !nests [ni].marked) ni--; if (ni <= 0) return false; nests [ni].marked = false; p = skipbracket (nests [ni].p) - 1; return true; } bool dcle::closenest () { PRFUN(closenest) // closes a nested type and takes you after it // or recycle in the case of array if (dclstr [p] == '}') { if (nests [ni].marked) return false; ni--; p++; return true; } if (nests [ni].marked) return false; if (++nests [ni].c < nests [ni].max || nests [ni].max == 0) { /* direct-declarator [] without constant-expression is assumed to accept any number of initializers in initializer-list. Ben Lau */ p = nests [ni].p; return true; } ni--; p++; return true; } bool dcle::comma () { PRFUN(comma) // takes you to the next point of interest if (dclstr [p] == '!') p++; again: while (dclstr [p] == '}' || dclstr [p] == ']') if (!closenest ()) return true; if (dclstr [nests [ni].p + 1] == UNI && dclstr [p] != '[') { int po = p = skipbracket (nests [ni].p) - 1; if (closenest () && po < p) goto again; } return true; } bool dcle::tofield () { PRFUN(tofield) // takes you to the next '!' int dp; while ((dp = dclstr [p]) != '!') if (opennest ()) continue; else if (dp == ']' || dp == '}') { if (!closenest ()) return false; } else return false; return true; } bool dcle::designator (Symbol D[]) { PRFUN(designator) // takes you where the designator points int i; while (ni >= 0 && !nests [ni].marked) ni--; if (dclstr [p = nests [ni].p + 1] == UNI) p++; for (i = 0; D [i] != -1; i++) if (ISSYMBOL (D [i])) { while (ISSYMBOL (dclstr [p]) && dclstr [p] != D [i]) p = skipbracket (p + 1); if (dclstr [p] != D [i]) return false; if (D [i+1] != -1) opennest (); } else { if (nests [ni].max != -1) { if (D [i] < INUMBER) D [i] = nests [ni].max;//hack nests [ni].c = D [i] - INUMBER; if (dclstr [p = nests [ni].p + 1] == UNI) p++; } else { if (!opennest ()) return false; if (D [i] < INUMBER) D [i] = nests [ni].max;//hack nests [ni].c = D [i] - INUMBER; if (D [i+1] == -1) if (dclstr [--p] == UNI) p--; // br0ken standard *&#@*#@&^%& } } return true; } bool dcle::tostruct (RegionPtr) { PRFUN(tostruct) // this is done the lazy way // struct { struct { struct { int a, b; } S1; } S2; } // S3 = { ((struct S1) {1,2}) }; // is not supported and ?maybe? rightly so if (!ISSYMBOL (dclstr [p])) return false; opennest (); p = skipbracket (nests [ni].p) - 1; return true; } Symbol *dcle::mk_current () { PRFUN(mk_current) // create the current field as a postfix expression int i, p; for (i = 0, p = 0; i <= ni; i++) if (nests [i].max != -1) { pexpr [p++] = '['; pexpr [p++] = INUMBER + nests [i].c; pexpr [p++] = ']'; } else { pexpr [p++] = '.'; pexpr [p++] = nests [i].s; } pexpr [p++] = ';'; pexpr [p] = -1; return &pexpr [1]; } void dcle::printexpr () { // debug routine int i; PRINTF ("XYZZY: "); for (i=0;pexpr[i]!=-1;i++) PRINTF ("%s", expand (pexpr [i])); PRINTF ("\t\t%i\n", i); } /***************************************************************************** *****************************************************************************/ void showdb () { //show_lookups (); PRINTF ("#%i types\n", types.nr); PRINTF ("#%i arglists\n", arglists.nr); PRINTF ("#%i regions\n", regions.nr ()); PRINTF ("#%i function definitions\n", functions.nr); } //**************************************************************************** // debugging routine, print a type //**************************************************************************** #define U "unsigned " #define S "short " #define L "long " #define I "int" #define C "char" static const char *btn [] = { // some ppl are lazy C, U C, S I, U S I, I, U I, L I, U L I, L L, U L L, "float", "double", "void" }; void printtype (int base, int *spec) { #define STDE stderr int i; if (base < -32) fprintf (STDE, "blitfield\n"); else if (base < _BTLIMIT) fprintf (STDE, "%s ", btn [base - S_CHAR]); else fprintf (STDE, "record #%i ", base); for (i = 0; spec [i] != -1; i++) if (spec [i] == '*') fprintf (STDE, "*"); else if (spec [i] == '[') fprintf (STDE, "[%i]", spec [++i]); else if (spec [i] == '(') fprintf (STDE, "(%i)", spec [++i]); else if (spec [i] == ':') fprintf (STDE, ":%i", spec [++i]); else { printf ("fuck %i", spec [i]); break; } fprintf (STDE, "\n"); } void printtype (typeID ti) { type t = types.x [ti]; printtype (t.base, t.spec); } ncc-2.8/lex.C0000644000175000001440000002247210502327674013163 0ustar stanusers00000000000000/****************************************************************************** C/C++ lexcial analyser on preprocessed source ******************************************************************************/ #include #include #include #include #include "global.h" token CTok; int &line = CTok.at_line; static char *Cpp; static int Ci, Clen; /****************************************************************************** Maybe this is faster than ctype.h macros The first 127 ASCII characters is a universal constant. ******************************************************************************/ static char ll_ctypes [256]; static void initctypes () { #define SET(x,y) ll_ctypes [(int)x] = y; SET('A',2) SET('B',2) SET('C',2) SET('D',2) SET('E',2) SET('F',3) SET('G',2) SET('H',2) SET('I',2) SET('J',2) SET('K',2) SET('L',3) SET('M',2) SET('N',2) SET('O',2) SET('P',2) SET('Q',2) SET('R',2) SET('S',2) SET('T',2) SET('U',3) SET('V',2) SET('W',2) SET('X',2) SET('Y',2) SET('Z',2) SET('a',2) SET('b',2) SET('c',2) SET('d',2) SET('e',2) SET('f',3) SET('g',2) SET('h',2) SET('i',2) SET('j',2) SET('k',2) SET('l',3) SET('m',2) SET('n',2) SET('o',2) SET('p',2) SET('q',2) SET('r',2) SET('s',2) SET('t',2) SET('u',3) SET('v',2) SET('w',2) SET('x',2) SET('y',2) SET('z',2) SET('_',2) SET('0',1) SET('1',1) SET('2',1) SET('3',1) SET('4',1) SET('5',1) SET('6',1) SET('7',1) SET('8',1) SET('9',1) } #define ISNIEND(x) (ll_ctypes [(int)x] == 3) #define ISALPHA(x) (ll_ctypes [(int)x] >= 2) #define ISDIGIT(x) (ll_ctypes [(int)x] == 1) #define ISALNUM(x) (ll_ctypes [(int)x] != 0) /****************************************************************************** Unwindable lex exceptional error conditions ******************************************************************************/ class EOFC { public: EOFC(const char*); }; EOFC::EOFC (const char *c) { fprintf (stderr, "Unterminated %s near token %i\n", c, Ci); } /*************************************************************************** Start of Token Parser Routines ***************************************************************************/ static inline void skip_ws () { for (;;) { for (;;) { if (Cpp [Ci] == ' ' || Cpp [Ci] == '\t') { if (++Ci >= Clen) return; continue; } if (Cpp [Ci] == '\n') { ++line; if (++Ci >= Clen) return; continue; } break; } #if 0 if (Cpp [Ci] == '\\' && Cpp [Ci + 1] == '\n') { Ci += 2; ++line; continue; } #endif break; } } static inline void get_ident () { CTok.type = IDENT_DUMMY; CTok.p = &Cpp [Ci]; while (ISALNUM (Cpp [Ci])) if (++Ci >= Clen) break; CTok.len = &Cpp [Ci] - CTok.p; } static char EOFstring [] = " string literal"; static void get_string () { CTok.type = STRING; CTok.p = &Cpp [++Ci]; for (;;) { while (Cpp [Ci] != '\\' && Cpp [Ci] != '"') if (++Ci >= Clen) throw EOFC (EOFstring); if (Cpp [Ci] == '\\') { Ci += 2; if (Ci >= Clen) throw EOFC (EOFstring); continue; } break; } CTok.len = &Cpp [Ci] - CTok.p; ++Ci; } static inline void get_exponent () { ++Ci; if (Cpp [Ci] == '-' || Cpp [Ci] == '+') Ci++; while (ISDIGIT (Cpp [Ci])) if (++Ci >= Clen) break; } static inline void get_float_frac () { // The token pointer and length are already set to // the decimal part, or this[char] && 0 if no decimal part ++Ci; while (ISDIGIT (Cpp [Ci])) if (++Ci >= Clen) break; } static char EOFchar [] = "character constant"; static void get_char_const () { ++Ci; CTok.type = CCONSTANT; CTok.p = &Cpp [Ci]; for (;;) { while (Cpp [Ci] != '\\' && Cpp [Ci] != '\'') if (++Ci >= Clen) throw EOFC (EOFchar); if (Cpp [Ci] == '\\') { Ci += 2; if (Ci >= Clen) throw EOFC (EOFchar); continue; } break; } CTok.len = &Cpp [Ci] - CTok.p; if (CTok.len > 10) throw (EOFchar); ++Ci; } static inline void get_nconst () { CTok.type = CONSTANT; CTok.p = &Cpp [Ci]; while (isalnum (Cpp [Ci])) if (++Ci >= Clen) break; if (Cpp [Ci] == '.') { get_float_frac (); CTok.type = FCONSTANT; } if (Cpp [Ci] == 'e' || Cpp [Ci] == 'E' || Cpp [Ci] == 'p') { get_exponent (); CTok.type = FCONSTANT; } while (ISNIEND (Cpp [Ci])) if (++Ci >= Clen) break; CTok.len = &Cpp [Ci] - CTok.p; } /*************************************************************************** Little utils ***************************************************************************/ static void grle_morph () { char gl = Cpp [Ci]; CTok.p = &Cpp [Ci]; ++Ci; if (Cpp [Ci] == gl) { ++Ci; if (Cpp [Ci] == '=') { ++Ci; CTok.type = (gl == '>') ? ASSIGNRS : ASSIGNLS; } else CTok.type = (gl == '>') ? RSH : LSH; } else if (Cpp [Ci] == '=' || Cpp [Ci] == '?') { ++Ci; CTok.type = (gl == '>') ? GEQCMP : LEQCMP; } else CTok.type = gl; } static void anor_morph () { char ao = Cpp [Ci]; ++Ci; if (Cpp [Ci] == ao) { ++Ci; CTok.type = (ao == '&') ? ANDAND : OROR; } else if (Cpp [Ci] == '=') { ++Ci; CTok.type = (ao == '&') ? ASSIGNBA : ASSIGNBO; } else CTok.type = ao; } /*************************************************************************** ***************************************************************************/ /****************************************************************************** Interface entry functions ******************************************************************************/ static void do_yylex () { Again: if (Ci >= Clen) { CTok.type = THE_END; return; } skip_ws (); if (Ci >= Clen) { CTok.type = THE_END; return; } CTok.p = &Cpp [Ci]; CTok.len = 0; if (ISDIGIT (Cpp [Ci])) get_nconst (); else if (ISALPHA (Cpp [Ci]) /*|| Cpp [Ci] == '_'*/) if (Cpp [Ci] == 'L' && (Cpp [Ci + 1] == '\'' || Cpp [Ci + 1] == '"')) { Ci++; goto Switch; } else get_ident (); else Switch: switch (Cpp [Ci]) { case '(': case ')': case ';': case ',': CTok.type = Cpp [Ci]; CTok.p = &Cpp [Ci]; ++Ci; break; case '*': CTok.type = Cpp [Ci]; ++Ci; if (Cpp [Ci] == '=') { CTok.type = ASSIGNM; ++Ci; break; } break; case '"': get_string (); return; case '\'': get_char_const (); return; case '/': ++Ci; if (Cpp [Ci] == '=') { CTok.type = ASSIGND; ++Ci; break; } CTok.type = '/'; break; case '.': if (ISDIGIT (Cpp [Ci + 1])) { get_nconst (); break; } ++Ci; if (Cpp [Ci] == '.' && Cpp [Ci + 1] == '.') { CTok.type = ELLIPSIS; Ci += 2; } else CTok.type = '.'; break; case '-': ++Ci; if (Cpp [Ci] == '>') { ++Ci; CTok.type = POINTSAT; break; } if (Cpp [Ci] == '-') { CTok.type = MINUSMINUS; ++Ci; break; } if (Cpp [Ci] == '=') { CTok.type = ASSIGNS; ++Ci; break; } CTok.type = '-'; break; case '+': ++Ci; if (Cpp [Ci] == '+') { CTok.type = PLUSPLUS; ++Ci; break; } if (Cpp [Ci] == '=') { CTok.type = ASSIGNA; ++Ci; break; } CTok.type = '+'; break; case '!': case '%': case '^': CTok.type = Cpp [Ci]; ++Ci; if (Cpp [Ci] == '=') { CTok.type = (CTok.type == '!') ? NEQCMP : (CTok.type == '%') ? ASSIGNR : ASSIGNBX; ++Ci; break; } break; case '&': case '|': anor_morph (); break; case ':': ++Ci; CTok.type = ':'; break; case '=': ++Ci; if (Cpp [Ci] == '=') { CTok.type = EQCMP; ++Ci; break; } CTok.type = '='; break; case '>': case '<': grle_morph (); break; case '#': CTok.type = '#'; if (Ci == 0 || Cpp [Ci - 1] == '\n' || Cpp [Ci - 1] == '\r') CTok.type = CPP_DIRECTIVE; ++Ci; if (Ci < Clen && Cpp [Ci] == '#') { CTok.type = CPP_CONCAT; ++Ci; } break; case '[': case ']': case '~': CTok.type = Cpp [Ci]; CTok.p = &Cpp [Ci]; ++Ci; break; case '\r': case '\f': ++Ci; goto Again; default: // $ CTok.type = Cpp [Ci]; CTok.p = &Cpp [Ci]; ++Ci; } CTok.len = &Cpp [Ci] - CTok.p; } static void enter_abspath_file (char *file) { char tmp [1024]; if (!abs_paths || file [0] == '/') enter_file_indicator (file); else enter_file_indicator (strcat (strcpy (tmp, cwd), file)); } static void skip_pp_line () { // For preprocessed source, the only directive is: // # "file" // send the file to enter_file_indicator () // ... but it can also be pragma (thing) char tmp [512]; tmp [0] = 0; if (Cpp [++Ci] == 'p') { // #pragma while (Ci < Clen && Cpp [Ci] != '\n') ++Ci; ++Ci; return; } // Assume, without verification, that the next token is // a line number. line = strtol (&Cpp [Ci], NULL, 10 ); for(;;) { if (Ci >= Clen) { CTok.type = THE_END; return; } switch (Cpp [Ci]) { case '\n': if (tmp [0]) enter_abspath_file (tmp); ++Ci; /* Scott */ return; case '"': get_string (); strncpy (tmp, CTok.p, CTok.len); tmp [CTok.len] = 0; break; default: ++Ci; } } } /****************************************************************************** Main ******************************************************************************/ extern bool quiet; void yynorm (char *c, int l) { initctypes (); Cpp = c; Clen = l; line = 1; Ci = 0; try { for (;;) { do_yylex (); if (CTok.type == THE_END) break; if (CTok.type == CPP_DIRECTIVE) skip_pp_line (); else enter_token (); } } catch (EOFC) { } if (!quiet) fprintf (stderr, "%i lines\n", line); } ncc-2.8/ncc.10000644000175000001440000000244310502332244013075 0ustar stanusers00000000000000.TH ncc 1 "8 Mar 2003" "Linux" "ncc" .SH NAME ncc \- source code analysis .SH SYNOPSIS ncc file.c .SH DESCRIPTION .B ncc is a program that can help you hack/study the source code of C programs. It will report which functions call which other functions, which functions are called by other functions and what global variables and members of structures are used by functions. This is useful if you want to analyse a program and eventually hack it. .SH USAGE To use .B ncc find the .B Makefile of the program you want to analyse. In the .B Makefile locate the line which sets the C compiler. That is usually something like .B CC = gcc and you must change it to .B CC = ncc -ncgcc -ncld -ncfabs. Then compile the application. It may be useful to also replace .B AR = ar with .B AR = nccar and .B LD = ld with .B LD = nccld to link nccout object files. .SH OUTPUT .B ncc will produce a file with .B nccout extension for every C file analysed. Then you can use the viewer .B nccnav (with .B nccnav program.nccout ) to view this data and do your work. If the procedure of make links object files with "ar" or "ld" you should manually link the produced .B .nccout files. .SH AUTHORS The homepage of the ncc project is: http://students.ceid.upatras.gr/~sxanth/ncc/ .SH SEE ALSO The documentation included in the package. ncc-2.8/README0000777000175000001440000000000010252401132014210 2doc/NCCustar stanusers00000000000000ncc-2.8/scripts/0000755000175000001440000000000011074666571013756 5ustar stanusers00000000000000ncc-2.8/scripts/nccstrip.py0000755000175000001440000000161710157707046016157 0ustar stanusers00000000000000#!/usr/bin/env python """I'd rather we did this in perl because it is strictly a practical extraction and report application. Anyway, this compresses nccout files by removing duplicate data as structure declarations from header files which are reported in every source file.""" import sys have = {} def getsub (line, set): TS = '' TL = [ line ] for j in sys.stdin: if j[0] not in set: break j = j[:-1] TS += j TL.append (j) if line in have: X = have [line] if type(X) == type (''): if X == TS: return have [line] = [X, TS] else: for k in have [line]: if k == TS: return have [line].append (TS) else: have [line] = TS for j in TL: print j; print for i in sys.stdin: if i [0] == '#': continue if i [0] == 'D': getsub (i[:-1], 'FgGsS') elif i [0] == 'P': getsub (i[:-1], 'YL') else: print i[:-1] ncc-2.8/scripts/gengraph.py.10000644000175000001440000000261010503031155016235 0ustar stanusers00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH GENGRAPH.PY 1 "September 10, 2006" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME gengraph.py \- Produces a call graph in dot format from an ncc-generated file. .SH SYNOPSIS .B gengraph.py .RI [options] .RI .RI .RI ... .SH OPTIONS .TP .BI -h | --help: usage statement .TP .BI -V | --version: print version and exit .TP .BI -D | --dot: additional options to dot .TP .BI -d | --depth: max depth of graph .TP .BI -i | --ignore: functions to ignore .TP .BI -l | --location: show file names of function declarations .TP .BI -s | --show: show function but ignore sub-functions .SH SAMPLE USAGE .B gengraph.py -i "exit strlen" nccout main | dot -Tsvg -o func.svg .B gengraph.py -i "exit strlen" nccout main | springgraph > output.png .SH SEE ALSO .BR ncc (1), .BR nccnav (1) ncc-2.8/scripts/nccstrip2.py0000755000175000001440000000243711074661646016246 0ustar stanusers00000000000000#!/usr/bin/env python # This file is installed and used automatically by ncc each time it # "links" nccout object files. import sys have = {} def getsub (line, set): global outlines TS = '' TL = [ line ] for j in instream: if j[0] not in set: break j = j[:-1] TS += j TL.append (j) if line in have: X = have [line] if type(X) == type (''): if X == TS: return have [line] = [X, TS] else: for k in have [line]: if k == TS: return have [line].append (TS) else: have [line] = TS outlines += len (TL) output ("\n".join (TL) + "\n") infile, outfile = sys.argv [1:] outlines = inlines = 0 def readinput (infile): global inlines for l in infile: yield l inlines += 1 instream = readinput (open (infile)) if infile == outfile: replace = True outfile = "nccstriptmp" else: replace = False write = open (outfile, "w").write def output (x): global outlines outlines += 1 write (x + "\n") for i in instream: if i [0] == '#': continue if i [0] == 'D': getsub (i[:-1], 'FgGsS') elif i [0] == 'P': getsub (i[:-1], 'YL') else: output (i[:-1]) if inlines: print "nccstrip: -%.2f%%"% ((inlines - outlines) * 100.0 / inlines) if replace: import os os.rename ("nccstriptmp", infile) ncc-2.8/scripts/gengraph.py0000755000175000001440000001111710147302275016113 0ustar stanusers00000000000000#!/usr/bin/env python # """Usage: %s [options] ... valid options: \t -h | --help: usage statement \t -V | --version: print version and exit \t -D | --dot: additional options to dot \t -d | --depth: max depth of graph \t -i | --ignore: functions to ignore \t -l | --location: show file names of function declarations \t -s | --show: show function but ignore sub-functions Reads a ncc-generated file and produces a call graph in dot format. Sample usage: gengraph.py -i "exit strlen" nccout main |dot -Tsvg -o func.svg """ # # Author: Jose Vasconcellos # Copyright (C) 2004 Jose Vasconcellos # # gengraph.py is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # gengraph.py is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Tested with Python version 2.3 and ncc-1.9 import sys import os import string import getopt # globals location = 0 maxdepth = 1000 # large number dot = None ignore = [] show = [] shown = [] cfile = None #current file cfunc = None #current func rfunc = {} # funcs: function dictionary # this dictionary maps a function to a list containing dependencies funcs = {} #################### def usage(): print __doc__ % os.path.basename(sys.argv[0]) sys.exit(1) #################### def addfunc(f, d): if f in funcs: # already exists, just append funcs[f].append(d) else: # new function entry; 1st item holds file name funcs[f] = [None, d] def addfile(f, p): if f in funcs: # existing entry, put file name in 1st entry in list funcs[f][0] = p else: # new function entry; create list with file name funcs[f] = [p] #################### def word(w): return w.rstrip(" ()\t\n") #################### def gengraph(s, d=0): if s: shown.append(s) if s in funcs: l = funcs[s] for f in l[1:]: f1 = f edge = "" if f1[0] == '*': edge = "[color=blue]" while rfunc.has_key(f1): f1 = rfunc[f1] print '"%s" -> "%s" %s;' % (s, f1, edge) if d < maxdepth and f1 == f and f not in shown: gengraph(f,d+1) #################### # start of main program # process command line options try: opts, args = getopt.getopt(sys.argv[1:], "hvVD:d:i:l:s:", \ ["help","version","depth","dot","ignore","location","show"]) except getopt.GetoptError: # print help information and exit: usage() for o, a in opts: if o in ("-h", "--help"): usage() elif o in ("-V", "--version"): print "$Id: gengraph.py 5 2004-11-18 03:21:58Z Jose $" sys.exit(1) elif o in ("-i", "--ignore"): ignore = string.split(a) elif o in ("-D", "--dot"): dot = a elif o in ("-d", "--depth"): maxdepth = int(a) elif o in ("-l", "--location"): location = 1 elif o in ("-s", "--show"): show = string.split(a) if len(args) < 2: usage() infile = open(args[0]) start = args[1:] # process lines in nccout file # and populate dictionary for line in infile: if len(line) == 0: pass elif line[0] == 'D': w = word(line[3:]) if w not in ignore: cfunc = w cfile = None elif line[0] == 'F': if cfunc and cfunc not in show: w = word(line[3:]) if w not in ignore: addfunc(cfunc, w) cfile = None elif line[0] == 'P': cfile = line[3:] cfunc = None elif line[0] == 'Y': addfile(word(line[3:]), cfile) cfunc = None elif line[0] == 'R': l = line[3:].split() rfunc[word(l[0])] = word(l[1]) cfunc = None cfile = None # generate graph print "digraph gengraph {" if dot: print dot # traverse graph for s in start: gengraph(s) if location: pass #todo else: # mark special nodes for s in shown: if s in show: print '"%s" [ style="bold", color="red", shape="box" ];' % s if s[0] == '*': print '"%s" [ shape="hexagon" ];' % s elif s.find('/') >= 0: print '"%s" [ shape="octagon" ];' % s # mark start nodes for s in start: print '"%s" [ style="filled,bold", fillcolor="#dddddd", shape="box" ];' % s print "}" # vim: set ai sw=4 et : ncc-2.8/README.1.8.rw0000644000175000001440000000315110163744321014067 0ustar stanusers00000000000000 It is very very useful to know "which functions modify a variable". [variable: noun. member of structure or global variable] when in the ncc analysis is detected that a variable is definitelly modified, that is reported accordingly. There are of course cases where a variable is indirectly modified and it can't be detected. Such a case which escapes ncc analysis is for example: *(&x) = 3; ncc only reports the cases where a variable is in the lvalue of an assignment (=, +=, -=, etc) or the ++, -- operators are applied on it. That is Good Enough. A Sample Program (analyse this): //-------------start------------ int GLOBAL; typedef struct { int x; } S; int foo () { S s; s.x++; GLOBAL++; } int bar () { int x; S s; x = GLOBAL; x = s.x; } int zoo () { S s; !GLOBAL; GLOBAL = 12; s.x = GLOBAL; GLOBAL = s.x; } //--------------end-------------- If this program is compiled with ncc, the output is: D: foo() S: S.x G: GLOBAL D: bar() g: GLOBAL s: S.x D: zoo() g: GLOBAL G: GLOBAL S: S.x s: S.x P: rr.c L: foo() 6 11 L: bar() 13 20 L: zoo() 22 30 Capital 'S' and 'G' denote that the variable is not modified. The interesting thing is to view this with 'nccnav'. 1. Run nccnav on the above output data. 2. Press on the file. 3. Press on function "bar()". 4. Press on "GLOBAL". Here is the interesting stuff. The resource of study at this point is the variable GLOBAL. Functions "bar()" and "zoo()" are painted blue and are above it. Functions "zoo()" and "foo()" are painted sandy brown and are below it. Happy Hacking! ------------------ ncc-2.8/parser.C0000644000175000001440000011131111074602201013641 0ustar stanusers00000000000000/******************************************************************************* C parser *******************************************************************************/ #include #include #include #include #include #include "global.h" exprtree CExpr; lrt last_result_type; //*************************************************************************** // Forward //*************************************************************************** static NormPtr constant_int_expression (NormPtr, int&); static NormPtr parse_expression (NormPtr); static NormPtr parse_structure (NormPtr); //*************************************************************************** // Declarations I //*************************************************************************** static NormPtr skip_parens (NormPtr); static NormPtr skip_brackets (NormPtr); static int initializer_nsize (NormPtr, int); static NormPtr get_enum_consts (NormPtr); class declarator { NormPtr p; int dp; inline void dcl (); void dirdcl (); void bitfield (); void arglist_to_specs (); NormPtr parse (NormPtr); #ifdef GNU_VIOLATIONS NormPtr bt_typeof (NormPtr); #endif NormPtr builtin (NormPtr); NormPtr bt_enum (NormPtr); NormPtr bt_typedef (NormPtr); NormPtr bt_struct (NormPtr); void complete_size (); void argument_conversions (); void semantics (); bool do_argument_conversions; public: ObjPtr basetype; bool have_extern, have_static, have_typedef, have_const, have_init, have_code, is_anonymous, is_struct_dcl; declarator (bool b = false) { do_argument_conversions = b; is_struct_dcl = false; } NormPtr parse_base (NormPtr); NormPtr parse_dcl (NormPtr); typeID gentype; NormPtr args; NormPtr psst, pslen; int symbol; int spec [MSPEC]; }; //*********************************************************************** // definitions : declarator //*********************************************************************** NormPtr declarator::parse (NormPtr i) { p = i; dp = 0; args = symbol = -1; have_init = have_code = false; if (CODE [i] == ':' || (ISSYMBOL (CODE [i]) && CODE [i + 1] == ':')) bitfield (); else dcl (); /* Ben Lau for M68K Kernel from uClinux */ if (CODE [p] == RESERVED___asm__) p = skip_parens (p + 1); spec [dp] = -1; have_init = CODE [p] == '='; have_code = CODE [p] == '{'; return p; } void declarator::bitfield () { if (CODE [p] != ':') symbol = CODE [p++]; spec [0] = ':'; p = constant_int_expression (++p, spec [1]); dp = 2; } void declarator::dcl () { int ns = 0; for (;; p++) if (CODE [p] == '*') ++ns; else if (CODE [p] != RESERVED_const && CODE [p] != RESERVED_volatile) break; dirdcl (); while (ns--) spec [dp++] = '*'; } NormPtr array_size_expression (NormPtr p, int &r) { NormPtr pe = constant_int_expression (p, r); if (CODE [p] != ']' && infuncs) { pe = parse_expression (p); r = 0; } return pe; } void declarator::dirdcl () { if (CODE [p] == '(') { ++p; dcl (); if (CODE [p++] != ')') syntax_error (p, "Missing parenthesis"); } else if (ISSYMBOL (CODE [p])) symbol = CODE [p++]; for (;;) { switch (CODE [p]) { case '(': if (args == -1) args = p; spec [dp++] = '('; arglist_to_specs (); continue; case '[': if (CODE [++p] == ']') { spec [dp++] = '['; spec [dp++] = 0; p++; continue; } spec [dp++] = '['; p = array_size_expression (p, spec [dp++]); if (CODE [p++] != ']') syntax_error (p, "No :]"); continue; } break; } } void declarator::complete_size () { int t = CODE [p + 1]; if (ISSTRING (t)) gstr: spec [1] = strlen (C_Strings [t - STRINGBASE]) + 1; else if (t == '(' && ISSTRING (CODE [p + 2]) && CODE [p + 3] == ')') { t = CODE [p + 2]; goto gstr; } else if (t == '{') { int i, a = esizeof_objptr (basetype); for (i = 2; spec [i] == '['; i += 2) a *= spec [i + 1]; spec [1] = initializer_nsize (p + 1, a); } else syntax_error (p, "incomplete array initializer single"); } void declarator::semantics () { int i = 0, v, dpi = dp; if (spec [0] == '(') { if (have_init) syntax_error (p, "Function != Variable"); } else if (have_code) syntax_error (p, "Variable != Function"); if (spec [0] == ':') { if (spec [1] < 0 || spec [1] > BITFIELD_Q) syntax_error (p, "absurd"); return; } while (i < dpi) if (spec [i] == '[') { i += 2; if (spec [i] == '(') syntax_error (p, "array of functions"); } else if (spec [i] == '(') { i += 2; v = spec [i]; if (v == '(' || v == '[') syntax_error (p, "Function returning invalid"); } else i++; } static int initializer_nsize (NormPtr p, int na) { int ec = 1; NormPtr e = skip_brackets (p++); while (p < e) if (CODE [p] == ',') ec++, p++; else if (CODE [p] == '{') { ec += na; p = skip_brackets (p); } else p++; return na ? ec / na : ec; } static NormPtr skip_parens (NormPtr p) { int ns = 0; for (; p < C_Ntok; p++) if (CODE [p] == '(') ++ns; else if (CODE [p] == ')') if (--ns == 0) return p + 1; return syntax_error (p, "Unclosed parenthesis:)"); } static NormPtr skip_brackets (NormPtr p) { int ns = 0; for (; p < C_Ntok; p++) if (CODE [p] == '{') ++ns; else if (CODE [p] == '}') if (--ns == 0) return p + 1; return syntax_error (p, "Unclosed brackets:}"); } //*********************************************************************** // definitions : declarator //*********************************************************************** void declarator::argument_conversions () { int tspec [MSPEC], *tp = tspec, *vp = spec; if (basetype >= TYPEDEF_BASE && *vp == -1) { type t; opentype (basetype - TYPEDEF_BASE, t); if (t.spec [0] == '(') *tp++ = '*'; } if (*vp == '(') *tp++ = '*'; for (;;vp++) { switch (*vp) { case '*': *tp++ = '*'; continue; case '[': *tp++ = '*'; vp++; continue; case '(': *tp++ = '('; *tp++ = *++vp; continue; } *tp = -1; break; } intcpy (spec, tspec); } NormPtr declarator::parse_dcl (NormPtr p) { p = parse (p); if (have_init && spec [0] == '[' && spec [1] == 0) complete_size (); semantics (); if (do_argument_conversions) argument_conversions (); gentype = gettype (basetype, spec); return p; } NormPtr declarator::parse_base (NormPtr p) { have_extern = have_static = have_typedef = have_const = is_anonymous = false; while (ISDCLFLAG (CODE [p])) switch (CODE [p++]) { case RESERVED_extern: have_extern = true; break; case RESERVED_static: have_static = true; break; case RESERVED_typedef: have_typedef = true; break; case RESERVED_const: have_const = true; break; } if ((have_extern && have_static) || (have_extern && have_typedef) || (have_static && have_typedef)) syntax_error (p, "Decide"); if (ISBASETYPE (CODE [p]) || ISHBASETYPE (CODE [p])) p = builtin (p); else if (CODE [p] == RESERVED_struct || CODE [p] == RESERVED_union) p = bt_struct (p); else if (ISSYMBOL (CODE [p])) p = bt_typedef (p); else if (CODE [p] == RESERVED_enum) p = bt_enum (p + 1); #ifdef GNU_VIOLATIONS else if (CODE [p] == RESERVED___typeof__) p = bt_typeof (p + 1); else if (CODE [p] == RESERVED___label__) { while (CODE [p] != ';') p++; } #endif else basetype = S_INT; while (ISDCLFLAG (CODE [p])) switch (CODE [p++]) { case RESERVED_extern: have_extern = true; break; case RESERVED_static: have_static = true; break; case RESERVED_typedef: have_typedef = true; break; case RESERVED_const: have_const = true; break; } return p; } NormPtr declarator::builtin (NormPtr p) { int bt, sh, lo, si, us; bt = sh = lo = si = us = 0; for (;ISBASETYPE (CODE [p]) || ISHBASETYPE (CODE [p]) || CODE [p] == RESERVED_const; p++) switch (CODE [p]) { case RESERVED_long: lo++; break; case RESERVED_short: sh++; break; case RESERVED_signed: si++; break; case RESERVED_unsigned: us++; break; case RESERVED_const: continue; default: if (bt) syntax_error (p, "Please specify"); bt = CODE [p]; } if (bt == 0) bt = RESERVED_int; if ((lo && sh) || (si && us)) syntax_error (p, "AMBIGUOUS specifiers"); switch (bt) { case RESERVED_float: basetype = FLOAT; break; case RESERVED_double: basetype = DOUBLE; break; case RESERVED_void: basetype = VOID; break; case RESERVED_char: basetype = (us) ? U_CHAR : S_CHAR; break; case RESERVED_int: if (sh) basetype = (us) ? U_SINT : S_SINT; else if (lo == 1) basetype = (us) ? U_LINT : S_LINT; else if (lo > 1) basetype = (us) ? U_LONG : S_LONG; else basetype = (us) ? U_INT : S_INT; } return p; } NormPtr declarator::bt_enum (NormPtr p) { basetype = S_INT; if (!ISSYMBOL (CODE [p]) && CODE [p] != '{') syntax_error (p, "DEAD rats after enum"); if (CODE [p] != '{' && CODE [p + 1] != '{') { if (!valid_enumtag (CODE [p]) && !introduce_enumtag (CODE [p], true)) syntax_error (p, "enum tag REDEFINED"); return p + 1; } if (CODE [p] != '{') if (!introduce_enumtag (CODE [p++])) syntax_error (p, "enum tag REDEFINED"); return get_enum_consts (p); } NormPtr declarator::bt_typedef (NormPtr p) { if ((basetype = lookup_typedef (CODE [p])) == -1) { basetype = S_INT; return p; } ++p; return ISHBASETYPE (CODE [p]) ? p + 1 : p; } NormPtr declarator::bt_struct (NormPtr p) { bool isst = CODE [p++] == RESERVED_struct; if (!ISSYMBOL (CODE [p]) && CODE [p] != '{') syntax_error (p, "DEAD RATS after struct"); if (CODE [p] != '{' && CODE [p + 1] != '{') { basetype = (CODE [p + 1] == ';') ? fwd_struct_tag (CODE [p], isst) : use_struct_tag (CODE [p], isst); return p + 1; } if ((is_anonymous = CODE [p] == '{')) basetype = introduce_anon_struct (isst); else if ((basetype = introduce_named_struct (CODE [p++], isst)) == -1) syntax_error (p, "Redefined structure tag"); is_struct_dcl = true; psst = p++ - 1; p = parse_structure (p); pslen = p - psst; return p; } static NormPtr get_enum_consts (NormPtr p) { int counter = 0, s; for (++p;;) { s = CODE [p++]; if (s == '}') break; if (!ISSYMBOL (s)) syntax_error (p, "Random NOISE INSIDE ENUM"); if (CODE [p] == '=') p = constant_int_expression (++p, counter); if (!introduce_enumconst (s, counter++)) syntax_error (p, "Enumeration constant exists"); s = CODE [p++]; if (s == '}') break; if (s != ',') syntax_error (p, "RANDOM noise inside enum"); } return p; } //*************************************************************************** // Forward //*************************************************************************** static NormPtr initializer_expr (Symbol, NormPtr); static NormPtr initializer_aggregate (Symbol, NormPtr); static NormPtr parse_function (Symbol, NormPtr, NormPtr, NormPtr); static bool is_dcl_start (int); //*************************************************************************** // Declarations II // // Four places of declarators: // 1) Declarator in code or global // 2) Declarator inside structure block // 3) Function argument list // 4) (type cast) //*************************************************************************** class cast_type { public: typeID gentype; NormPtr parse (NormPtr); }; class arglist { NormPtr argument (NormPtr); NormPtr parse_newstyle (NormPtr); bool old_declaration (NormPtr); NormPtr parse_oldstyle (NormPtr, bool = false); public: bool nolist; typeID types [100]; int names [100], ii; void parse_declare (NormPtr); NormPtr parse (NormPtr); }; class declaration { protected: virtual void semantics (NormPtr); NormPtr parse_initializer (NormPtr); public: declarator D; declaration (bool b = false) : D (b) { } NormPtr parse (NormPtr); }; class declaration_instruct : public declaration { void semantics (NormPtr); }; //*********************************************************************** // definitions: cast, arglist //*********************************************************************** NormPtr cast_type::parse (NormPtr p) { declarator B; p = B.parse_dcl (B.parse_base (p)); if (B.have_typedef || B.have_extern || B.have_static || B.have_code || B.have_init || B.symbol != -1 || B.spec [0] == ':') syntax_error (p, "Improper cast"); gentype = B.gentype; return p; } NormPtr arglist::argument (NormPtr p) { declarator B (true); p = B.parse_dcl (B.parse_base (p)); if (B.have_typedef || B.have_extern || B.have_static || B.have_code || B.have_init || B.spec [0] == ':') syntax_error (p, "Crap argument"); names [ii] = B.symbol; types [ii++] = B.gentype; return p; } bool arglist::old_declaration (NormPtr p) { return ISSYMBOL (CODE [p]) && (CODE [p + 1] == ',' || CODE [p + 1] == ')') && lookup_typedef (CODE [p]) == -1; } NormPtr arglist::parse_newstyle (NormPtr p) { ii = 0; if (CODE [p] == ')') { nolist = true; return p + 1; } if (CODE [p] == RESERVED_void && CODE [p + 1] == ')') return p + 2; while (1) { if (CODE [p] == ELLIPSIS) { types [ii++] = SPECIAL_ELLIPSIS; if (CODE [p + 1] != ')') syntax_error (p, "'...' must be last"); return p + 2; } switch (CODE [p = argument (p)]) { default: syntax_error (p, "Invalid argument list"); case ')': return p + 1; case ',': p++; } } return p + 1; } NormPtr arglist::parse_oldstyle (NormPtr p, bool dcl) { // few syntax/semantics tests. We suppose old program, which // is syntactically correct for the last 10 years int i = 0, j; ii = 1; types [0] = ARGLIST_OPEN; types [1] = -1; if (CODE [p] != ')') do names [i++] = CODE [p++]; while (CODE [p++] == ','); if (dcl) { declaration D (true); while (CODE [p] != '{') { p = D.parse (p); for (j = 0; j < i; j++) if (names [j] == D.D.symbol) { names [j] = -1; break; } } for (j = 0; j < i; j++) if (names [j] != -1) introduce_obj (names [j], SIntType, DEFAULT); } else { if (!is_dcl_start (CODE [p])) return p; while (CODE [p] != '{') p++; } return p; } void arglist::parse_declare (NormPtr p) { if (old_declaration (p)) { parse_oldstyle (p, true); return; } int i; parse_newstyle (p); for (i = 0; i < ii; i++) if (types [i] != SPECIAL_ELLIPSIS) { if (names [i] == -1) syntax_error (p, "Abstract argument"); introduce_obj (names [i], types [i], DEFAULT); } } NormPtr arglist::parse (NormPtr p) { nolist = false; return old_declaration (p) ? parse_oldstyle (p) : parse_newstyle (p); } void declarator::arglist_to_specs () { arglist A; p = A.parse (p + 1); if (A.nolist) A.types [A.ii++] = ARGLIST_OPEN; A.types [A.ii] = -1; spec [dp++] = make_arglist (A.types); } //*********************************************************************** // definitions: declaration, instructure //*********************************************************************** NormPtr declaration::parse (NormPtr p) { VARSPC vs; bool ok; typeID t; NormPtr dclstart = p; // to report beginning of function... D.is_struct_dcl = false; p = D.parse_base (p); if (CODE [p] == ';') { if (D.is_struct_dcl && usage_only && report_structs && !D.is_anonymous) { D.is_struct_dcl = false; struct_location (D.basetype, D.psst, D.pslen); } else if (D.is_struct_dcl && D.is_anonymous) spill_anonymous (D.basetype); } else while (CODE [p] != ';') { p = D.parse_dcl (p); semantics (p); t = D.gentype; vs = (D.have_extern)?EXTERN:(D.have_static)?STATIC:DEFAULT; if (D.have_typedef) ok = introduce_tdef (D.symbol, t); else ok = introduce_obj (D.symbol, t, vs); if (!ok) syntax_error (p, "redefined: ", expand (D.symbol)); if (D.is_anonymous) D.is_anonymous = !rename_struct (t, D.symbol); if (D.is_struct_dcl && usage_only && report_structs) { D.is_struct_dcl = false; struct_location (D.basetype, D.psst, D.pslen); } if (D.have_code) return parse_function (D.symbol, D.args, p, dclstart); if (D.have_init) p = parse_initializer (p + 1); if (CODE [p] == ';') break; if (CODE [p] != ',') { if (CODE [p] != RESERVED___asm__) syntax_error(p, "unaccaptable declaration, separator;", expand (CODE [p])); p = skip_parens (p + 1); continue; } p++; } return p + 1; } NormPtr declaration::parse_initializer (NormPtr p) { if (CODE [p] != '{') return initializer_expr (D.symbol, p); #ifdef PARSE_ARRAY_INITIALIZERS return initializer_aggregate (D.symbol, p); #else return base_of (D.gentype) < _BTLIMIT ? skip_brackets (p) : initializer_aggregate (D.symbol, p); #endif } void declaration::semantics (NormPtr p) { if (D.spec [0] == ':' || (D.have_typedef && (D.have_init || D.have_code))) syntax_error (p, "Not the \"right thing\""); if (D.symbol == -1) syntax_error (p, "ABSENT symbol"); if (D.have_code && !INGLOBAL) syntax_error (p, "Do you like nested functions?"); } void declaration_instruct::semantics (NormPtr p) { if (D.have_static || D.have_extern || D.have_typedef || D.spec [0] == '(' || D.have_init || D.have_code || (D.symbol == -1 && D.spec [0] != ':')) syntax_error (p, "Not good dcl in struct"); } static bool is_typename (int token) { return token == RESERVED_struct || token == RESERVED_union || ISBASETYPE(token) || ISHBASETYPE (token) || token == RESERVED_enum || (ISSYMBOL (token) && is_typedef (token)); } static bool is_dcl_start (int token) { return ISDCLFLAG (token) || is_typename (token) #ifdef GNU_VIOLATIONS || token == RESERVED___label__ || token == RESERVED___typeof__ #endif ; } //************************************************************************** // Part II // // By now we are ok with the declarations (introducing new names // to the program). Whenever we see a declaration, invoke a declarator. // We can at any time lookup what's an identifier, with lookup(). // // Missing: // parsing expressions in initializers // // But this is ok, because this is exactly what we are going to // do now. //************************************************************************** // C expressions //************************************************************************** NormPtr ExpressionPtr; static int bopid; static int priority (int op) { if (op > 256) switch (op) { case LSH: bopid = SHL; return 11; case RSH: bopid = SHR; return 11; case GEQCMP: bopid = CGRE; return 10; case LEQCMP: bopid = CLEE; return 10; case EQCMP: bopid = BEQ; return 9; case NEQCMP: bopid = BNEQ; return 9; case ANDAND: bopid = IAND; return 5; case OROR: bopid = IOR; return 4; default: return 0; } switch (op) { case '*': bopid = MUL; return 13; case '/': bopid = DIV; return 13; case '%': bopid = REM; return 13; case '+': bopid = ADD; return 12; case '-': bopid = SUB; return 12; case '>': bopid = CGR; return 10; case '<': bopid = CLE; return 10; case '&': bopid = BAND; return 8; } if (op == '^') { bopid = BXOR; return 7; } if (op != '|') return 0; bopid = BOR; return 6; } static int xlate_uop (int op) { switch (op) { case '!': return LNEG; case '*': return PTRIND; case '+': return UPLUS; case '-': return UMINUS; case '&': return ADDROF; } switch (op) { case PLUSPLUS: return PPPRE; case MINUSMINUS: return MMPRE; default: return OCPL; } } subexpr *&ee = CExpr.ee; int &NeTop = CExpr.ne; class expression_parser { inline void reloc (); void value_to_expression (exprID, Symbol); #ifdef GNU_VIOLATIONS exprID gnu_st_expr (); exprID gnu_ctor_expr (typeID); #endif #ifdef LABEL_VALUES exprID gnu_label_value (); #endif exprID sizeof_typename (); exprID unary_expression (); exprID argument_expression_list (); exprID primary_expression (); exprID prefix_expression (); exprID binary_expression (exprID, int); exprID logicalOR_expression (); int nee, extra; public: expression_parser (NormPtr, subexpr*); exprID postfix_expression (); exprID assignment_expression (); exprID conditional_expression (); exprID expression (); NormPtr EP; subexpr *ee; int iee; ~expression_parser (); }; //*********************************************************************** // definitions //*********************************************************************** exprID expression_parser::sizeof_typename () { cast_type T; EP = T.parse (EP + 2); if (CODE [EP++] != ')') syntax_error (EP, "erroneous sizeof"); ee [iee].action = VALUE; exprID c = sizeof_typeID (T.gentype); ee [iee].voici.value = c; return iee++; } #define ISUNARY(x) \ ((x<'~') ? (x=='!'||x=='*'||x=='&'||x=='-'||x=='+')\ : (x==PLUSPLUS || x==MINUSMINUS || x=='~')) exprID expression_parser::unary_expression () { int prefix [40]; typeID casts [40]; int ipr = 0, t; exprID e = -1; for (t = CODE [EP]; t < STRINGBASE; t = CODE [++EP]) if (ISUNARY (t)) prefix [ipr++] = xlate_uop (t); else if (t == '(') if (is_dcl_start (CODE [EP + 1])) { cast_type C; EP = C.parse (EP + 1); casts [ipr] = C.gentype; prefix [ipr++] = CAST; #ifdef GNU_VIOLATIONS if (CODE [EP + 1] == '{') { e = gnu_ctor_expr (C.gentype); goto have_unary; } #endif } else break; else if (t == RESERVED_sizeof) if (CODE [EP+1] == '(' && is_dcl_start (CODE [EP+2])) { e = sizeof_typename (); goto have_unary; } else prefix [ipr++] = SIZEOF; #ifdef LABEL_VALUES else if (t == ANDAND) { e = gnu_label_value (); goto have_unary; } #endif else break; if ((e = postfix_expression ()) == -1 && ipr > 0) syntax_error (EP, "prefix operator w/o operand"); have_unary: reloc (); /* * */ while (ipr--) { ee [iee].voici.e = e; if ((ee [iee].action = prefix [ipr]) == CAST) ee [iee].voila.cast = casts [ipr]; e = iee++; } return e; } exprID expression_parser::primary_expression () { int t = CODE [EP++]; if (ISSYMBOL (t)) { ee [iee].action = SYMBOL; ee [iee].voici.symbol = t; return iee++; } if (ISNUMBER (t) || ISSTRING (t)) { value_to_expression (iee, t); return iee++; } if (t != '(') { EP--; return -1; } exprID e; #ifdef GNU_VIOLATIONS if (CODE [EP] == '{') e = gnu_st_expr (); else #endif e = expression (); if (CODE [EP++] != ')') syntax_error (EP, "parse error"); return e; } exprID expression_parser::postfix_expression () { exprID e = primary_expression (), c; if (e == -1) return -1; int t; for (t = CODE [EP];; t = CODE [++EP]) { ee [iee].voici.e = e; switch (t) { case PLUSPLUS: ee [e = iee++].action = PPPOST; break; case MINUSMINUS: ee [e = iee++].action = MMPOST; break; case '[': EP++; ee [e = iee++].action = ARRAY; c = expression (); ee [e].e = c; if (CODE [EP] != ']') syntax_error (EP, "missing ']'"); break; case '(': EP++; ee [e = iee++].action = FCALL; c = argument_expression_list (); ee [e].e = c; if (CODE [EP] != ')') syntax_error (EP, "missing ')'"); break; case POINTSAT: ee [e = iee++].action = PTRIND; ee [iee].voici.e = e; case '.': ee [e = iee++].action = MEMB; ee [e].voila.member = CODE [++EP]; if (!ISSYMBOL (ee [e].voila.member)) syntax_error (EP, "->members only"); break; default: return e; } } } exprID expression_parser::binary_expression (exprID lop, int pri) { static int p2; int coperator = bopid; exprID e; EP++; if ((e = unary_expression ()) == -1) syntax_error (EP, "two operands expected"); p2 = priority (CODE [EP]); while (p2 > pri) e = binary_expression (e, p2); ee [iee].voici.e = lop; ee [iee].action = coperator; ee [iee].e = e; e = iee++; return (p2 < pri) ? e : binary_expression (e, pri); } exprID expression_parser::logicalOR_expression () { int p; exprID e = unary_expression (); if (e == -1) return -1; while ((p = priority (CODE [EP]))) e = binary_expression (e, p); return e; } exprID expression_parser::conditional_expression () { exprID e = logicalOR_expression (), c; if (e == -1) return -1; if (CODE [EP] == '?') { ee [iee].voici.e = e; ee [e = iee++].action = COND; EP++; c = expression (); ee [e].e = c; if (CODE [EP++] != ':') syntax_error (EP, "(what) ? is ':' missing"); if ((c = conditional_expression ()) == -1) syntax_error (EP, "(what) ? is : missing"); ee [e].voila.eelse = c; } return e; } exprID expression_parser::assignment_expression () { exprID e = conditional_expression (), c; if (e == -1) return -1; if (!ISASSIGNMENT (CODE [EP])) return e; ee [iee].voici.e = e; ee [e = iee++].action = CODE [EP++]; if ((c = assignment_expression ()) == -1) syntax_error (EP, " = (missing operand)"); ee [e].e = c; return e; } exprID expression_parser::expression () { exprID e = assignment_expression (), c; if (e == -1) return -1; if (CODE [EP] == ',') { ++EP; ee [iee].voici.e = e; ee [e = iee++].action = COMMA; c = expression (); ee [e].e = c; } return e; } exprID expression_parser::argument_expression_list () { exprID e = assignment_expression (), c; if (e == -1) return -1; if (CODE [EP] == ',') { ++EP; ee [iee].voici.e = e; ee [e = iee++].action = ARGCOMMA; c = argument_expression_list (); ee [e].e = c; } return e; } expression_parser::expression_parser (NormPtr p, subexpr *tee) { #define STDNEE 138 ExpressionPtr = EP = p; nee = STDNEE; extra = false; ee = tee; iee = 0; } void expression_parser::reloc () { #define RELOCAT 10 if (nee - iee < RELOCAT) { subexpr *bigger = new subexpr [nee += 32]; memcpy (bigger, ee, iee * sizeof ee [0]); if (extra) delete [] ee; ee = bigger; extra = true; } } expression_parser::~expression_parser () { if (extra) delete [] ee; } //*********************************************************************** // definitions //*********************************************************************** void expression_parser::value_to_expression (exprID i, Symbol s) { if (ISSTRING (s)) { ee [i].action = SVALUE; ee [i].voici.value = s - STRINGBASE; return; } if (s >= INUMBER) { ee [i].action = UVALUE; ee [i].voici.uvalue = s - INUMBER; return; } if (s >= FLOATBASE) { ee [i].action = FVALUE; ee [i].voici.fvalue = C_Floats [s - FLOATBASE]; return; } if (s >= UINT32BASE) { ee [i].action = UVALUE; ee [i].voici.uvalue = C_Unsigned [s - UINT32BASE]; return; } ee [i].action = VALUE; ee [i].voici.value = getint (s); } static NormPtr parse_expression (NormPtr p) { subexpr tee [STDNEE]; expression_parser PP (p, tee); CExpr.first = PP.expression (); CExpr.ee = PP.ee; CExpr.ne = PP.iee; if (!usage_only) prcode (p, PP.EP - p); ncc->cc_expression (); return PP.EP; } static NormPtr constant_int_expression (NormPtr p, int &r) { if (ISNUMBER (CODE [p]) && CODE [p] < UINT32BASE && !priority (CODE [p + 1])) { r = getint (CODE [p]); return p + 1; } subexpr tee [STDNEE]; expression_parser PP (p, tee); CExpr.first = PP.conditional_expression (); CExpr.ee = PP.ee; CExpr.ne = PP.iee; r = cc_int_expression (); return PP.EP; } //************************************************************************** // // Some GNU Violations we don't like // //************************************************************************** #ifdef GNU_VIOLATIONS static NormPtr compound_statement (NormPtr p); exprID expression_parser::gnu_st_expr () { EP = compound_statement (EP + 1); ee [iee].action = COMPOUND_RESULT; ee [iee].voici.using_result = last_result; ee [iee].voila.result_type = gettype (last_result_type.base, last_result_type.spec); return iee++; } static NormPtr initializer_aggregate (Symbol, NormPtr); exprID expression_parser::gnu_ctor_expr (typeID c) { open_compound (); introduce_obj (intern_sym, c, DEFAULT); EP = initializer_aggregate (intern_sym, EP + 1); //EP = skip_brackets (EP + 1); close_region (); ee [iee].action = VALUE; return iee++; } NormPtr declarator::bt_typeof (NormPtr p) { if (CODE [p++] != '(') syntax_error (p, "typeof '('"); if (is_dcl_start (CODE [p])) { cast_type C; p = C.parse (p); basetype = C.gentype + TYPEDEF_BASE; } else { subexpr tee [STDNEE]; expression_parser PP (p, tee); CExpr.first = PP.expression (); CExpr.ee = PP.ee; CExpr.ne = PP.iee; basetype = typeof_expression (); p = PP.EP; } if (CODE [p] != ')') syntax_error (p, "typeof ')'"); return p + 1; } #endif #ifdef LABEL_VALUES exprID expression_parser::gnu_label_value () { int s = CODE [++EP]; if (!ISSYMBOL (s)) syntax_error (EP, "&&identifier only, for labels"); ee [iee].action = AVALUE; ee [iee].voici.symbol = s; EP++; return iee++; } #endif //************************************************************************** // Part III // // statements, this is really easy //************************************************************************** static int IDLEV = -1; #define PCMD(CMD) \ if (pseudo_code) {\ PRINTF ("#X: %s\n", CMD);\ } struct PINDENT { PINDENT () { IDLEV += 1; } ~PINDENT () { PCMD ("*"); IDLEV -= 1; } }; #define PSEUDOCODE(CMD) PINDENT P; PCMD(CMD); static NormPtr while_statement (NormPtr p); static NormPtr for_statement (NormPtr p); static NormPtr do_statement (NormPtr p); static NormPtr switch_statement (NormPtr p); static NormPtr if_statement (NormPtr p); static NormPtr compound_statement (NormPtr p); static NormPtr __asm___statement (NormPtr p); static NormPtr statement (NormPtr p) { int t = CODE [p++]; if (ISSYMBOL (t) && CODE [p] == ':') return CODE [p + 1] == '}' ? p + 1 : statement (p + 1); switch (t) { case RESERVED_default: if (CODE [p] != ':') syntax_error (p, "default:"); return p + 1; case RESERVED_case: int tmp; p = constant_int_expression (p, tmp); if (CODE [p] == ELLIPSIS) p = constant_int_expression (p + 1, tmp); if (CODE [p] != ':') syntax_error (p, "case ERROR:"); return p + 1; case RESERVED_continue: case RESERVED_break: if (CODE [p] != ';') syntax_error (p, "break | continue ';'"); return p + 1; case RESERVED_goto: if (ISSYMBOL (CODE [p]) && CODE [p + 1] == ';') return p + 2; case RESERVED_return: p = parse_expression (p); if (CODE [p] != ';') syntax_error (p, "return ... ';'"); return p + 1; case RESERVED_while: return while_statement (p); case RESERVED_for: return for_statement (p); case RESERVED_do: return do_statement (p); case RESERVED_switch: return switch_statement (p); case RESERVED_if: return if_statement (p); case RESERVED___asm__: return __asm___statement (p); case RESERVED_else: syntax_error (p, "else without if"); case '{': return compound_statement (p); default: p = parse_expression (p - 1); if (CODE [p++] != ';') syntax_error (p, "expression + ';' = statement"); case ';': return p; } } static NormPtr parenth_expression (NormPtr p) { if (CODE [p++] != '(') syntax_error (p, "'(' expression"); p = parse_expression (p); if (CODE [p++] != ')') syntax_error (p, "expression ')'"); return p; } static NormPtr while_statement (NormPtr p) { PSEUDOCODE ("REPEAT"); return statement (parenth_expression (p)); } static NormPtr do_statement (NormPtr p) { PSEUDOCODE ("REPEAT"); p = statement (p); if (CODE [p++] != RESERVED_while) syntax_error (p, "do w/o while"); p = parenth_expression (p); if (CODE [p++] != ';') syntax_error (p, "This is special, but you need a ';'"); return p; } static NormPtr for_statement (NormPtr p) { if (CODE [p++] != '(') syntax_error (p, "for '('..."); p = parse_expression (p); PSEUDOCODE ("REPEAT"); if (CODE [p++] != ';') syntax_error (p, "for (expression ';' ..."); p = parse_expression (p); if (CODE [p++] != ';') syntax_error (p, "for (expression; expression ';' ..."); p = parse_expression (p); if (CODE [p++] != ')') syntax_error (p, "for (;; ')'"); return statement (p); } static NormPtr switch_statement (NormPtr p) { p = parenth_expression (p); return statement (p); } static NormPtr if_statement (NormPtr p) { { PSEUDOCODE ("IF"); p = statement (parenth_expression (p)); } if (CODE [p] == RESERVED_else) { PSEUDOCODE ("ELSE"); return statement (p + 1); } return p; } static NormPtr parse_declaration (NormPtr p) { declaration D; return D.parse (p); } static NormPtr compound_statement (NormPtr p) { open_compound (); #ifdef NCC_ISOC99 while (CODE [p] != '}') p = (is_dcl_start (CODE [p]) && CODE [p + 1] != ':') ? parse_declaration (p) : statement (p); #else while (CODE [p] != '}') if (is_dcl_start (CODE [p])) p = parse_declaration (p); else break; while (CODE [p] != '}') p = statement (p); #endif close_region (); return p + 1; } static NormPtr __asm___statement (NormPtr p) { NormPtr ast = p + 1; if (CODE [p] != '(') syntax_error (p, "__asm__ '('"); p = skip_parens (p); if (CODE [p++] != ';') syntax_error (p, "asm() ';'"); ncc->inline_assembly (ast, p - ast - 1); #ifdef GNU_VIOLATIONS last_result_type.base = VOID; last_result_type.spec [0] = -1; #endif ++last_result; return p; } //*********************************************************************** // // Part IV - the rest // //*********************************************************************** //*********************************************************************** // // Aggregate initializers: // The goal is to: a) Catch functions assigned to structure // members, and b) Catch function calls in initializers. // //*********************************************************************** static inline NormPtr join_expression (dcle &E, NormPtr p) { #if 0 if (ISNUMBER (CODE [p]) || ISSTRING (CODE [p])) if (CODE [p + 1] == ',' || CODE [p + 1] == '}') { // skip these quickly (may be lots of them) E.tofield (); return p + 1; } #endif NormPtr pe, cntk; subexpr tee [STDNEE]; expression_parser EEP (p, tee); exprID e; typeID t; Symbol *se, *tmp; // root subexpression manually constructed tee [0].action = '='; EEP.iee = 1; // right part of the assignment is in CODE[] // and is assignment expression if ((e = EEP.assignment_expression ()) == -1) syntax_error (p, "wrong in here"); pe = EEP.EP; EEP.ee [0].e = e; // check to see if the type of the assignment is // a structure CExpr.first = e; CExpr.ee = EEP.ee; CExpr.ne = EEP.iee; t = typeof_expression () - TYPEDEF_BASE; // ..and advance E accordingly if (spec_of (t)[0] == -1 && base_of (t) > _BTLIMIT) E.tostruct (base_of (t)); else E.tofield (); // skip these quickly (calculated constant exprs) #if 0 if (EEP.ee [e].action <= FVALUE) return pe; #endif // left part of the assignment is made in // E.mk_current and is a postfix expression tmp = E.mk_current (); if (0||!usage_only) prcode (p, EEP.EP - p, tmp); cntk = C_Ntok; se = CODE; CODE = tmp; for (C_Ntok = 0; CODE [C_Ntok] != -1; C_Ntok++); EEP.EP = 0; e = EEP.postfix_expression (); EEP.ee [0].voici.e = e; CODE = se; C_Ntok = cntk; // these two joined at tee[0] which is an // assignment. Now send to compiler CExpr.first = 0; CExpr.ee = EEP.ee; CExpr.ne = EEP.iee; ncc->cc_expression (); return pe; } static NormPtr initializer_aggregate (Symbol s, NormPtr p) { // aggregates can be very complex. if something bad happens, skip analysis NormPtr p0 = p; dcle E (s); for (;;) { for (;;) if (CODE [p] == '{') { p++; if (!E.open_bracket ()) goto Bad; } else if (ISSYMBOL (CODE [p]) && CODE [p + 1] == ':') { Symbol d [2] = { CODE [p], -1 }; if (!E.designator (d)) goto Bad; if (CODE [p += 2] != '{') break; } else if (CODE [p] == '.' || CODE [p] == '[') { Symbol d [20]; int di = 0; while (CODE [p] == '.' || CODE [p] == '[') if (CODE [p++] == '.') d [di++] = CODE [p++]; else { p = constant_int_expression (p, d [di]); if (CODE [p++] == ELLIPSIS) p = constant_int_expression (p, d [di]) + 1; if (d [di] < 0) d [di] = -1; d [di++] += INUMBER; } if (CODE [p] == '=') p++; d [di] = -1; if (!E.designator (d)) goto Bad; if (CODE [p] != '{') break; } else break; if (CODE [p] != ',' && CODE [p] != '}') p = join_expression (E, p); for (;;) if (CODE [p] == '}') { p++; if (!E.close_bracket ()) return p; //*/*/*/*/*/*/*> OUT } else if (CODE [p] == ',') { if (CODE [++p] == '}') continue; if (!E.comma ()) syntax_error (p, "excess"); } else break; } Bad: return skip_brackets (p0); } //******** end aggregate initializers static NormPtr initializer_expr (Symbol s, NormPtr p) { // if (ISSTRING (CODE [p])) { // return p + 1; // } subexpr tee [STDNEE]; tee [0].action = '='; tee [0].voici.e = 1; tee [1].action = SYMBOL; tee [1].voici.symbol = s; expression_parser EEP (p, tee); EEP.iee = 2; exprID e; if ((e = EEP.assignment_expression ()) == -1) syntax_error (p, "erroneous initialization"); EEP.ee [0].e = e; CExpr.first = 0; CExpr.ee = EEP.ee; CExpr.ne = EEP.iee; if (!usage_only) prcode (p, EEP.EP - p, s); ncc->cc_expression (); return EEP.EP; } static NormPtr parse_structure (NormPtr p) { declaration_instruct D; while (CODE [p] != '}' && p < C_Ntok) p = D.parse (p); close_region (); return p + 1; } static NormPtr parse_function (Symbol s, NormPtr a, NormPtr p, NormPtr b) { NormPtr e = skip_brackets (p); if (!function_definition (s, a, p, e, b+1)) //syntax_error (p, "Function definition already defined", expand (s)); ; return e; } void do_functions () { int i; NormPtr args, body; infuncs = true; for (i = 0; function_no (i, &args, &body); i++) { PSEUDOCODE("PCODE.0"); arglist A; A.parse_declare (args + 1); compound_statement (body + 1); close_region (); } } // // Well, that's it. // void parse_translation_unit () { NormPtr p = 0; declaration D; while (p < C_Ntok) if (CODE [p] == ';') p++; else if (CODE [p] == RESERVED___asm__) p = skip_parens (p + 1); else p = D.parse (p); do_functions (); } void parse_C () { parse_translation_unit (); } ncc-2.8/space.C0000644000175000001440000002027111074114517013454 0ustar stanusers00000000000000#include #include #include #include #include #include "global.h" #include "dbstree.h" #include "mem_pool.h" bool include_values = true; static int tprev_line = -1, *prev_line = &tprev_line; static earray Filez; static mem_pool Linez; static mem_pool tmpCODE; static mem_pool TsInt8; static mem_pool TsInt16; static mem_pool TsInt32; static mem_pool TuInt32; static mem_pool TFloat; static int nreserved, indx; class symboltmp; class stringtmp; static dbsTree symtree; static dbsTree strtree; class symboltmp : public dbsNodeStr { public: symboltmp *less, *more; symboltmp (int I) : dbsNodeStr (), ID (I) { symtree.addself (this); } unsigned int ID; }; class stringtmp : public dbsNodeStr { public: stringtmp *less, *more; stringtmp (int I) : dbsNodeStr (), ID (I) { strtree.addself (this); } unsigned int ID; }; static void symtoarray (symboltmp *s) { if (s->ID >= SYMBASE) C_Syms [s->ID - SYMBASE] = s->Name; delete s; } static void strtoarray (stringtmp *s) { C_Strings [s->ID - STRINGBASE] = s->Name; delete s; } static char *string; static int enter_string () { static unsigned int SID = STRINGBASE; //DBS_STRQUERY = true_string (string); DBS_STRQUERY = string; stringtmp *S = (stringtmp*) strtree.dbsFind (); if (S) delete [] DBS_STRQUERY; else S = new stringtmp (SID++); return S->ID; } static int c_symbol (char *s, int len) { static unsigned int SID = SYMBASE; DBS_STRQUERY = (char*) alloca (len + 1); strncpy (DBS_STRQUERY, s, len); DBS_STRQUERY [len] = 0; symboltmp *S = (symboltmp*) symtree.dbsFind (); if (S) return S->ID; DBS_STRQUERY = StrDup (DBS_STRQUERY); S = new symboltmp (SID++); return S->ID; } static int enter_float () { int i; TFloat [i = TFloat.alloc ()] = strtod (CTok.p, NULL); return FLOATBASE + i; } static int character_constant (); static int enter_integer () { long int is, iu; if (CTok.type == CCONSTANT) is = character_constant (); else is = strtol (CTok.p, NULL, 0); if (is == 0) return INT8BASE; if (is < 128 && is >= -128) { TsInt8 [iu = TsInt8.alloc ()] = is; return INT8BASE + iu; } if (is < 32768 && is >= -32768) { TsInt16 [iu = TsInt16.alloc ()] = is; return INT16BASE + iu; } if (is != LONG_MAX) { TsInt32 [iu = TsInt32.alloc ()] = is; return INT32BASE + iu; } TuInt32 [iu = TuInt32.alloc ()] = strtoul (CTok.p, NULL, 0); return UINT32BASE + iu; } static void string_constant (char *s, int l) { char *ns; int si; if (!include_strings) { string = ""; return; } if (string) { ns = new char [(si = strlen (string) + l) + 1]; strncat (strcpy (ns, string), s, l); ns [si] = 0; delete [] string; string = ns; } else { string = new char [l + 1]; strncpy (string, s, l); string [l] = 0; } } static inline void _enter_token (int i) { if (i == RESERVED_auto || i == RESERVED_volatile || i == RESERVED_inline || i == RESERVED__Complex) return; if (i == RESERVED_double) i = RESERVED_float; tmpCODE [indx = tmpCODE.alloc ()] = i; if (*prev_line != CTok.at_line) { Linez [i = Linez.alloc ()].ftok = indx; Linez [i].line = *prev_line = CTok.at_line; } } void enter_token () { int i; if (CTok.type == IDENT_DUMMY) i = c_symbol (CTok.p, CTok.len); else if (CTok.type == STRING) { string_constant (CTok.p, CTok.len); return; } else if (CTok.type == CONSTANT || CTok.type == CCONSTANT) i = (include_values) ? enter_integer () : INT8BASE; else if (CTok.type == FCONSTANT) i = (include_values) ? enter_float () : FLOATBASE; else i = CTok.type; if (string) { _enter_token ((include_strings) ? enter_string () : STRINGBASE); string = NULL; } _enter_token (i); } void enter_file_indicator (char *f) { int last; if (!f [0]) return; if (Filez.nr) { last = Filez.nr - 1; if (!strcmp (f, Filez.x [last].file)) return; if (Filez.x [last].indx == indx + 1) { delete [] Filez.x [last].file; Filez.x [last].file = StrDup (f); return; } } last = Filez.alloc (); Filez.x [last].indx = indx + 1; Filez.x [last].file = StrDup (f); } #define LOOKBUILTIN(x) \ DBS_STRQUERY = #x;\ S = (symboltmp*) symtree.dbsFind ();\ ccbuiltins.bt ## x = (S) ? (int) S->ID : -1; static void used_builtins () { symboltmp *S; LOOKBUILTIN (__func__); LOOKBUILTIN (__FUNCTION__); LOOKBUILTIN (__PRETTY_FUNCTION__); LOOKBUILTIN (__builtin_return_address); LOOKBUILTIN (__builtin_alloca); } void make_norm () { if (string) _enter_token (enter_string ()); used_builtins (); // C_Ntok = tmpCODE.nr (); CODE = new int [C_Ntok + 3]; tmpCODE.copy (&CODE); CODE [C_Ntok] = FORCEERROR; CODE [C_Ntok + 1] = ';'; CODE [C_Ntok + 2] = FORCEERROR; tmpCODE.destroy (); // C_Syms = new char* [C_Nsyms = symtree.nnodes - nreserved]; symtree.deltree (symtoarray); Filez.freeze (); C_Files = Filez.x; C_Nfiles = Filez.nr; C_Nlines = Linez.nr (); Linez.copy (&C_Lines); Linez.destroy (); C_Strings = new char* [C_Nstrings = strtree.nnodes]; strtree.deltree (strtoarray); // TFloat.copy (&C_Floats); TFloat.destroy (); TsInt8.copy (&C_Chars); TsInt8.destroy (); TsInt16.copy (&C_Shortints); TsInt16.destroy (); TsInt32.copy (&C_Ints); TsInt32.destroy (); TuInt32.copy (&C_Unsigned); TuInt32.destroy (); } #define RESERVED(x) \ DBS_STRQUERY = #x;\ symtree.dbsFind ();\ new symboltmp (RESERVED_ ## x); \ ++nreserved; static void reserved_c () { RESERVED(__inline__); RESERVED(__inline); RESERVED(inline); RESERVED(do); RESERVED(struct); RESERVED(case); RESERVED(for); RESERVED(short); RESERVED(union); RESERVED(sizeof); RESERVED(register); RESERVED(break); RESERVED(auto); RESERVED(continue); RESERVED(const); RESERVED(default); RESERVED(enum); RESERVED(else); RESERVED(extern); RESERVED(goto); RESERVED(if); RESERVED(long); RESERVED(return); RESERVED(signed); RESERVED(static); RESERVED(switch); RESERVED(typedef); RESERVED(unsigned); RESERVED(volatile); RESERVED(while); RESERVED(__asm__); #ifdef GNU_VIOLATIONS RESERVED(__typeof__); RESERVED(__label__); RESERVED(_Complex); #endif RESERVED(void); RESERVED(int); RESERVED(char); RESERVED(float); RESERVED(double); } Symbol intern_sym; void prepare () { reserved_c (); symtree.dbsBalance (); indx = -1; if (!include_strings) { string = ""; enter_string (); } string = NULL; TFloat [TFloat.alloc ()] = 0.0; // often have division by zero if all values zeroed TsInt8 [TsInt8.alloc ()] = include_values ? 0 : 1; intern_sym = c_symbol ("_@@@_", 5); } //****************************************************** // small utility // evaluate number which is integer //****************************************************** int getint (int token) { if (token < INT16BASE) return C_Chars [token - INT8BASE]; if (token < INT32BASE) return C_Shortints [token - INT16BASE]; if (token < UINT32BASE) return C_Ints [token - INT32BASE]; return syntax_error ("Expected integer and got something else", "else"); } //****************************************************** // small utility // evaluate character constants // a terrible deja-vu... //****************************************************** const static char escc [] = "ntvbrfae"; const static char esct [] = "\n\t\v\b\r\f\a\e"; static char escape (char **s) { char i; (*s)++; if (**s >= '0' && **s < '8') { i = **s - '0'; (*s)++; if (**s >= '0' && **s < '8') { i <<= 3; i = **s - '0'; (*s)++; if (**s >= '0' && **s < '8') { i <<= 3; i += **s - '0'; } } return i; } if (**s == 'x') { (*s)++; if (!isxdigit (**s)) warning ("bad hex character escape"); i = (**s - '0') << 4; (*s)++; if (!isxdigit (**s)) warning ("bad hex character escape"); return i + (**s - '0'); } for (i = 0; i < (int) sizeof (escc) - 1; i++) if (**s == escc [(int)i]) return esct [(int)i]; return **s; } static int character_constant () { char *s = CTok.p; if (*s == '\'') { warning ("Empty character constant"); return 0; } return (*s == '\\') ? escape (&s) : s [1]; } #if 0 static char *true_string (char *s) { char *e = s; char *d; char *tmp = d = (char*) alloca (strlen (s) + 5); // heuristic for (;*s; s++) *d++ = *s == '\\' ? escape (&s) : *s; *d = 0; delete [] e; return StrDup (tmp); } #endif ncc-2.8/preproc.C0000644000175000001440000003154011074141740014032 0ustar stanusers00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" bool usage_only, multiple, include_strings, quiet = false; bool abs_paths = false, report_structs = true, pseudo_code = false; bool halt_on_error = false, no_error = false; char *sourcefile, *preprocfile = PREPROCESSOR_OUTPUT, *cwd; static char **ncc_key; static bool issource (char *f) { char *e = f + strlen (f) - 1; if (*(e-1) == '.') return *e == 'c' || *e == 'h' || *e == 'C' || *e == 'i'; return *(e-3) == '.' && (*(e-2) == 'c' || *(e-2) == 'C'); } static bool isobj (char *f) { int i = strlen (f); return (f [i-1] == 'o' || f [i-1] == 'a') && f [i-2] == '.'; } static char *objfile (char *f) { char *r = strdup (f); char *p = rindex (r, '.'); if (p) strcpy (p, ".o"); return r; } FILE *report_stream; static void openout (char *f, char *mode = "w") { if (!f) return; char *c = (char*) alloca (strlen (f) + sizeof OUTPUT_EXT); report_stream = fopen (strcat (strcpy (c, f), OUTPUT_EXT), mode); fprintf (stderr, "ncc: output file is ["COLS"%s"COLE"]\n", c); } static void stripout (char *f) { if (report_stream != stdout) { fclose (report_stream); char tmp [1024]; sprintf (tmp, "nccstrip2.py %s"OUTPUT_EXT" %s"OUTPUT_EXT, f, f); system (tmp); } report_stream = stdout; } static void CATFILE (char *data, int len, FILE *out) { /* writing out a huge mmaped file with fwrite takes a lot of time for some reason. */ #define MAXWRITE 16 * 1024 if (len <= MAXWRITE) { fwrite (data, 1, len, out); return; } CATFILE (data, len / 2, out); CATFILE (data + len / 2, len - len / 2, out); } #define NCCOBJ "# nccobj " static void LINK (char **obj, int objn) { int i; for (i = 0; i < objn; i++) { char *ncobj = (char*) alloca (strlen (obj [i]) + 10 + sizeof OUTPUT_EXT); strcat (strcpy (ncobj, obj [i]), OUTPUT_EXT); load_file L (ncobj); if (L.success == ZC_OK) { fprintf (stderr, "ncc: Linking object file ["COLS"%s"COLE"] (%i bytes)\n", ncobj, L.len); PRINTF (NCCOBJ"%s\n", ncobj); CATFILE (L.data, L.len, report_stream); } } } static void RUN (char *outfile, char **argv) { int i; if (!quiet) { fprintf (stderr, "Running: "); for (i = 0; argv [i]; i++) fprintf (stderr, "%s ", argv [i]); fprintf (stderr, "\n"); } int pid = fork (); if (pid == 0) { if (outfile) { if (!freopen (outfile, "w", stdout)) exit (127); } execvp (argv [0], argv); exit (127); } int status; waitpid (pid, &status, 0); if (WEXITSTATUS (status) != 0) { fprintf (stderr, "[%s] Program failed..\n", argv[0]); exit (1); } } /************************************************************* # # binutils emulation mode. # happens to work in some cases... # *************************************************************/ static char *ext (char *f) { char *f2 = (char*) alloca (strlen (f) + 10 + sizeof OUTPUT_EXT); return StrDup (strcat (strcpy (f2, f), OUTPUT_EXT)); } static bool startswith (char *s, char *h) { return strncmp (s, h, strlen (h)) == 0; } static void nccar_x (int argc, char **argv) { /* ar x archive [members] * extract into current directory. At the moment we extract all members. */ argc--, ++argv; FILE *F = fopen (ext (argv [0]), "r"), *OUTF = 0; argc--, ++argv; int i; char tmp [1024], n [500]; if (!F) return; while (fgets (tmp, sizeof tmp, F)) { if (startswith (tmp, NCCOBJ)) { strcpy (n, tmp + sizeof NCCOBJ - 1); n [strlen (n) - 1] = 0; if (OUTF) fclose (OUTF); OUTF = fopen (n, "w"); fprintf (stderr, "ncc: extract ["COLS"%s"COLE"]\n", n); } if (OUTF) fputs (tmp, OUTF); } if (OUTF) fclose (OUTF); } static void nccar (int argc, char **argv) { /* * we take the simple usage: * ar [max-four-options] archive-file.[ao] obj1.o obj2.o ... */ int i, j; if (strchr (argv [1], 'x')) return nccar_x (argc-1, argv+1); if (!strchr (argv [1], 'r')) { for (i = 1; i < 5 && i < argc; i++) if (isobj (argv [i])) { openout (argv [i], "a"); LINK (&argv [i + 1], argc - i - 1); stripout (argv [i]); return; } fprintf (stderr, "nccar: Nothing to do\n"); } else { for (i = 1; i < 5 && i < argc; i++) if (isobj (argv [i])) break; #define TMPARCH "tmp.nccarchive" char *archive = ext (argv [i]); link (archive, TMPARCH); unlink (archive); FILE *oldarch = fopen (TMPARCH, "r"); if (!oldarch) { openout (argv [i], "a"); LINK (&argv [i + 1], argc - i - 1); stripout (argv [i]); return; } /* replacing members in the archive */ char *outfile = argv [i]; openout (outfile, "w"); bool skipping = false; char l [512], n [512]; while (fgets (l, 500, oldarch)) { if (startswith (l, NCCOBJ)) { strcpy (n, l + sizeof NCCOBJ - 1); n [strlen (n) - sizeof OUTPUT_EXT] = 0; skipping = false; for (j = i; j < argc; j++) if (!strcmp (argv [j], n)) { LINK (&argv [j], 1); argv [j] = " "; skipping = true; break; } } if (!skipping) fputs (l, report_stream); } fclose (oldarch); unlink (TMPARCH); for (j = i + 1; j < argc; j++) if (strcmp (argv [j], " ")) LINK (&argv [j], 1); stripout (outfile); } } static void nccld (int argc, char **argv) { /* * 'c++'/'g++' is sometimes used as a linker * we only do the job if there are object files */ int i, objfileno = 0; char *ofile = 0, **objfiles = (char**) alloca (argc * sizeof *objfiles); for (i = 1; i < argc; i++) if (argv [i][0] == '-') { if (argv [i][1] == 'o' && argv [i][2] == 0) ofile = argv [++i]; } else if (isobj (argv [i])) objfiles [objfileno++] = argv [i]; if (ofile && objfileno) { openout (ofile); LINK (objfiles, objfileno); stripout (ofile); } else fprintf (stderr, "%s: Nothing to do\n", argv [0]); } static void emubinutils (int argc, char **argv) { if (0); #define PROGNAME(x, y) else if (!strcmp (argv [0] + strlen (argv [0]) + 1 - sizeof x, x)) {\ argv [0] = x;\ RUN (NULL, argv);\ ncc ## y (argc, argv);\ } #define SPROGNAME(x) PROGNAME(#x,x) SPROGNAME(ar) SPROGNAME(ld) PROGNAME("c++", ld) PROGNAME("g++", ld) else return; fflush (stdout); exit (0); } const char help [] = "ncc "NCC_VERSION" - The new/next generation C compiler\n" "The user is the only one responsible for any damages\n" "Written by Stelios Xanthakis\n" "Homepage: http://students.ceid.upatras.gr/~sxanth/ncc/\n" "\n" "Options starting with '-nc' are ncc options, while the rest gcc:\n" " -ncgcc : also run gcc compiler (produces useful object file)\n" " -ncgcc=PROG : use PROG instead of \"gcc\"\n" " -nccpp=PROG : use PROG for preprocessing\n" " Files:\n" " -ncld : emulate object file output: write the output to "OUTPUT_EXT"\n" " -ncoo : write the output to sourcefile.c"OUTPUT_EXT"\n" " -ncspp : keep sourcefile.i preprocessor output instead of " PREPROCESSOR_OUTPUT"\n" " Output:\n" " -nccc : compile and produce virtual bytecode\n" " -ncmv : display multiple uses of functions and variables\n" " -ncpc : like -ncmv but also produce pseudo-code data\n" " -ncnrs : do not report structure declaration locations (lots of data)\n" " -ncerr : halt ncc on the first expression error detected\n" " Switches:\n" " -nc00 : do not include constant values (faster/less memory)\n" " Extras:\n" " -nckey : scan source file for additional output (see doc)\n" " Filenames:\n" " -ncfabs : report absolute pathnames in the output\n" " -nc- : ignored option\n" "\nncc can also be called as 'nccar', 'nccld', 'nccc++', 'nccg++'\n" "In these cases it will invoke the programs 'ar', 'ld', 'c++' and 'g++'\n" "and then attempt to collect and link object files with the extension "OUTPUT_EXT"\n" ; void preproc (int argc, char**argv) { int i; report_stream = stdout; for (i = 0; i < argc; i++) if (!strcmp (argv [i], "-ncquiet")) { quiet = true; for (--argc; i < argc; i++) argv [i] = argv [i + 1]; argv [i] = 0; } if (1 && !quiet) { fprintf (stderr, "Invoked: "); for (i = 0; i < argc; i++) fprintf (stderr, "%s ", argv [i]); fprintf (stderr, "\n"); } /* emulate ar, ld, ... */ emubinutils (argc, argv); bool spp = false, dontdoit = false, mkobj = false, ncclinker = false; bool rungcc = false; char *keys [10]; char **gccopt, **cppopt, **nccopt, **files, **objfiles, **nofileopt; char *ofile = 0; int gccno, cppno, nccno, filesno, objfileno, nofileno, keyno; cppopt = (char**) alloca ((8 + argc) * sizeof (char*)); nccopt = (char**) alloca ((3 + argc) * sizeof (char*)); gccopt = (char**) alloca ((3 + argc) * sizeof (char*)); files = (char**) alloca (argc * sizeof (char*)); objfiles = (char**) alloca (argc * sizeof (char*)); nofileopt = (char**) alloca ((3 + argc) * sizeof (char*)); cppopt [0] = gccopt [0] = "gcc"; cppopt [1] = "-E"; cppopt [2] = "-D__NCC__"; cppopt [3] = "-imacros"; cppopt [4] = NOGNU; nofileopt [0] = "ncc"; files [0] = NULL; cppno = 5; gccno = 1; keyno = filesno = nccno = objfileno = 0; nofileno = 1; for (i = 1; i < argc; i++) if (argv [i][0] == '-' && argv [i][1] == 'n' && argv [i][2] == 'c') { nccopt [nccno++] = (nofileopt [nofileno++] = argv[i]) + 3; if (!strcmp (argv [i], "-ncld")) ncclinker = true; else if (!strcmp (argv [i], "-ncgcc")) rungcc = true; } else { gccopt [gccno++] = argv [i]; if (issource (argv [i])) cppopt [cppno++] = files [filesno++] = argv [i]; else { nofileopt [nofileno++] = argv [i]; if (isobj (argv [i])) objfiles [objfileno++] = argv [i]; if (argv [i][0] == '-') if (argv [i][1] == 'O' || argv [i][1] == 'm') goto addit; else if (argv [i][1] == 'D' || argv [i][1] == 'I') if (argv [i][2] == 0) goto separate; else addit: cppopt [cppno++] = argv [i]; else if (argv [i][1] == 'i') { separate: cppopt [cppno++] = argv [i++]; gccopt [gccno++] = cppopt [cppno++] = argv [i]; } else if (argv [i][1] == 'E') dontdoit = true; } if (!strcmp (argv [i], "-o")) gccopt [gccno++] = ofile = argv [++i]; else if (!strcmp (argv [i], "-c")) mkobj = 1; } if (!mkobj && ncclinker) nofileopt [nofileno++] = "-c"; nccopt [nccno] = gccopt [gccno] = cppopt [cppno] = nofileopt [nofileno + 1] = NULL; if (!ofile) ofile = mkobj ? filesno != 1 ? 0 : objfile (files [0]) : (char*) "a.out"; if (filesno > 1) { if (rungcc) RUN (NULL, gccopt); fprintf (stderr, "Multiple files. Forking\n"); if (ofile) for (i = 0; i < nofileno; i++) if (!strcmp (nofileopt [i], "-o")) nofileopt [i] = "-c"; if (rungcc) for (i = 0; i < nofileno; i++) if (!strcmp (nofileopt [i], "-ncgcc")) nofileopt [i] = "-nc-"; for (i = 0; i < filesno; i++) { nofileopt [nofileno] = files [i]; RUN (NULL, nofileopt); if (ncclinker) objfiles [objfileno++] = objfile (files [i]); } if (ncclinker) { if (ofile) openout (ofile); LINK (objfiles, objfileno); if (ofile) stripout (ofile); } exit (0); } include_values = usage_only = true; multiple = false; for (i = 0; i < nccno; i++) if (0); #define NCCOPT(x) else if (!strcmp (nccopt [i], x)) #define NCCPOPT(x) else if (!strncmp (nccopt [i], x, sizeof x - 1)) NCCPOPT("gcc=") { gccopt [0] = nccopt [i] + 4; RUN (NULL, gccopt); } NCCOPT ("-"); NCCOPT ("gcc") RUN (NULL, gccopt); NCCOPT ("cc") usage_only = false; NCCOPT ("err") halt_on_error = true; NCCOPT ("noerr")no_error = true; NCCOPT ("mv") multiple = true; NCCOPT ("pc") multiple = pseudo_code = true; NCCOPT ("oo") openout (files [0]); NCCOPT ("ld") openout (ofile); NCCOPT ("00") include_values = false; NCCOPT ("spp") spp = true; NCCOPT ("fabs") abs_paths = true; NCCOPT ("nrs") report_structs = false; NCCPOPT ("cpp=") cppopt [0] = nccopt [i] + 4; NCCPOPT ("key") keys [keyno++] = nccopt [i] + 3; NCCOPT ("quiet"); else { fputs (help, stderr); exit (strcmp (nccopt [i], "help") != 0); } if (!(sourcefile = files [0])) { if (objfileno) { LINK (objfiles, objfileno); if (ofile) stripout (ofile); } else fprintf (stderr, "ncc: No C source file\n"); exit (0); } if (dontdoit) { fprintf (stderr, "ncc: '-E'. Won't do it...\n"); exit (0); } if (spp) { preprocfile = StrDup (sourcefile); preprocfile [strlen (preprocfile) - 1] = 'i'; } if (keyno) { ncc_key = new char* [keyno + 1]; for (i = 0; i < keyno; i++) ncc_key [i] = keys [i]; ncc_key [i] = NULL; } if (usage_only) set_usage_report (); else set_compilation (); include_strings = include_values || ncc_key; if (objfileno) LINK (objfiles, objfileno); RUN (preprocfile, cppopt); } void ncc_keys () { #define KSZ (sizeof NCC_INFILE_KEY - 1) int i, j; if (ncc_key) for (i = 0; i < C_Nstrings; i++) if (!strncmp (C_Strings [i], NCC_INFILE_KEY, KSZ)) { for (j = 0; ncc_key [j]; j++) if (!strncmp (C_Strings [i] + KSZ, ncc_key [j], strlen (ncc_key [j]))) break; if (ncc_key [j]) PRINTF ("%s", C_Strings [i] + KSZ + strlen (ncc_key [j])); } } ncc-2.8/global.h0000644000175000001440000001610411074137502013665 0ustar stanusers00000000000000#include "config.h" #include "norm.h" #include "mem_pool.h" #define MSPEC 20 #define BITFIELD_Q 64 #define COLS "\033[01;37m" #define COLE "\033[0m" #define PRINTF(...) fprintf (report_stream, __VA_ARGS__) extern FILE *report_stream; // // the types we'll be using // typedef int NormPtr; typedef int RegionPtr, ObjPtr, typeID, ArglPtr, Symbol, *Vspec; typedef int exprID; // // preprocessing // extern void preproc (int, char**); extern void ncc_keys (); // // program options // extern bool usage_only, include_values, include_strings, multiple, abs_paths, report_structs, halt_on_error, pseudo_code, no_error; extern char *sourcefile, *preprocfile, *cwd; // // inform // extern char* StrDup (char*); extern void debug (const char*, NormPtr, int); extern void prcode (NormPtr, int); extern void prcode (NormPtr, int, Symbol[]); extern void prcode (NormPtr, int, Symbol); extern void printtype (int, int*); extern void printtype (typeID); extern int syntax_error (NormPtr, char* = NULL); extern int syntax_error (char*, char*); extern int syntax_error (NormPtr, char*, char*); extern void half_error (char*, char* = NULL); extern void warning (char*, char = 0); extern char *expand (int); extern int cline_of (NormPtr); extern int cfile_of (NormPtr); extern char *in_file (NormPtr); extern void report_error (); class EXPR_ERROR {public: EXPR_ERROR () {}}; // // lex & normalized C source // struct token { int at_line; unsigned int type; char *p; int len; }; extern token CTok; struct cfile_i { int indx; char *file; }; struct clines_i { int ftok, line; }; extern int* CODE; extern int C_Ntok; extern char** C_Syms; extern int C_Nsyms; extern char** C_Strings; extern int C_Nstrings; extern cfile_i* C_Files; extern int C_Nfiles; extern clines_i* C_Lines; extern int C_Nlines; extern double* C_Floats; extern signed char* C_Chars; extern short int* C_Shortints; extern long int* C_Ints; extern unsigned long* C_Unsigned; extern struct __builtins__ { int bt__builtin_alloca; int bt__builtin_return_address; int bt__FUNCTION__; int bt__func__; int bt__PRETTY_FUNCTION__; } ccbuiltins; extern void enter_token (); extern void enter_file_indicator (char*); extern void prepare (); extern void make_norm (); extern int getint (int); extern void yynorm (char*, int); extern Symbol intern_sym; // // utilities // extern void intcpycat (int*, const int*, const int*); extern int* intdup (int*); extern int intcmp (int*, int*); extern void intncpy (int*, int*, int); extern inline void intcpy (int *d, const int *s) { while ((*d++ = *s++) != -1); } extern inline int intlen (const int *i) { int l=0; while (*i++ != -1) l++; return l; } class load_file { int fd; public: load_file (char*); int success; char *data; int len; ~load_file (); }; #define ZC_OK 0 #define ZC_NA 1 #define ZC_AC 2 #define ZC_FF 3 // // the compilation // extern void parse_C (); // // CDB interface // enum VARSPC { EXTERN, STATIC, DEFAULT }; typedef bool Ok; extern typeID VoidType, SIntType; enum BASETYPE { S_CHAR = -20, U_CHAR, S_SINT, U_SINT, S_INT, U_INT, S_LINT, U_LINT, S_LONG, U_LONG, FLOAT, DOUBLE, VOID, _BTLIMIT }; #define INTEGRAL(x) (x >= S_CHAR && x <= U_LONG) #define TYPEDEF_BASE 50000 struct type { int base; Vspec spec; }; #define ISFUNCTION(t) ((t).spec [0] == '(') #define T_BASETYPE(t) ((t).base < _BTLIMIT) #define T_BASESTRUCT(t) (t > 0 && t < TYPEDEF_BASE) #define ARGLIST_OPEN -2 #define SPECIAL_ELLIPSIS -3 #define INCODE (!INGLOBAL && !INSTRUCT) extern bool INGLOBAL, INSTRUCT, infuncs; extern ArglPtr NoArgSpec; extern void init_cdb (); extern typeID gettype (type&); extern typeID gettype (int, int*); extern ArglPtr make_arglist (typeID*); extern typeID* ret_arglist (ArglPtr); extern void opentype (typeID, type&); extern int base_of (typeID); extern int* spec_of (typeID); extern int esizeof_objptr (ObjPtr); extern int sizeof_typeID (typeID); extern int sizeof_type (int, Vspec); extern int ptr_increment (int, Vspec); extern Ok introduce_obj (Symbol, typeID, VARSPC); extern Ok introduce_tdef (Symbol, typeID); extern ObjPtr lookup_typedef (Symbol); extern Ok is_typedef (Symbol); extern Ok introduce_enumconst (Symbol, int); extern Ok introduce_enumtag (Symbol, bool=false); extern Ok valid_enumtag (Symbol); extern RegionPtr introduce_anon_struct (bool); extern RegionPtr introduce_named_struct (Symbol, bool); extern RegionPtr use_struct_tag (Symbol, bool); extern RegionPtr fwd_struct_tag (Symbol, bool); extern Ok function_definition (Symbol, NormPtr, NormPtr, NormPtr, NormPtr); extern Ok function_no (int, NormPtr*, NormPtr*); extern void open_compound (); extern void close_region (); extern Symbol struct_by_name (RegionPtr); extern void functions_of_file (); extern bool have_function (Symbol); extern bool rename_struct (typeID, Symbol); extern void struct_location (typeID, NormPtr, NormPtr); extern void structs_of_file (); extern void spill_anonymous (RegionPtr); // // CDB lookups // struct lookup_object { bool enumconst; int ec; ObjPtr base; RegionPtr FRAME; int displacement; int spec [50]; lookup_object (Symbol, bool=true); }; struct lookup_function { bool fptr; ObjPtr base; RegionPtr FRAME; bool ARGFUNC; int displacement; int spec [50]; bool found; lookup_function (Symbol, bool=true); }; struct lookup_member { ObjPtr base; int spec [50]; int displacement; lookup_member (Symbol, RegionPtr); }; // // cc-expressions // enum COPS { VALUE, FVALUE, SVALUE, UVALUE, AVALUE, SYMBOL, FCALL, ARRAY, MEMB, PPPOST, MMPOST, PPPRE, MMPRE, LNEG, OCPL, PTRIND, ADDROF, UPLUS, UMINUS, CAST, SIZEOF, MUL, DIV, REM, ADD, SUB, SHL, SHR, BEQ, BNEQ, CGR, CGRE, CLE, CLEE, BAND, BOR, BXOR, IAND, IOR, GCOND, COND, // assignments taken from norm.h defines COMMA, ARGCOMMA, COMPOUND_RESULT }; struct subexpr { int action; union { int using_result; long int value; unsigned long int uvalue; double fvalue; Symbol symbol; exprID e; } voici; exprID e; union { typeID cast; Symbol member; exprID eelse; typeID result_type; } voila; }; struct exprtree { subexpr *ee; int ne; exprID first; }; extern exprtree CExpr; extern int last_result; extern subexpr *ⅇ extern int &NeTop; extern NormPtr ExpressionPtr; extern typeID typeof_expression (); extern int cc_int_expression (); extern struct lrt { int base; int spec [MSPEC]; } last_result_type; // // expand initializer // class dcle { struct { int p; bool marked; int c, max; int s; } nests [30]; int ni; Symbol *dclstr; NormPtr p; void openarray (); void openstruct (); NormPtr skipbracket (NormPtr); bool opennest (); bool closenest (); Symbol pexpr [30]; public: dcle (Symbol); bool open_bracket (); bool tofield (); bool comma (); bool tostruct (RegionPtr); bool designator (Symbol[]); bool close_bracket (); Symbol* mk_current (); void printexpr (); ~dcle (); }; // // different behaviour of the compiler // extern class ncci { public: virtual void cc_expression () = 0; virtual void new_function (Symbol) = 0; virtual void inline_assembly (NormPtr, int) = 0; virtual void finir () { } } *ncc; extern void set_compilation (); extern void set_usage_report (); ncc-2.8/config.h0000644000175000001440000000107110665114360013671 0ustar stanusers00000000000000#ifdef __GNUC__ #ifndef alloca #define alloca __builtin_alloca #endif #else #ifdef __FreeBSD__ #include #else #include #endif #endif #define NCC_VERSION "2.7" #define GNU_VIOLATIONS #define LABEL_VALUES #define OUTPUT_EXT ".nccout" #define NOGNU "/usr/include/nognu" #define NCC_INFILE_KEY "ncc-key" #define FAKE_VARIABLE_ARRAYS #define NCC_ISOC99 #define PARSE_ARRAY_INITIALIZERS // If you have linux with /dev/shm mounted // try this one -- performance boost //#define PREPROCESSOR_OUTPUT "/dev/shm/NCC.i" #define PREPROCESSOR_OUTPUT "NCC.i" ncc-2.8/main.C0000644000175000001440000001162211074111104013272 0ustar stanusers00000000000000#include #include #include #include #include #include #include #include #include #include #include "global.h" extern bool quiet; class ncci * ncc; // int* CODE; int C_Ntok; char** C_Syms; int C_Nsyms; char** C_Strings; int C_Nstrings; cfile_i* C_Files; int C_Nfiles; clines_i* C_Lines; int C_Nlines; double* C_Floats; signed char* C_Chars; short int* C_Shortints; long int* C_Ints; unsigned long* C_Unsigned; // *********** --------- *********** struct __builtins__ ccbuiltins; int syntax_error (int i, char *c) { if (c) fprintf (stderr, "ncc-error : %s \n", c); debug ("syntax error:", i - 25, 50); exit (1); } int syntax_error (char *a1, char *a2) { fprintf (stderr, "ncc-error: %s %s\n", a1, a2); exit (1); } int syntax_error (int i, char *p, char *t) { fprintf (stderr, "ncc-error %s --> [%s]\n", p, t); debug ("syntax error:", i - 25, 50); exit (1); } static int nhe = 0; void half_error (char *m1, char *m2) { #define STDDBG stderr if (m2) fprintf (STDDBG, "%s %s\n", m1, m2); else fprintf (STDDBG, "%s\n", m1); debug ("expression ncc-error:", ExpressionPtr - 15, 30); if (halt_on_error) { *(int*)0=0; } if (no_error) report_error (); else if (nhe++ > 20) syntax_error ("Maximum number of errors", "aborted"); throw EXPR_ERROR (); } void warning (char *m1, char m2) { if (m2) fprintf (stderr, "warning:%s %c\n", m1, m2); else fprintf (stderr, "warning:%s\n", m1); } void yylex_open (char *file) { fprintf (stderr, "Opening preprocessed file %s\n", file); load_file C (file); if (C.success != ZC_OK) { fprintf (stderr, "Problems opening %s\n", file); exit (1); } yynorm (C.data, C.len); } extern void showdb (); static void set_cwd () { char tmp [512]; if (!getcwd (tmp, 512)) strcpy (tmp, "/TOO_BIG_PATH"); cwd = StrDup (strcat (tmp, "/")); } int main (int argc, char **argv) { set_cwd (); // parse options and invoke preprocessor preproc (argc, argv); // initialize lexical analyser prepare (); // lexical analysis yylex_open (preprocfile); // unlink NCC.i if not -ncspp if (preprocfile == PREPROCESSOR_OUTPUT) unlink (preprocfile); // sum up into the big normalized array of tokens make_norm (); // strings are in the symbol table, so look for ncc keys ncc_keys (); // initialize syntactical analyser database init_cdb (); // syntactical analysis parse_C (); // object file useless ? if (nhe && !no_error) syntax_error ("Compilation errors", "in expressions"); // final stuff ncc->finir (); // file-as-function calling functions defined in this file if (usage_only) functions_of_file (); // report structure declaration locations if (usage_only && report_structs) structs_of_file (); // print out what we learned from all this showdb (); fprintf (stderr, quiet ? "%i Tokens, %i symbols, %i expressions\n": "%i Tokens\n%i symbols\n%i expressions\n", C_Ntok, C_Nsyms, last_result); } char *StrDup (char *c) { return strcpy (new char [strlen (c) + 1], c); } char *StrDup (char *c, int i) { char *d = new char [i + 1]; d [i] = 0; return strncpy (d, c, i); } #ifdef EFENCE // ###### Electric Fence ###### // These activate efence on our C++ allocations. // ############################################# void *operator new (size_t s) { return malloc (s); } void operator delete (void *p) { free (p); } void *operator new[] (size_t s) { return malloc (s); } void operator delete[] (void *p) { free (p); } #endif #ifdef _POSIX_MAPPED_FILES load_file::load_file (char *f) { data = NULL; success = ZC_NA; fd = -1; struct stat statbuf; if (stat (f, &statbuf) == -1) return; len = statbuf.st_size; if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return; success = ((data = (char*) mmap (0, len, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) ? ZC_OK : ZC_FF; } load_file::~load_file () { if (data && data != MAP_FAILED) munmap (data, len); if (fd != -1) close (fd); } #else load_file::load_file (char *f) { data = NULL; success = ZC_NA; fd = -1; struct stat statbuf; if (stat (f, &statbuf) == -1) return; len = statbuf.st_size; if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return; data = (char*) malloc (len); success = (len = read (fd, data, len)) != -1 ? ZC_OK : ZC_FF; } load_file::~load_file () { if (data) free (data); if (fd != -1) close (fd); } #endif //*********************************************************************** // definitions //*********************************************************************** void intcpycat (int *d, const int *s1, const int *s2) { while ((*d++ = *s1++) != -1); d -= 1; while ((*d++ = *s2++) != -1); } int *intdup (int *i) { int *r = new int [intlen (i) + 1]; intcpy (r, i); return r; } int intcmp (int *x, int *y) { while (*x == *y && *x != -1) x++, y++; return (*x < *y) ? -1 : (*x == *y) ? 0 : 1; } void intncpy (int *d, int *s, int l) { while (l--) *d++ = *s++; } ncc-2.8/nccnav/0000755000175000001440000000000011074666444013536 5ustar stanusers00000000000000ncc-2.8/nccnav/Makefile0000644000175000001440000000067510502336277015177 0ustar stanusers00000000000000 CC = g++ LCFLAGS = -Wall -g CFLAGS = $(LCFLAGS) -c -g OBJDIR = ../objdir $(OBJDIR)/nccnav: nccnav.C $(OBJDIR)/dbstree.o $(OBJDIR)/inttree.o $(CC) -g nccnav.C -I.. -lncurses -o nccnav $(OBJDIR)/dbstree.o $(OBJDIR)/inttree.o $(OBJDIR)/dbstree.o: ../dbstree.[Ch] $(CC) $(CFLAGS) ../dbstree.C -c @mv dbstree.o $(OBJDIR) $(OBJDIR)/inttree.o: ../inttree.[Ch] $(CC) $(CFLAGS) ../inttree.C -c @mv inttree.o $(OBJDIR) clean: rm -f *.o nccnav ncc-2.8/nccnav/nccnav.10000644000175000001440000001335210501513437015057 0ustar stanusers00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH NCCNAV 1 "September 10, 2006" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME nccnav, nccnavi \- explore the output of ncc on a text-mode console .SH SYNOPSIS .B nccnav .RI [ Code.map ] .br .B nccnavi .RI [ Code.map ] .SH DESCRIPTION This manual page documents briefly the .B nccnav command which can be used to browse the output generated by ncc on a set of C / C++ files. .SH COMMANDS (Mode 1) When first invoked, nccnav will display a list of source files. .TP .B Arrow keys / Page up / Page down Can be used to navigate to the desired file .TP .B Enter Selects a file and takes the user to Mode 2 .TP .B q Takes the user back to the previous screen .TP .B O Displays a list of all the functions in the selected file .TP .B a-z A-Z (except q) Moves the cursor to the function beginning with the specified character .TP .B E Displays a list of all functions not called by any other function .TP .B G Displays a list of all the global variables .SH COMMANDS (Mode 2) This mode is oriented around a resource which can be a: file, function, global variable, member of structure, structure For any of the above, all the related resources are listed. For example, in the case of a there are: 1. File(s) with definition (may not exist) .br 2. Functions calling (Blue) .br 3. Global variables used by (Red) .br 4. .br 5. Functions called by .br Dim grey: functions that do not call any other functions. .br Brown: functions that do call other functions. .br Red: Recursion detector alert (if enabled) .br 6. Structure members used by (Dark grey) .TP .B 1-6 Recursively enter MODE 2 for that resource. .TP .B q Pressing enter on 4 or typing 'q' will return to the previous screen. .TP .B BACKSPACE The INITIAL SCREENS with a longjmp. .TP .B < The HISTORY MODE. .TP .B r Pressing 'r' on a red 5 get you to the UNROLL MODE. .TP .B m Pressing 'm' on a function will get you to the POP-UP MODE .TP .B C Pressing 'C' will run system("bash") .SH INTERNAL SOURCE VIEWER Pressing on: .TP .B ... a structure: Will extract and display the structure and declaration text. .TP .B ... a function: Will display the function text as found in the file which contains the function definition. For this to work, nccnav must be in the correct root directory or paths should be absolute. The ncc option -ncfabs is rather useful. Note that if a function reports to be defined in more than one files, this will probably fail. This happens because ncc does not distinguish different static functions with the same name. They are considered the same thing and their resources are mixed in nccnav. Currently, if nccnav detects more than one files for a function it will issue a warning. .TP .B ... a file in Mode 2: Will display the contents of the entire file. By default, the internal source viewer uses .B less. When invoked via the .B nccnavi command, the source code is automatically indented and viewed through less. .SH RECURSION DETECTOR The recursion detector is enabled by default. It can be disabled by pressing 'R' while in MODE 2. (it's supposed to be expensive and may be confusing) The recursion detector works in MODE 2 and if the current resource is a function. In this case it will paint RED all the functions called by the current function, which will eventually lead back to it by some way of recursion. Pressing 'r' on one of the red ones will display one of the possible paths through which recursion can happen. Currently there is no way to view alternative paths. In this mode, 'q' will get you back while enter will proceed ahead deeper into MODE 2 for the selected resource. .SH POPUP MODE This is an alternative way to browse the call flow and is entered by pressing 'm' on a function in MODE 2. Pop-up menus are generated, where the top element is a function and below it all the functions called by it. Movement is possible with the UP/DOWN arrows. or RIGHT will expand a new pop-up for the current function. 'q' or LEFT will close the current pop-up and activate the previous one. SPACE is available to view the source code. BACKSPACE will close all the popups and return to MODE 2. Pressing '2' will enter MODE 2 for the current selected element. In this case, HISTORY and BACKSPACE are set at this breakpoint. .SH HISTORY MODE At any time you can press '<' and '>' to browse through all the previous screens. on one of them will jump back to that screen. Any other key will exit the HISTORY MODE. .SH EXAMPLE Supposing you've compiled the linux kernel with ncc. Collecting all the .nccout files can be done with : .TP .B find . -name \*.nccout | xargs cat > kernel.map .TP You can use pathremover to truncate long paths in `kernel.map'. .TP .B find . -name \*.nccout | xargs cat | pathremover /mnt/src/hacks/linux-2.4.10/ > kernel.map .TP Then, that's viewed with: .TP .B nccnav kernel.map .TP For more information on using ncc on the Linux kernel, please refer to: .TP .B /usr/share/doc/ncc/hacking.LINUX-KERNEL .SH SEE ALSO .BR ncc (1), .BR gengraph.py (1), .BR nccstrip.py (1), .BR pathremover (1), .SH AUTHOR nccnav was written by Stelios Xanthakis . ncc-2.8/nccnav/nccnav.C0000644000175000001440000010547011042062661015103 0ustar stanusers00000000000000// sxanth@ceid.upatras.gr #include #include #include #include #include #include #include #include #include #include #include #include "dbstree.h" #include "inttree.h" // If you don't want an external viewer, // comment out the definitions of SOURCE_VIEWER #define SOURCE_VIEWER_INDENT "indent -st -kr | less" #define SOURCE_VIEWER "less" #define COLOR_VIEWER "vi -R" #define OUTFILE1 "nccnav.outs" static char *svr; static char CWD [1000]; // "uti.mod" // // utilities // static char *strDup (char *s) { return strcpy (new char [strlen (s) + 1], s); } #define SHUT_UP_GCC(x) (void)x; static void progress () { static char pchars [] = "-\\|/", out [] = { '\r', '[', 0, ']' }; static int pi, ti; if (!(ti++ & 127)) { out [2] = pchars [pi = (pi + 1)%(sizeof pchars - 1)]; write (fileno (stdout), out, sizeof out); } } static void aprogress () { static int ti; char out [] = { '\r', '[', 0, ']' }; out [2] = '0' + ti++; write (fileno (stdout), out, sizeof out); } class foreach_intNode { protected: virtual void A (intNode*) = 0; void inorder (intNode*); public: }; void foreach_intNode::inorder (intNode *i) { if (i->less) inorder (i->less); A (i); if (i->more) inorder (i->more); } class load_file { int fd; public: load_file (char*); int success; char *data; int len; ~load_file (); }; #define ZC_OK 0 #define ZC_NA 1 #define ZC_AC 2 #define ZC_FF 3 #ifdef _POSIX_MAPPED_FILES load_file::load_file (char *f) { data = NULL; success = ZC_NA; fd = -1; struct stat statbuf; if (stat (f, &statbuf) == -1) return; len = statbuf.st_size; if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return; success = ((data = (char*) mmap (0, len, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) ? ZC_OK : ZC_FF; } load_file::~load_file () { if (data && data != MAP_FAILED) munmap (data, len); if (fd != -1) close (fd); } #else load_file::load_file (char *f) { data = NULL; success = ZC_NA; fd = -1; struct stat statbuf; if (stat (f, &statbuf) == -1) return; len = statbuf.st_size; if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return; data = (char*) malloc (len); success = read (fd, data, len) == len ? ZC_OK : ZC_FF; } load_file::~load_file () { if (data) free (data); if (fd != -1) close (fd); } #endif // "st1.mod" // // Symbol tree // class symNode : public dbsNodeStr { public: symNode *less, *more; unsigned int i; symNode (); ~symNode (); }; static dbsTree symTree; static unsigned int nsym = 1; symNode::symNode () : dbsNodeStr () { symTree.addself (this); i = nsym++; } symNode::~symNode () { if (less) delete less; if (more) delete more; } unsigned int enter_symbol (char *sy) { DBS_STRQUERY = sy; symNode *s = (symNode*) symTree.dbsFind (); if (!s) { DBS_STRQUERY = strDup (DBS_STRQUERY); s = new symNode; progress (); } return s->i; } // // Hyper nodes // class hyperNode : public intNode { public: void settle (); intTree itree; unsigned int *rel; hyperNode (); void relates (unsigned int); void rsettle (); }; static intTree hyperTree; hyperNode::hyperNode () : intNode (&hyperTree) { } #define SOURCE_ID 0 #define STRUCT_ID 1 #define CALLER_ID 2 #define GLOBAL_ID 3 #define CALL_ID 4 #define MEMBER_ID 5 #define NFIELDS 6 #define RECURS_BOOST 7 /* color */ #define BASE (1000*1000) #define SOURCE_BASE (SOURCE_ID * BASE) #define CALLER_BASE (CALLER_ID * BASE) #define GLOBAL_BASE (GLOBAL_ID * BASE) #define CALL_BASE (CALL_ID * BASE) #define STRUCT_BASE (STRUCT_ID * BASE) #define MEMBER_BASE (MEMBER_ID * BASE) #define FIELD_BASE (NFIELDS * BASE) #define RECURS_BASE (RECURS_BOOST * BASE) static unsigned int symbol_of (unsigned int i) { return i % BASE; } static unsigned int base_of (unsigned int i) { return i / BASE; } unsigned int counts [NFIELDS], dupes; void hyperNode::relates (unsigned int y) { if (!itree.intFind (y)) new intNode (&itree); else if (base_of (y) != STRUCT_ID && base_of (Key) != STRUCT_ID) dupes++; } static void relate (int x, int y) { hyperNode *h = (hyperNode*) hyperTree.intFind (x); if (!h) { h = new hyperNode; counts [base_of (x)]++; } h->relates (y); } static int nlinks = 0; static void relative (int x, int y) { ++nlinks; relate (base_of (x) == CALLER_ID ? x - CALLER_BASE + CALL_BASE : x, y); relate (base_of (y) == CALLER_ID ? y - CALLER_BASE + CALL_BASE : y, x); } // "repl.mod" // // macro replacements // static struct repl_s { unsigned int r, f; } * replarr; static unsigned int nreplarr, areplarr; static void stor_replacement (unsigned int r, unsigned int f) { if (nreplarr == areplarr) replarr = (repl_s*) realloc (replarr, sizeof (repl_s) * (areplarr += 64)); replarr [nreplarr].r = r; replarr [nreplarr++].f = f; } static void repl_line (char *l) { char *d = strchr (l, ' '); if (d) { *d = 0; stor_replacement (enter_symbol (l) + CALL_BASE, enter_symbol (d + 1) + CALL_BASE); } } void do_replacements () { unsigned int i, j, k, nu = 0, *un = (unsigned int*) alloca (sizeof * un * nreplarr); hyperNode **hns = (hyperNode**) alloca (sizeof * hns * nreplarr); for (i = 0; i < nreplarr; i++) { for (j = 0; j < nu; j++) if (un [j] == replarr [i].r) break; if (j < nu) continue; un [nu++] = replarr [i].r; hyperNode *h = hns [j] = (hyperNode*) hyperTree.intFind (replarr [i].r); if (!h) continue; h->settle (); for (j = 0; j < (unsigned int) h->itree.cnt; j++) { hyperNode *h2 = (hyperNode*) hyperTree.intFind (h->rel [j] - CALLER_BASE + CALL_BASE); intNode *n = h2->itree.intFind (replarr [i].r); if (n) { n->intRemove (&h2->itree); delete n; } } h->intRemove (&hyperTree); } for (i = 0; i < nreplarr; i++) { for (j = 0; j < nu; j++) if (un [j] == replarr [i].f) break; if (j < nu) { unsigned int l, m; k = replarr [i].f; for (j = 0; un [j] != replarr [i].r; j++); for (l = 0; l < nreplarr; l++) if (replarr [l].r == k) { for (m = 0; m < nu; m++) if (un [m] == replarr [l].f) break; if (m < nu) continue; if (hns [j]) for (m = 0; (int) m < hns [j]->itree.cnt; m++) relative (hns [j]->rel [m], replarr [l].f); } continue; } for (j = 0; un [j] != replarr [i].r; j++); if (hns [j]) for (k = 0; (int) k < hns [j]->itree.cnt; k++) relative (hns [j]->rel [k], replarr [i].f); } } // "ln.mod" // // Line numbers // struct file_chunk { unsigned int start, end; } NoChunk = { ~0, ~0 }; class lnnode : public intNode { public: file_chunk line; lnnode (unsigned int, unsigned int); }; static intTree lnTree; lnnode::lnnode (unsigned int l, unsigned int e) : intNode (&lnTree) { line.start = l; line.end = e; } static void enter_line (unsigned int f, unsigned int l, unsigned int e) { if (!lnTree.intFind (f)) new lnnode (l, e); } file_chunk line_of (unsigned int f) { lnnode *l = (lnnode*) lnTree.intFind (f); return (l) ? l->line : NoChunk; } // "agr.mod" // // Make structure -> members link // void struct_fp_link (char *s) { char *dot, ss [100]; if (!(dot = strchr (s, '.'))) return; *dot = 0; strcpy (ss, s+1); *dot = '.'; relative (CALL_BASE + enter_symbol (s), STRUCT_BASE + enter_symbol (ss)); } void struct_link (char *s) { char *dot, ss [100]; if (!(dot = strchr (s, '.'))) return; *dot = 0; strcpy (ss, s); *dot = '.'; relative (MEMBER_BASE + enter_symbol (s), STRUCT_BASE + enter_symbol (ss)); } // // Structure line numbers // void struct_loc (char *s) { char *p; if (!(p = strchr (s, ' '))) return; *p++ = 0; enter_line (enter_symbol (s) + STRUCT_BASE, atoi (p), atoi (strchr (p, ' '))); } // // Symbol Table // static struct st_entry { char *name; int weight; } *symbol_table; static void totable (symNode *s) { static int order = 0; symbol_table [s->i].name = s->Name; symbol_table [s->i].weight = order++; } void make_symbol_table () { symbol_table = new st_entry [nsym]; symTree.foreach (totable); delete symTree.root; } static int weight (unsigned int i) { return symbol_table [symbol_of (i)].weight; } static char *symbol_name (unsigned int i) { return symbol_table [symbol_of (i)].name; } // "hns.mod" // // Settle hypernodes // static unsigned int *tmp_table; class enter_intnodes : public foreach_intNode { void A (intNode *i) { *tmp_table++ = i->Key; } public: enter_intnodes (intTree *I) { if (I->root) inorder (I->root); } }; void hyperNode::settle () { if (!itree.root) return; tmp_table = rel = new unsigned int [itree.cnt]; enter_intnodes E (&itree); SHUT_UP_GCC (E) delete itree.root; } class hyper_settle : public foreach_intNode { protected: void A (intNode *i) { ((hyperNode*)i)->settle (); } public: hyper_settle () { inorder (hyperTree.root); } }; void settle_hyperNodes () { hyper_settle H; SHUT_UP_GCC (H) } // // Sort symbol indexes // static void swap (unsigned int v[], int i, int j) { unsigned int swp = v [i]; v [i] = v [j]; v [j] = swp; } void qsort (unsigned int v[], int left, int right) { int i, last, wleft; if (left >= right) return; swap (v, left, (left+right)/2); wleft = weight (v [left]); last = left; for (i = left+1; i <= right; i++) if (weight (v [i]) < wleft) swap (v, i, ++last); swap (v, last, left); qsort (v, left, last-1); qsort (v, last+1, right); } void sort_fields (unsigned int v [], int n) { int i, j, k; struct { unsigned int *v; int n; } field [NFIELDS]; for (i = 0; i < NFIELDS; i++) { field [i].v = (unsigned int*) alloca (n * sizeof (int)); field [i].n = 0; } for (i = 0; i < n; i++) field [base_of (v [i])].v [field [base_of (v [i])].n++] = v [i]; for (j = i = 0; i < NFIELDS; i++) { if (field [i].n > 1) qsort (field [i].v, 0, field [i].n - 1); for (k = 0; k < field [i].n; k++) v [j++] = field [i].v [k]; } } // "ia.mod" // // Staring tables // class hyper_gets : public foreach_intNode { void A (intNode*); public: unsigned int nfiles, nfuncs; hyper_gets (); }; unsigned int *Files, *funcs, *entries, nentries, *globals, nglobals; static bool has_callers (hyperNode *h) { int i; for (i = 0; i < h->itree.cnt; i++) if (base_of (h->rel [i]) == CALLER_ID // recursive callers of self, do not count && symbol_of (h->rel [i]) != symbol_of (h->Key)) return true; return false; } void hyper_gets::A (intNode *i) { hyperNode *h = (hyperNode*) i; switch (base_of (h->Key)) { case SOURCE_ID: Files [nfiles++] = h->Key; break; case GLOBAL_ID: globals [nglobals++] = h->Key; break; case CALL_ID: funcs [nfuncs++] = h->Key; if (!has_callers (h)) nentries++; break; } } hyper_gets::hyper_gets () { nglobals = nfiles = nfuncs = nentries = 0; inorder (hyperTree.root); counts [CALL_ID] = nfuncs; } void starting_tables () { unsigned int i, j; Files = new unsigned int [counts [SOURCE_ID]]; funcs = new unsigned int [counts [CALL_ID]]; globals = new unsigned int [counts [GLOBAL_ID]]; hyper_gets H; SHUT_UP_GCC (H); entries = new unsigned int [nentries]; for (i = j = 0; i < counts [CALL_ID]; i++) if (!has_callers ((hyperNode*) hyperTree.intFind (funcs [i]))) entries [j++] = funcs [i]; qsort (Files, 0, counts [SOURCE_ID] - 1); qsort (funcs, 0, counts [CALL_ID] - 1); qsort (globals, 0, counts [GLOBAL_ID] - 1); qsort (entries, 0, nentries - 1); } // "pf.mod" // // Parse File // void linenumber (char *s) { char *f; if (!(f = strchr (s, ' '))) return; *f++ = 0; enter_line (enter_symbol (s) + CALL_BASE, atoi (f), atoi (strchr (f, ' '))); } #define FMT_CALLER 'D' #define FMT_CALL 'F' #define FMT_SOURCE 'P' #define FMT_GLOBAL 'g' #define FMT_GLOBALw 'G' #define FMT_STRUCT 's' #define FMT_STRUCTw 'S' #define FMT_LINE 'L' #define FMT_SLINE 'Y' #define FMT_REPL 'R' static bool wrt; unsigned int parse_line (char *l) { int base; char tmp [1000]; wrt = false; switch (l [0]) { case FMT_LINE: base = CALL_BASE; linenumber (l+3); break; case FMT_CALLER: base = CALLER_BASE; break; case FMT_CALL: base = CALL_BASE; if (l[3] == '*') struct_fp_link (l+3); break; case FMT_SOURCE: base = SOURCE_BASE; if (!strncmp (CWD, l + 3, strlen (CWD))) strcpy (l + 3, strcpy (tmp, l + 3 + strlen (CWD))); break; case FMT_GLOBALw: wrt = true; case FMT_GLOBAL: base = GLOBAL_BASE; break; case FMT_STRUCTw: wrt = true; case FMT_STRUCT: base = MEMBER_BASE; struct_link (l+3); break; case FMT_SLINE: base = STRUCT_BASE; struct_loc (l + 3); break; case FMT_REPL: repl_line (l + 3); return ~0; default: return ~0; } return base + enter_symbol (l + 3); } #define MLINE 512 void parse_file (FILE *f) { static int dfunc = 0; int h; char line [MLINE]; while (fgets (line, MLINE, f)) { line [strlen (line) - 1] = 0; if ((h = parse_line (line)) == ~0) continue; if (base_of (h) == CALLER_ID || base_of (h) == SOURCE_ID) dfunc = h; else relative (wrt ? dfunc - CALLER_BASE + CALL_BASE : dfunc, h); } } void read_file (char *p) { FILE *f = fopen (p, "r"); if (!f) { printf ("No such file [%s]\n", p); exit (1); } parse_file (f); fclose (f); if (!hyperTree.root) { printf ("File [%s] empty\n", p); exit (0); } aprogress (); make_symbol_table (); aprogress (); do_replacements (); aprogress (); settle_hyperNodes (); aprogress (); starting_tables (); } // // Get Relations of Link // struct linker { unsigned int tn, *t; linker (unsigned int, bool = true); }; linker::linker (unsigned int i, bool dosort) { hyperNode *h = (hyperNode*) hyperTree.intFind (i); if (dosort) sort_fields (t = h->rel, tn = h->itree.cnt); else t = h->rel, tn = h->itree.cnt; } // "recur.mod" // // Recursion detection // class recursion_detector { unsigned int caller; bool check_in (unsigned int); public: recursion_detector (unsigned int, unsigned int); unsigned int *visited; bool indeed; ~recursion_detector (); }; bool recursion_detector::check_in (unsigned int c) { hyperNode *h = (hyperNode*) hyperTree.intFind (c); unsigned int cs = symbol_of (c); unsigned int *t = h->rel; unsigned int tn = h->itree.cnt; unsigned int i; for (i = 0; i < tn; i++) if (base_of (t [i]) == CALL_ID) { unsigned int s = symbol_of (t [i]); if (s == caller) { visited [caller] = cs; return true; } else if (!visited [s]) { visited [s] = cs; if (check_in (t [i])) return true; } } return false; } recursion_detector::recursion_detector (unsigned int ca, unsigned ch) { visited = (unsigned int*) calloc (nsym, sizeof (int)); caller = symbol_of (ca); visited [symbol_of (ch)] = caller; indeed = check_in (ch); } recursion_detector::~recursion_detector () { free (visited); } bool is_recursive (unsigned int caller, unsigned int child) { recursion_detector RD (caller, child); return RD.indeed; } // // make recursion path // struct recursion_path { unsigned int *path; unsigned int n; recursion_path (unsigned int, unsigned int); ~recursion_path (); }; recursion_path::recursion_path (unsigned int caller, unsigned int child) { recursion_detector RD (caller, child); if (RD.indeed) { unsigned int cs = symbol_of (caller); unsigned int i, cnt; for (cnt = 2, i = RD.visited [cs]; i != cs; i = RD.visited [i]) ++cnt; n = cnt; path = (unsigned int*) malloc (sizeof (int) * cnt); path [n-1] = cs + CALL_BASE; for (cnt = 1, i = RD.visited [cs]; i != cs; i = RD.visited [i]) path [n-1-cnt++] = i + CALL_BASE; path [n-1-cnt++] = i + CALLER_BASE; n = cnt; } else path = 0; } recursion_path::~recursion_path () { if (path) free (path); } // "grf.mod" // // Graphics // unsigned int scr_x, scr_y; enum APP_COLOR { BLACK, HIGHLIGHT, C_UP1, C_UP2, C_UP3, NORMAL, C_DOWN1, C_DOWN2, C_DOWN3, C_DOWN4, OTHER }; enum ecol { WHITE, BLUE, RED, GREEN, YELLOW, CYAN, MAGENTA }; int Gcolor (APP_COLOR a) { switch (a) { case HIGHLIGHT: return COLOR_PAIR(WHITE)|A_BOLD; case C_UP1: return COLOR_PAIR (BLUE); case C_UP2: return COLOR_PAIR (BLUE)|A_BOLD; case C_UP3: return COLOR_PAIR (RED); case NORMAL: return COLOR_PAIR(WHITE); case C_DOWN1: return COLOR_PAIR(YELLOW)|A_DIM; case C_DOWN2: return COLOR_PAIR(WHITE)|A_DIM; case C_DOWN3: return COLOR_PAIR (CYAN); case C_DOWN4: return COLOR_PAIR(WHITE)|A_DIM; default: case OTHER: return COLOR_PAIR(MAGENTA); } } void printstr (int x, int y, char *s, APP_COLOR a) { attrset (Gcolor (a)); move (y, x); /* unfortunatelly, there's a bug in ncurses and addnstr (s, -1); counts tabs as 1 character with the result that long lines go past the end of the scr_x and appear on the next line */ unsigned int i; char *p; for (i = x, p = s; i < scr_x && *p; p++) if (*p == '\t') i += 8; else i++; if (!*p) printw ("%s", s); else { char tmp [256]; strncpy (tmp, s, p - s); tmp [p - s] = 0; printw ("%s", tmp); } } void cls () { clear (); } int inkey () { return getch (); } // Init modes void init_ncurses () { initscr (); //leaveok (stdscr, true); start_color (); init_pair (BLUE, COLOR_BLUE, COLOR_BLACK); init_pair (RED, COLOR_RED, COLOR_BLACK); init_pair (GREEN, COLOR_GREEN, COLOR_BLACK); init_pair (YELLOW, COLOR_YELLOW, COLOR_BLACK); init_pair (CYAN, COLOR_CYAN, COLOR_BLACK); init_pair (MAGENTA, COLOR_MAGENTA, COLOR_BLACK); crmode (); keypad (stdscr, TRUE); noecho (); scr_x = COLS; scr_y = LINES-1; } void init_graphics () { init_ncurses (); } void end_graphics () { flash (); endwin (); } // // Text entry display // static bool has_calls (unsigned int i) { hyperNode *h = (hyperNode*) hyperTree.intFind (i); unsigned int *a = h->rel, j = h->itree.cnt; for (i = 0; i < j; i++) if (base_of (a [i]) == CALL_ID) return true; return false; } static bool has_file (unsigned int i) { hyperNode *h = (hyperNode*) hyperTree.intFind (i); unsigned int *a = h->rel, j = h->itree.cnt; for (i = 0; i < j; i++) if (base_of (a [i]) == SOURCE_ID) return true; return false; } APP_COLOR colors (unsigned int v) { switch (base_of (v)) { case SOURCE_ID: return C_UP1; case CALLER_ID: return C_UP2; case MEMBER_ID: return C_DOWN4; case GLOBAL_ID: return C_UP3; case NFIELDS: return OTHER; case CALL_ID: return !has_file (v) ? C_DOWN3 : has_calls (v) ? C_DOWN1 : C_DOWN2; case RECURS_BOOST: return C_UP3; } return OTHER; } void printent (int x, int y, unsigned int e, unsigned int m, bool hi) { char trunc [180], *dot; char *s = symbol_name (e); if (strlen (s) > m) if (strchr (s, '/')) s = strcpy (trunc, strrchr (s, '/')); else s = strcpy (trunc, s + strlen (s) - m); printstr (x, y, s, hi ? HIGHLIGHT : colors (e)); if ((dot = strchr (s, '.'))) printstr (x + (dot - s), y, ".", NORMAL); } // "vscr.mod" // // a screenful // class nvscr { public: static int jump_node; int ii; nvscr *next, *prev; virtual void vdraw () = 0; } *nvtop; int nvscr::jump_node; // "tdm.mod" // // Map with ncurses // struct coords { unsigned int x, y; }; class TDmap : public nvscr { unsigned int epl, eps, e_spc; unsigned int *vmap, e_tot, e_cur, e_top, e_topp; coords locate (unsigned int); void add_to_list (); public: TDmap (unsigned int*, unsigned int, unsigned int, unsigned int=0); void move_arrow (int); void move_jump (unsigned int); void vdraw () { draw_map (); } void draw_map (); unsigned int enter (); unsigned int cursor (); virtual ~TDmap (); }; void TDmap::add_to_list () { if ((prev = nvtop)) { prev->next = this; ii = prev->ii + 1; } else ii = 0; next = 0; nvtop = this; jump_node = ii; } TDmap::~TDmap () { if ((nvtop = prev)) nvtop->next = 0; } unsigned int TDmap::enter () { if (base_of (vmap [e_cur]) != RECURS_BOOST) return vmap [e_cur]; return symbol_of (vmap [e_cur]) + CALL_BASE; } unsigned int TDmap::cursor () { return e_cur; } void TDmap::move_arrow (int a) { unsigned int dir = 0, mov; switch (a) { case KEY_LEFT: mov = 1, dir = 1; break; case KEY_RIGHT: mov = 1; break; case KEY_UP: mov = epl, dir = 1; break; case KEY_DOWN: mov = epl; break; case KEY_PPAGE: mov = eps, dir = 1; break; case KEY_NPAGE: mov = eps; break; default: return; } if (!dir) { if (e_cur+mov < e_tot) e_cur += mov; else if (mov == eps) while (e_cur + epl < e_tot) e_cur += epl; if (e_cur >= e_top + eps) e_top += eps; } else { e_cur = (e_cur >= mov) ? e_cur - mov : e_cur % epl; if (e_cur < e_top) e_top = (e_top > eps) ? e_top - eps : 0; } } void TDmap::move_jump (unsigned int i) { e_cur = i; e_top = (i/eps) * eps; } coords TDmap::locate (unsigned int i) { coords r; r.x = e_spc * (i % epl); r.y = (i - e_top) / epl; return r; } void TDmap::draw_map () { unsigned int i; coords xy; if (e_topp != e_top) { e_topp = e_top; cls (); } for (i = e_top; i < e_top+eps && i < e_tot; i++) { xy = locate (i); printent (xy.x, xy.y, vmap [i], e_spc-1, false); } xy = locate (e_cur); printent (xy.x, xy.y, vmap [e_cur], e_spc-1, true); } unsigned int fieldlen (unsigned int *v, unsigned int n) { unsigned int i, ml; for (ml = i = 0; i < n; i++) if (strlen (symbol_name (v [i])) > ml) ml = strlen (symbol_name (v [i])); return (ml > scr_x/2) ? scr_x/2 : (ml < 15) ? 15 : ml+1; } TDmap::TDmap (unsigned int *v, unsigned int n, unsigned int c, unsigned int ml) { if (!ml) ml = fieldlen (v, n); e_spc = scr_x/(scr_x/ml); vmap = v; e_topp = e_tot = n; epl = scr_x / e_spc; eps = scr_y * epl; move_jump (e_cur = c); add_to_list (); } // "tv.mod" // // text Viewer // #ifndef SOURCE_VIEWER class txtviewer { char **line; unsigned int nlines, maxline, topline, tab; void draw (); void scrolll (int); void main (); public: txtviewer (char*, char*, unsigned int); }; void txtviewer::draw () { unsigned int i; cls (); for (i = topline; i < nlines && i < topline+scr_y; i++) { if (strlen (line [i]) <= tab) continue; printstr (0, i-topline, line [i]+tab, NORMAL); } } void txtviewer::scrolll (int key) { unsigned int tlp = topline, tbp = tab; switch (key) { case KEY_UP: if (topline > 0) topline--; break; case KEY_DOWN: if (topline + scr_y < nlines) topline++; break; case KEY_LEFT: if (tab) tab--; break; case KEY_RIGHT: if (maxline - tab > scr_x/2) tab++; break; case KEY_PPAGE: if (topline) topline = (topline > scr_y) ? topline - scr_y : 0; break; case KEY_NPAGE: if (topline + scr_y + 1 < nlines) topline += scr_y; break; default: return; } if (tlp != topline || tbp != tab) draw (); } void txtviewer::main () { int key; while ((key = inkey ()) != '\n' && key != ' ') scrolll (key); } txtviewer::txtviewer (char *title, char *txt, unsigned int l) { nlines = l+1; line = (char**) alloca (nlines * sizeof (char*)); maxline = l = 0; line [l++] = title; do { line [l] = txt; if ((txt = strchr (txt, '\n'))) *txt++ = 0; if (strlen (line [l]) > maxline) maxline = strlen (line [l]); } while (++l < nlines); topline = tab = 0; draw (); main (); } #else /* SOURCE_VIEWER exists */ void source_viewer_fatal () { endwin (); printf ("ALERT! Failed executing [%s] to display the text.\n" " Please recompile nccnav with a different SOURCE_VIEWER value.\n", svr); exit (1); } void txtviewer (char *title, char *txt, unsigned int l, bool colored) { def_prog_mode(); /* Save the tty modes */ endwin(); /* End curses mode temporarily */ if (colored) { unsigned int h = 0, *ip = (unsigned int*) title; char *tmpc, cmd [100]; char tmpname [100]; int i; for (i = 0; i < strlen (title) / 4; i++) h ^= *ip++; sprintf (tmpname, "/tmp/nccnav-tmp%x.c", h); FILE *f = fopen (tmpc = tmpname, "w"); fputs (title, f); fputc ('\n', f); fputs (txt, f); fclose (f); sprintf (cmd, COLOR_VIEWER" %s", tmpc); system (cmd); unlink (tmpc); } else { FILE *p = popen (svr, "w"); if (!p) source_viewer_fatal (); fputs (title, p); fputc ('\n', p); fputs (txt, p); fflush (p); pclose (p); } reset_prog_mode(); /* Return to the previous tty mode*/ /* stored by def_prog_mode() */ refresh(); /* Do refresh() to restore the */ /* Screen contents */ } #endif // "vf.mod" // // Extract text from file // char *text_from (char *f, int l1, int l2) { load_file L (f); if (L.success != ZC_OK) return NULL; int i, cl; char *r; for (i = 0, cl = 1; i <= L.len && cl < l1; i++) if (L.data [i] == '\n') cl++; if (cl < l1) return NULL; l1 = i; for (;i < L.len && cl <= l2; i++) if (L.data [i] == '\n') cl++; if (cl < l2) return NULL; l2 = i; for (i = l1; i && !isspace (L.data [i]); --i); l1 = 0; for (; i; i--) if (L.data [i] == '/' && L.data [i - 1] == '*') for (i -= 2; i && (L.data [i] != '/' || L.data [i + 1] != '*'); --i); else if (isspace (L.data [i])) continue; else { cl = i; while (i && L.data [i] != '\n') --i; for (i += !!i; isspace (L.data [i]); ++i); if (L.data [i] != '/' || L.data [i + 1] != '/') { for (l1 = cl + 1; isspace (L.data [l1]); l1++); break; } } r = new char [1 + l2 - l1]; memcpy (r, L.data + l1, l2 - l1); r [l2 - l1] = 0; return r; } void func_text (unsigned int f, bool color=false) { char *txt, *title, *fn; file_chunk F = line_of (f); if (F.start == ~0U) return; linker L (f); fn = symbol_name (L.t [0]); txt = text_from (fn, F.start, F.end); if (txt) { title = (char*) alloca (strlen (fn) + 20); sprintf (title, "# %s %u", fn, F.start); txtviewer (title, txt, F.end - F.start + 1, color); delete [] txt; } } // "file" // // view entire file // void file_text (unsigned int i, bool color) { #ifdef SOURCE_VIEWER def_prog_mode(); /* Save the tty modes */ endwin(); /* End curses mode temporarily */ if (color) { char tmp [1024]; strcat (strcpy (tmp, COLOR_VIEWER" "), symbol_name (i)); system (tmp); } else { load_file L (symbol_name (i)); if (L.success != ZC_OK) return; FILE *p = popen (svr, "w"); if (!p) source_viewer_fatal (); fwrite (L.data, 1, L.len, p); fflush (p); pclose (p); } reset_prog_mode(); /* Return to the previous tty mode*/ /* stored by def_prog_mode() */ refresh(); /* Do refresh() to restore the */ /* Screen contents */ #endif } // "pd.mod" // // pop ups // void pastmode (); void enter_othermode (unsigned int); class FOO {}; class popup : public nvscr { popup *outer; unsigned int *ent, nent; WINDOW *w; unsigned int top, cur, mx, my, dh, dw; void add_to_list (); void prefresh (); void draw (); void main (); public: popup (popup*, unsigned int, unsigned int, unsigned int); void vdraw (); virtual ~popup (); }; void popup::add_to_list () { next = 0; prev = (outer) ? outer->prev : nvtop; prev->next = nvtop = this; jump_node = ii = prev->ii + 1; } void popup::prefresh () { if (outer) outer->prefresh (); else { touchwin (stdscr); wnoutrefresh (stdscr); } touchwin (w); wnoutrefresh (w); } #define CLEARBOX \ wclear (w);\ box (w, 0, 0); void popup::draw () { unsigned int i; for (i = 0; i < dh; i++) { wattrset (w, Gcolor (i ? colors (ent [top + i]) : C_UP3)); mvwprintw (w, i, 1, "%s", symbol_name (ent [top + i])); } wmove (w, cur - top, 1); wattrset (w, Gcolor (HIGHLIGHT)); wprintw (w, "%s", symbol_name (ent [cur])); touchwin (w); wrefresh (w); } void popup::vdraw () { prefresh (); doupdate (); } void popup::main () { draw (); prefresh (); for (; ii <= jump_node; draw()) switch (inkey ()) { case KEY_DOWN: if (cur < nent - 1) if (++cur == top + dh) { top++; CLEARBOX; } break; case KEY_UP: if (cur > 0) if (--cur < top) { top--; CLEARBOX } break; case KEY_PPAGE: cur = top = 0; CLEARBOX break; case KEY_NPAGE: if (cur < nent - 1) { if ((cur += 6) >= nent) cur = nent - 1; if (cur >= top + dh) { top = cur - dh; CLEARBOX } } break; case KEY_BACKSPACE: throw FOO (); case KEY_LEFT: case 'q': return; case KEY_RIGHT: if (cur == 0) break; case '\n': if (!cur) return; { popup (this, ent [cur], mx + dw - 4, my + cur - top); } vdraw (); break; case 'v': func_text (ent [cur], true); break; case ' ': func_text (ent [cur]); cls (); vdraw (); break; case '2': enter_othermode (ent [cur]); cls (); vdraw (); break; case '<': pastmode (); cls (); vdraw (); } } popup::popup (popup *o, unsigned int f, unsigned int x, unsigned int y) { linker L (f); unsigned int i, j; for (i = j = 0; i < L.tn; i++) if (base_of (L.t [i]) == CALL_ID) j++; ent = 0; if (!j) return; ent = (unsigned int*) malloc (sizeof (unsigned int) * (nent = j + 1)); ent [0] = f; for (i = 0, j = 1; i < L.tn; i++) if (base_of (L.t [i]) == CALL_ID) ent [j++] = L.t [i]; if (nent >= scr_y) { dh = scr_y - 1; my = 0; } else { dh = nent; my = (y + dh >= scr_y) ? 0 : y; } for (i = j = 0; i < nent; i++) if ((f = strlen (symbol_name (ent [i]))) > j) j = f; dw = j + 2; mx = (x + dw >= scr_x) ? 0 : x; top = cur = 0; outer = o; w = newwin (dh + 1, dw, my, mx); CLEARBOX; add_to_list (); main (); } popup::~popup () { if (ent) { (nvtop = prev)->next = 0; free (ent); delwin (w); } } // "mod.mod" // // modes // void pastmode () { char m [20]; nvscr *c = nvtop->prev; for (;;) { cls (); sprintf (m, "%i/%i", c->ii, nvtop->ii); printstr (0, scr_y, m, HIGHLIGHT); c->vdraw (); switch (inkey ()) { case '<': if (c->prev) c = c->prev; break; case '>': if (c->next) c = c->next; break; case '\n': c->jump_node = c->ii; return; default: nvtop->vdraw (); return; } } } void othermode (unsigned int); void recursmode (recursion_path &RP) { int i; TDmap M (RP.path, RP.n, 0, scr_x); M.draw_map (); for (; M.ii <= M.jump_node; M.draw_map ()) { printstr (0, scr_y, "Recursion unroll", C_UP2); if ((i = inkey ()) == 'q') break; if (i == '\n') { if (M.cursor () == 0) break; othermode (M.enter ()); cls (); } else M.move_arrow (i); } } void color_recursives (unsigned int caller, unsigned int *pa, int npa) { int i; for (i = 0; i < npa; i++) if (base_of (pa [i]) == CALL_ID && is_recursive (caller, pa [i])) pa [i] = symbol_of (pa [i]) + RECURS_BASE; } void othermode (unsigned int e) { static bool rdetect = true; int key; unsigned int i, j, c, *jarr; e = base_of (e) == CALLER_ID ? symbol_of (e) + CALL_BASE : e; unsigned int tbase = base_of (e); linker L (e); jarr = (unsigned int*) alloca ((L.tn + 1) * sizeof(int)); i = j = 0; while (i < L.tn && base_of (L.t [i]) < CALL_ID) jarr [j++] = L.t [i++]; jarr [c = j++] = e = symbol_of (e) + FIELD_BASE; while (i < L.tn) jarr [j++] = L.t [i++]; if (rdetect && tbase == CALL_ID) color_recursives (e, jarr + c + 1, i - c); TDmap M (jarr, j, c); M.draw_map (); for (;M.ii <= M.jump_node; M.draw_map ()) { if (tbase == CALL_ID && c >= 2 && base_of (jarr [1]) == SOURCE_ID) { printstr (0, scr_y, "* FUNCTION IS :MULTIPLE DEFINITIONS *", HIGHLIGHT); printstr (13, scr_y, " br0ken", C_UP2); } switch (key = inkey ()) { case ' ': case 'v': j = base_of (M.enter ()); if (j == CALL_ID) func_text (M.enter (), key == 'v'); else if (j == CALLER_ID) func_text (symbol_of (M.enter ()) + CALL_BASE, key == 'v'); else if (j == NFIELDS && tbase == CALL_ID) func_text (symbol_of (M.enter ()) + CALL_BASE, key == 'v'); else if ((j == SOURCE_ID||(j == NFIELDS&&tbase == SOURCE_ID))) file_text (symbol_of (M.enter ()), key == 'v'); else if (j == STRUCT_ID || (j == NFIELDS && tbase == STRUCT_ID)) func_text (symbol_of (M.enter ()) + STRUCT_BASE, key == 'v'); else break; if (key != '1') cls (); break; case '\n': if (M.enter () == e) return; othermode (M.enter ()); cls (); break; case KEY_BACKSPACE: cls (); throw FOO(); case 'q': cls (); return; case 'r': if (base_of (M.enter ()) == CALL_ID) { recursion_path RP (e, M.enter ()); if (RP.path) recursmode (RP); } cls (); break; case 'R': if ((rdetect =! rdetect)) { printstr (0, scr_y, "Recursion Detector ENABLED.", OTHER); color_recursives (e, jarr + c + 1, i - c); } else { printstr (0, scr_y, "Recursion Detector DISABLED", C_UP3); for (j = c; j < i+1; j++) if (base_of (jarr [j]) == RECURS_BOOST) jarr [j] -= RECURS_BASE - CALL_BASE; } break; case 'm': j = base_of (M.enter ()); if (j == CALL_ID || j == CALLER_ID || (j == NFIELDS && tbase == CALL_ID)) try { j = j == CALL_ID ? M.enter () : symbol_of (M.enter ()) + CALL_BASE; cls (); popup P (0, j, 0, 0); } catch (FOO) { } cls (); break; case '<': pastmode (); cls (); break; case 'C': // end_graphics (); def_prog_mode(); /* Save the tty modes */ endwin(); /* End curses mode temporarily */ system ("bash"); reset_prog_mode(); /* Return to the previous tty mode*/ /* stored by def_prog_mode() */ refresh(); /* Do refresh() to restore the */ /* Screen contents */ // init_graphics (); break; default: M.move_arrow (key); } } } void enter_othermode (unsigned int e) { try { othermode (e); } catch (FOO) { } } void globmode () { unsigned int *t, tn; t = globals, tn = counts [GLOBAL_ID]; int key; TDmap M (t, tn, 0); M.draw_map (); for (;;M.draw_map ()) switch (key = inkey ()) { case '\n': enter_othermode (M.enter ()); cls (); break; case KEY_BACKSPACE: case 'q': return; default: if (key < 256 && isalpha (key)) { unsigned int j = M.cursor (); if (*symbol_name (t [j]) < key) while (j < tn && *symbol_name (t [j]) < key) j++; else while (j > 0 && *symbol_name (t [j]) > key) j--; M.move_jump (j); } else M.move_arrow (key); } } void funcmode (bool all) { unsigned int *t, tn; if (all) t = funcs, tn = counts [CALL_ID]; else t = entries, tn = nentries; if (!tn) { flash (); return; } int key; TDmap M (t, tn, 0); M.draw_map (); for (;;M.draw_map ()) switch (key = inkey ()) { case '\n': enter_othermode (M.enter ()); cls (); break; case KEY_BACKSPACE: case 'q': return; case 'v': func_text (M.enter (), true); cls (); break; case ' ': func_text (M.enter ()); cls (); break; default: if (key < 256 && isalpha (key)) { unsigned int j = M.cursor (); if (*symbol_name (t [j]) < key) while (j < tn && *symbol_name (t [j]) < key) j++; else while (j > 0 && *symbol_name (t [j]) > key) j--; M.move_jump (j); } else M.move_arrow (key); } } void initialmode () { int key; TDmap M (Files, counts [SOURCE_ID], 0); M.draw_map (); for (;;M.draw_map ()) switch (key = inkey ()) { case '\n': enter_othermode (M.enter ()); cls (); break; case KEY_BACKSPACE: case 'q': return; case 'E': funcmode (false); cls (); break; case 'O': funcmode (true); cls (); break; case 'g': case 'G': globmode (); cls (); break; default: M.move_arrow (key); } } // "main" // int main (int argc, char **argv) { getcwd (CWD, sizeof CWD-1); if (CWD [strlen (CWD) - 1] != '/') strcat (CWD, "/"); #ifdef SOURCE_VIEWER svr = (char*) ((!strcmp (argv [0], "nccnav")) ? SOURCE_VIEWER : SOURCE_VIEWER_INDENT); #endif char *MapFile = (argc > 1) ? argv [1] : (char*)"Code.map"; read_file (MapFile); signal (SIGPIPE, SIG_IGN); init_graphics (); initialmode (); end_graphics (); printf ("%i\tFilez \n%i\tfunctionz \n%i\tglobal variables \n" "%i\tstructs\n%i\tmembers \n%i\tTotal links\n", counts [SOURCE_ID], counts [CALL_ID], counts [GLOBAL_ID], counts [STRUCT_ID], counts [MEMBER_ID], nlinks - dupes/2); } ncc-2.8/nccnav/README0000644000175000001440000001425410501575225014412 0ustar stanusers00000000000000 nccnav is used explore the output of ncc in a convenient manner for hacking. INSTALL & EXTERNAL VIEWER ========================= As of version 1.3 of ncc, nccnav can invoke an external viewer to browse source code. To install nccnav, copy the file nccnav to /usr/bin. Then make a symbolic link nccnavi -> nccnav If called as "nccnav" the source code will be viewed with "less". If called as "nccnavi" the source will be automatically indented and viewed with "indent -kr -st | less" INPUT ===== Supposing you've compiled the linux kernel with ncc. Collecting all the .nccout files can be done with : find . -name \*.nccout | xargs cat > kernel.map You can use the pathremover which is found in the current directory to truncate long paths in `kernel.map'. If you want to compile and install pathremover, the above command will be something like: find . -name \*.nccout | xargs cat | pathremover /mnt/src/hacks/linux-2.4.10/ > kernel.map Then, that's viewed with: nccnav kernel.map DISPLAY ======= nccnav is designed to run in a text console. The ideal display is a black&white framebuffer 132x50 text mode. Of course, it can be executed in an xterm window, but this usually sucks. In the framebuffer case, the colors are different from the normal text mode. DIM white is missing. INITIAL SCREENS =============== Initially, nccnav displays a list of all the source files. Pressing 'O' shows a list of all the functions and BACKSPACE or 'q' goes back to the source files. Pressing 'E' shows a list of all the functions not called by any other function (which are supposed to be interface entry functions). Pressing 'G' shows a list of all the global variables. Pressing any letter (except 'q') in the list of functions will get the cursor to the closest function beginning with that letter. Movement is possible with the arrow keys and page-up/down. Pressing on a file or function or variable goes to MODE 2. MODE 2 ====== This mode is oriented around a resource which can be a: file, function, global variable, member of structure, structure For any of the above, all the related resources are listed. For example, in the case of a there are: 1. File(s) with definition (may not exist) 2. Functions calling (Blue) 3. Global variables used by (Red) 4. 5. Functions called by Dim grey: functions that do not call any other functions. Brown: functions that do call other functions. Red: Recursion detector alert (if enabled) 6. Structure members used by (Dark grey) Selecting something from 1, 2, 3, 5, 6 and pressing enter, will recursively enter MODE 2 for that resource. Pressing enter on 4 or typing 'q' will return to the previous screen. Pressing BACKSPACE will get you to the INITIAL SCREENS with a longjmp. Pressing '<' will get you to the HISTORY MODE. Pressing 'r' on a red 5 get you to the UNROLL MODE. Pressing 'm' on a function will get you to the POP-UP MODE Pressing 'C' will run system("bash") FUNCTION CODE ============= Pressing on a function will show the function text as found in the file which is known to include the function definition. This means that, nccnav must be in the correct root directory or paths should be absolute. The ncc option -ncfabs is rather useful. Pressing 'v' on a function will show the function text with "vi" (after extracting it from its file and saving it to a temporary file), colorized with C syntax. Note that external library functions, obviously have no file. Also note that if a function reports to be defined in more than one files, this will probably fail. This happens because ncc does not distinguish different static functions with the same name. They are considered the same thing and their resources are mixed in nccnav. Currently, if nccnav detects more than one files for a function it will issue a warning. Pressing or will get you to the previous screen if you are using the internal source viewer. Otherwise, exit the external application by it's custom way of doing so. MORE CODE ========= Pressing on a file in MODE 2 will display the entire file. Pressing 'v' on a file in MODE 2 will *edit the original* file with vi (":q\n" to exit) Pressing or 'v' on a structure will extract and show the structure declaration text. RECURSION DETECTOR ================== The recursion detector is by default enabled. It can be disabled by pressing 'R' while in MODE 2. (it's supposed to be expensive and may be confusing) The recursion detector works in MODE 2 and if the current resource is a function. In this case it will paint RED all the functions called by the current function, which will eventually lead back to it by some way of recursion. Pressing 'r' on one of the red ones will display one of the possible paths through which recursion can happen. Currently there is no way to view alternative paths. In this mode, 'q' will get you back while enter will proceed ahead deeper into MODE 2 for the selected resource. POP-UP MODE =========== This is an alternative way to browse the call flow and is entered by pressing 'm' on a function in MODE 2. Pop-up menus are generated, where the top element is a function and below it all the functions called by it. Movement is possible with the UP/DOWN arrows. or RIGHT will expand a new pop-up for the current function. 'q' or LEFT will close the current pop-up and activate the previous one. SPACE is available to view the source code. BACKSPACE will close all the popups and return to MODE 2. Pressing '2' will enter MODE 2 for the current selected element. In this case, HISTORY and BACKSPACE are set at this breakpoint. HISTORY MODE ============ At any time you can press '<' and '>' to browse through all the previous screens. on one of them will jump back to that screen. Any other key will exit the HISTORY MODE. WHAT TO DO IF YOU'RE STUCK ========================== Nothing. Stay calm and do not panic. Remember: The user is the only person responsible for any damages. FINALLY ======= Except from hacking unknown projects, ncc together with valgrind are an excellent debugging platform for discovering and fixing bugs in zero time. Happy hacking! ncc-2.8/nccnav/pathremover.c0000644000175000001440000000131607607425747016245 0ustar stanusers00000000000000/* Very useful program to remove root path dir. Root path dir is bad because filenames are very long and you only get two columns of display in nccnav. Sample Usage: find . -name \*.nccout | xargs cat | pathremover /mnt/sources/hacks/linux-2.40.2/ > Code.map the path argument must end in '/' to do it right. */ #include #include int main (int argc, char **argv) { char *cwd, line [10240]; int cwdl; FILE *in; if (argc != 2) return 1; cwdl = strlen (cwd = argv [1]); in = stdin; while (fgets (line, 10240, in)) if (line [0] == 'P' && !strncmp (line + 3, cwd, cwdl)) { fputs ("P: ", stdout); fputs (line + 3 + cwdl, stdout); } else fputs (line, stdout); return 0; } ncc-2.8/objdir/0000755000175000001440000000000011074666444013537 5ustar stanusers00000000000000ncc-2.8/norm.h0000644000175000001440000001053711074114502013377 0ustar stanusers00000000000000// // The CODE[] table may include the things below, or // ascii characters for simple operators // #define THE_END 0 // actually it should be infty.. #define CALL_AGAIN 256 #define IDENT_DUMMY 257 // Intermediate reserved #define FCONSTANT 258 #define CCONSTANT 259 #define CONSTANT 300 #define STRING 301 #define FORCEERROR 302 // Special reserved #define CPP_CONCAT 302 #define CPP_DIRECTIVE 303 #define MEGATOKEN 304 #define ANONYMOUS 305 // C/C++ operators #define DOTSTAR 350 #define ELLIPSIS 351 #define POINTSAT_STAR 352 #define POINTSAT 353 #define MINUSMINUS 354 #define PLUSPLUS 355 #define SCOPE 356 #define GEQCMP 357 #define LSH 358 #define OROR 359 #define ANDAND 360 #define EQCMP 361 #define NEQCMP 362 #define RSH 363 #define LEQCMP 364 // '=' #define ASSIGNA 367 #define ASSIGNS 368 #define ASSIGNM 369 #define ASSIGND 370 #define ASSIGNR 371 #define ASSIGNBA 372 #define ASSIGNBX 373 #define ASSIGNBO 374 #define ASSIGNRS 375 #define ASSIGNLS 376 // Indexed symbols #define NSTRINGS 100000 #define NSYMBOLS 1000000 #define BASE 2000 #define STRINGBASE BASE // bases for multiplexed quantities #define SYMBASE (BASE + NSTRINGS) #define NUMBASE (SYMBASE + NSYMBOLS) #define INT8BASE (NUMBASE) #define INT16BASE (INT8BASE + 1000000) #define INT32BASE (INT8BASE + 2000000) #define UINT32BASE (INT8BASE + 3000000) #define FLOATBASE (INT8BASE + 4000000) #define INUMBER (INT8BASE + 5000000) #define ISSTRING(x) (x >= BASE && x < SYMBASE) #define ISSYMBOL(x) (x >= SYMBASE && x < NUMBASE) #define ISNUMBER(x) (x >= NUMBASE) // Helper routines #define ISOPERATOR(x) (x <= '~' || x >= DOTSTAR && x <= ASSIGNLS) #define ISASSIGNMENT(x) (x == '=' || x >= ASSIGNA && x <= ASSIGNLS) #define ISRESERVED(x) (x >= RESERVED_auto && x <= RESERVED_END) #define ISDCLFLAG(x) (x >= RESERVED_auto && x <= RESERVED_volatile) #define ISBASETYPE(x) (x >= RESERVED_void && x <= RESERVED_bool) #define ISSTRUCTSPC(x) (x >= RESERVED_class && x <= RESERVED_union) #define ISHBASETYPE(x) (x >= RESERVED_long && x <= RESERVED_unsigned) #define ISDCLSTRT(x) (x >= RESERVED_long && x <= RESERVED_bool) #define SYMBOLID(x) (x - SYMBASE) // C and C++ declaration flags #define RESERVED_auto 700 #define RESERVED_const 701 #define RESERVED_extern 702 #define RESERVED_inline 703 #define RESERVED___inline__ 703 #define RESERVED___inline 703 #define RESERVED_register 704 #define RESERVED_static 705 #define RESERVED_typedef 706 #define RESERVED_volatile 707 // these can define a type #define RESERVED_long 708 #define RESERVED_short 709 #define RESERVED_signed 710 #define RESERVED_unsigned 711 // C and C++ base types #define RESERVED_void 720 #define RESERVED_char 721 #define RESERVED_int 722 #define RESERVED_float 723 #define RESERVED_double 724 #define RESERVED_bool 725 // C and C++ aggregate specifiers #define RESERVED_class 731 #define RESERVED_struct 732 #define RESERVED_union 733 // C and C++ reserved words -- not including basic types #define RESERVED_break 741 #define RESERVED_case 742 #define RESERVED_catch 743 #define RESERVED_const_cast 745 #define RESERVED_continue 746 #define RESERVED_default 747 #define RESERVED_delete 748 #define RESERVED_do 749 #define RESERVED_dynamic_cast 750 #define RESERVED_else 751 #define RESERVED_enum 752 #define RESERVED_explict 753 #define RESERVED_export 754 #define RESERVED_false 755 // -Dfalse=0 #define RESERVED_for 756 #define RESERVED_friend 757 #define RESERVED_goto 758 #define RESERVED_if 759 #define RESERVED_mutable 760 #define RESERVED_RESERVED 761 #define RESERVED_namespace 762 #define RESERVED_new 763 #define RESERVED_operator 764 #define RESERVED_private 765 #define RESERVED_protected 766 #define RESERVED_public 767 #define RESERVED_reinterpret_cast 768 #define RESERVED_return 769 #define RESERVED_sizeof 770 #define RESERVED_static_cast 771 #define RESERVED_switch 773 #define RESERVED_template 774 #define RESERVED_this 775 #define RESERVED_throw 776 #define RESERVED_true 777 // -Dtrue=1 #define RESERVED_try 778 #define RESERVED_typeid 779 #define RESERVED_typename 780 #define RESERVED_using 781 #define RESERVED_virtual 782 #define RESERVED_while 783 #define RESERVED___asm__ 784 #ifdef GNU_VIOLATIONS #define RESERVED___typeof__ 800 #define RESERVED___label__ 801 #define RESERVED__Complex 802 #endif #define RESERVED_END 888 ncc-2.8/ccexpr.C0000644000175000001440000007276310502044035013652 0ustar stanusers00000000000000/***************************************************************************** compiler of C expressions to bytecode *****************************************************************************/ #include #include #include "global.h" int last_result; /****************************************************************************** ******************************************************************************/ int *assembly; int ap; # define TSI 's' # define TUI 'u' # define TFP 'f' # define TPTR 'p' # define TREC 'r' # define TBF 'b' #define CONVI '}' #define CONVF '\\' #define EST '$' #define ENI 'N' #define ENP 'P' #define EIN 'I' #define ENF 'F' #define ESMB 'S' #define ENL 'A' #define JMP 'J' // escape to JMP[TFA] # define JMPT 't' # define JMPF 'f' # define JMPA 'a' #define LABEL 'L' #define TEST 'T' #define CALL 'C' #define COPYREC 'Q' #define RESULT 'R' #define NOOP ' ' #define EEND '\n' // ZNPIFSAJLTCQR #define PASM1(x1) assembly[ap++] = x1 #define PASM2(x1,x2) PASM1 (x1); PASM1 (x2) #define PASM3(x1,x2,x3) PASM2 (x1,x2); PASM1 (x3) #define PASM4(x1,x2,x3,x4) PASM3 (x1,x2,x3); PASM1 (x4) #define PASM5(x1,x2,x3,x4,x5) PASM3 (x1,x2,x3); PASM2 (x4,x5) #define PASM6(x1,x2,x3,x4,x5,x6) PASM3 (x1,x2,x3); PASM3 (x4,x5,x6) #define ENDASM PASM1 (EEND) static int LP, SP; class ccsub { void fconv (); void iconv (); inline int sub_ptri (ccsub&, ccsub&); inline void settype (int); inline void lvaluate (); inline void copytype (ccsub&); inline void lvcopy (ccsub&); inline int putthis (int*); inline void putthis (int); inline void degrade (ccsub&); inline void arithmetic_convert (ccsub&, ccsub&); inline void ptr_compare (ccsub&, ccsub&); inline bool integral (); inline bool arithmetic (); inline bool structure (); inline bool constant (); inline void assign_convert (ccsub&, bool = false); inline void argtype (typeID); bool op1return; static ccsub op1; void cc_binwconv (exprID, ccsub&, ccsub&); void cc_addptr (ccsub&, ccsub&); int ptrfix (ccsub&); bool lv; void cc_fcall (exprID); void cc_prefix (exprID); void cc_postfix (exprID); void cc_tival (exprID); void cc_tfval (exprID); void cc_tsval (exprID); void cc_tlval (exprID); void cc_terminal (exprID); void cc_sizeof (exprID); void cc_dot (exprID); void cc_array (exprID); void cc_star (exprID); void cc_addrof (exprID); void cc_ecast (exprID); void cc_usign (exprID); void cc_nbool (exprID); void cc_compl (exprID); void cc_add (exprID); void cc_sub (exprID); void cc_muldiv (exprID); void cc_bintg (exprID, int); void cc_cmp (exprID, int); void cc_bool (exprID); void cc_conditional (exprID); void cc_assign (exprID); void cc_assign (ccsub&, ccsub&); void cc_oassign (exprID); void cc_compound_result (exprID); public: ccsub (exprID); ccsub () {} ccsub (typeID, bool); int base, spec [MSPEC]; int pobj, obj; inline void putthis (); }; ccsub ccsub::op1; void ccsub::cc_tival (exprID e) { settype (S_INT); pobj = ENI; obj = e; } void ccsub::cc_tfval (exprID e) { settype (FLOAT); pobj = ENF; obj = e; } void ccsub::cc_tsval (exprID e) { base = S_CHAR; spec [0] = '*'; spec [1] = -1; pobj = ENP; obj = e; } void ccsub::cc_tlval (exprID e) { base = VOID; spec [0] = '*'; spec [1] = -1; pobj = ENL; obj = e; } //################################### // sizeof expression //################################### void ccsub::cc_sizeof (exprID e) { int sap = ap; ccsub o (ee [e].voici.e); ap = sap; settype (S_INT); pobj = ENI; ee [e].action = VALUE; ee [e].voici.value = sizeof_type (o.base, o.spec); obj = e; } //################################### // terminal symbol //################################### void ccsub::cc_terminal (exprID ei) { subexpr e = ee [ei]; lookup_object ll (e.voici.symbol, false); if (ll.enumconst) { settype (S_INT); pobj = ENI; ee [ei].voici.value = ll.ec; ee [ei].action = VALUE; cc_tival (ei); } else { base = ll.base; intcpy (spec, ll.spec); pobj = ESMB; lvaluate (); } obj = ei; } //################################### // address operator & //################################### void ccsub::cc_addrof (exprID ei) { ccsub o (ee [ei].voici.e); pobj = o.pobj; obj = o.obj; base = o.base; if (o.lv || o.structure () || o.spec [0] != -1) { spec [0] = '*'; intcpy (&spec [1], o.spec); }// else if (o.spec [0] != -1) // intcpy (spec, o.spec); else half_error ("&address_of not addressable"); } //################################### // pointer indirection * //################################### void ccsub::cc_star (exprID e) { ccsub o (ee [e].voici.e); if (o.lv) { putthis (SP++); PASM1 ('='); o.putthis (); PASM1 (EEND); } else { pobj = o.pobj; obj = o.obj; } degrade (o); lvaluate (); } //################################### // array reference [] //################################### void ccsub::cc_array (exprID ei) { ccsub o1 (ee [ei].voici.e), o2 (ee [ei].e); cc_addptr (o1, o2); degrade (*this); lvaluate (); } //################################### // structure reference . //################################### void ccsub::cc_dot (exprID ei) { subexpr e = ee [ei]; ccsub o (e.voici.e); if (!o.structure ()) half_error ("member of no structure"); lookup_member lm (e.voila.member, o.base); base = lm.base; intcpy (spec, lm.spec); putthis (SP++); PASM4 ('=', EIN, lm.displacement, '+'); o.putthis (); ENDASM; lvaluate (); } //################################### // cast (type-name) //################################### void ccsub::cc_ecast (exprID ei) { subexpr e = ee [ei]; ccsub o (e.voici.e), pseudo (e.voila.cast, true); o.assign_convert (pseudo, true); copytype (o); *this = o; } //################################### // unary +/- //################################### void ccsub::cc_usign (exprID ei) { subexpr e = ee [ei]; ccsub o (e.voici.e); if (!o.arithmetic ()) half_error ("sign on pointer"); copytype (o); if (e.action == UPLUS) lvcopy (o); else if (o.pobj == ENI) { pobj = ENI; ee [ei].voici.value = -ee [o.obj].voici.value; ee [ei].action = VALUE; obj = ei; } else if (o.pobj == ENF) { pobj = ENF; ee [ei].action = FVALUE; ee [ei].voici.fvalue = -ee [o.obj].voici.fvalue; obj = ei; } else { putthis (SP++); PASM2 ('=', '-'); o.putthis (); ENDASM; } } //################################### // logical negation ! //################################### void ccsub::cc_nbool (exprID ei) { ccsub o (ee [ei].voici.e); if (o.pobj == ENI || o.pobj == ENF) { pobj = ENI; ee [obj = ei].voici.value = !ee [o.obj].voici.value; ee [ei].action = VALUE; } else { putthis (SP++); PASM2 ('=', '!'); o.putthis (); ENDASM; } settype (S_INT); } //################################### // one's complement ~ //################################### void ccsub::cc_compl (exprID ei) { ccsub o (ee [ei].voici.e); if (!o.integral ()) half_error ("~integral"); if (o.pobj == ENI) { pobj = ENI; ee [obj = ei].voici.value = ~ee [o.obj].voici.value; ee [ei].action = VALUE; } else { putthis (SP++); PASM2 ('=', '~'); o.putthis (); ENDASM; } settype (S_INT); } //################################### // binary operators with integral // operands <<%&|^>> //################################### void ccsub::cc_bintg (exprID ei, int op) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); if (op1return) op1 = o1; if (!o1.integral () || !o2.integral ()) half_error ("<<, %, &, |, ^, >> need intergal operands"); if (o1.pobj == ENI && o2.pobj == ENI) { int v = 0, v1 = ee [o1.obj].voici.value; int v2 = ee [o2.obj].voici.value; pobj = ENI; switch (e.action) { case REM: v = v1 % v2; break; case SHR: v = v1 >> v2; break; case SHL: v = v1 << v2; break; case BOR: v = v1 | v2; break; case BAND: v = v1 & v2; break; case BXOR: v = v1 ^ v2; break; } ee [obj = ei].voici.value = v; ee [ei].action = VALUE; } else { putthis (SP++); PASM1 ('='); o1.putthis (); PASM1 (op); o2.putthis (); ENDASM; } settype (S_INT); } //################################### // multiply divide * / //################################### void ccsub::cc_muldiv (exprID ei) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); if (op1return) op1 = o1; if (!o1.arithmetic () || !o2.arithmetic ()) half_error ("*,/ need arithmetic operands"); cc_binwconv (ei, o1, o2); } //################################### // prefix ++ -- //################################### void ccsub::cc_prefix (exprID ei) { int pi = 1; subexpr e = ee [ei]; ccsub o (e.voici.e); if (!o.lv) half_error ("(++/--) no-lvalue"); if (o.arithmetic ()) { if (!o.integral ()) half_error ("++float"); } else pi = ptr_increment (o.base, o.spec); o.putthis (); PASM1 ('='); o.putthis (); PASM4 (e.action == PPPRE ? '+' : '-', EIN, pi, EEND); copytype (o); lvcopy (o); } //################################### // postfix ++ -- //################################### void ccsub::cc_postfix (exprID ei) { int pi = 1; subexpr e = ee [ei]; ccsub o (e.voici.e); if (!o.lv) half_error ("no-lvalue (++/--)"); if (o.arithmetic ()) { if (!o.integral ()) half_error ("--float"); } else pi = ptr_increment (o.base, o.spec); copytype (o); putthis (SP++); PASM1 ('='); o.putthis (); ENDASM; o.putthis (); PASM1 ('='); o.putthis (); PASM4 (e.action == PPPOST ? '+' : '-', EIN, pi, EEND); } //################################### // addition + //################################### void ccsub::cc_add (exprID ei) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); if (op1return) op1 = o1; if (o1.arithmetic () && o2.arithmetic ()) cc_binwconv (ei, o1, o2); else cc_addptr (o1, o2); } //################################### // subtract - //################################### void ccsub::cc_sub (exprID ei) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); if (op1return) op1 = o1; if (o1.arithmetic () && o2.arithmetic ()) { cc_binwconv (ei, o1, o2); return; } if (!o1.arithmetic () && !o2.arithmetic ()) { // (p1 - p2) / sizeof (*p1) int pi = sub_ptri (o1, o2), tobj; PASM3 (EST, tobj = SP++, '='); o1.putthis (); PASM1 ('-'); o2.putthis (); ENDASM; putthis (SP++); PASM6 ('=', EST, tobj, '/', EIN, pi); ENDASM; settype (S_INT); } else if (o2.integral ()) { // p - sizeof (*p) * v o2.ptrfix (o1); copytype (o1); putthis (SP++); PASM1 ('='); o1.putthis (); PASM1 ('-'); o2.putthis (); ENDASM; } else if (o1.arithmetic ()) half_error ("integer - pointer"); else half_error ("pointer - shit"); } //################################### // compare == != < <= >= > //################################### void ccsub::cc_cmp (exprID ei, int op) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); if (o1.arithmetic () && o1.arithmetic ()) arithmetic_convert (o1, o2); else ptr_compare (o1, o2); if (o1.pobj == ENI && o2.pobj == ENI) { int v = 0, v1 = ee [o1.obj].voici.value; int v2 = ee [o2.obj].voici.value; pobj = ENI; switch (e.action) { case BEQ: v = v1 == v2; break; case BNEQ: v = v1 != v2; break; case CGR: v = v1 > v2; break; case CGRE: v = v1 >= v2; break; case CLE: v = v1 < v2; break; case CLEE: v = v1 <= v2; break; } ee [obj = ei].voici.value = v; ee [ei].action = VALUE; } else { putthis (SP++); PASM1 ('='); o1.putthis (); PASM1 (op); o2.putthis (); ENDASM; } settype (S_INT); } //################################### // early termination && || //################################### void ccsub::cc_bool (exprID ei) { subexpr e = ee [ei]; int jlabel, oror = e.action == IOR; ccsub o1 (e.voici.e); if (o1.pobj == ENI) { int v = ee [o1.obj].voici.value; if (oror ? v != 0 : v == 0) { ee [ei].action = VALUE; ee [ei].voici.value = (oror) ? 1 : 0; pobj = ENI; obj = ei; } else { ccsub o2 (e.e); if (o2.pobj == ENI) { ee [ei].action = VALUE; ee [ei].voici.value = ee [o2.obj].voici.value != 0; pobj = ENI; obj = ei; } else { putthis (SP++); PASM4 ('=', EIN, 1, EEND); PASM1 (TEST); o2.putthis (); PASM4 (EEND, JMP, JMPT, LP); putthis (); PASM4 ('=', EIN, 0, EEND); PASM2 (LABEL, LP++); } } } else { putthis (SP++); PASM4 ('=', EIN, oror ? 0 : 1, EEND); PASM1 (TEST); o1.putthis (); PASM4 (EEND, JMP, oror ? JMPT : JMPF, jlabel = LP++); ccsub o2 (e.e); PASM1 (TEST); o2.putthis (); PASM4 (EEND, JMP, oror ? JMPF : JMPT, LP); PASM2 (LABEL, jlabel); putthis (); PASM4 ('=', EIN, oror ? 1 : 0, EEND); PASM2 (LABEL, LP++); } settype (S_INT); } //################################### // conditional ? : //################################### void ccsub::cc_conditional (exprID ei) { subexpr e = ee [ei]; int jlabel1, jlabel2; bool o1float, o2float; ccsub o (e.voici.e); if (o.pobj == ENI) { if (ee [o.obj].voici.value) if (e.e == -1) *this = o; else { ccsub o1 (e.e); *this = o1; } else { ccsub o1 (e.voila.eelse); *this = o1; } return; } PASM1 (TEST); o.putthis (); PASM4 (EEND, JMP, JMPF, jlabel1 = LP++); ccsub o1 (e.e), *ufo = e.e == -1 ? &o : &o1; o1float = ufo->arithmetic () && ufo->base == FLOAT; putthis (SP++); PASM1 ('='); ufo->putthis (); PASM4 (EEND, JMP, JMPA, jlabel2 = LP++); PASM2 (LABEL, jlabel1); ccsub o2 (e.voila.eelse); o2float = o2.arithmetic () && o2.base == FLOAT; if (o1float || o2float && o1float != o2float) if (o1float) { // ? float : int o2.fconv (); putthis (); PASM1 ('='); o2.putthis (); PASM3 (EEND, LABEL, jlabel2); settype (FLOAT); } else { // ? int : float putthis (SP++); PASM1 ('='); o2.putthis (); PASM6 (EEND, JMP, JMPA, LP, LABEL, jlabel2); ufo->fconv (); putthis (); PASM1 ('='); ufo->putthis (); PASM3 (EEND, LABEL, LP++); settype (FLOAT); } else { putthis (); PASM1 ('='); o2.putthis (); PASM3 (EEND, LABEL, jlabel2); if (ufo->arithmetic ()) copytype (o2); else copytype (*ufo); } } //################################### // assignment = //################################### void ccsub::cc_assign (exprID ei) { subexpr e = ee [ei]; ccsub o1 (e.voici.e), o2 (e.e); cc_assign (o1, o2); } void ccsub::cc_assign (ccsub &o1, ccsub &o2) { o2.assign_convert (o1); if (o1.lv) { o1.putthis (); PASM1 ('='); o2.putthis (); ENDASM; } else { if (!o1.structure ()) half_error ("not lvalue in assignment"); if (o1.base != o2.base) half_error ("incompatible"); PASM2 (COPYREC, o1.base); o1.putthis (); o2.putthis (); ENDASM; } copytype (o1); obj = o2.obj; pobj = o2.pobj; } //################################### // op assignment += -= *= /= ... //################################### void ccsub::cc_oassign (exprID ei) { op1return = true; switch (ee [ei].action) { case ASSIGNA: ee [ei].action = ADD; cc_add (ei); break; case ASSIGNS: ee [ei].action = SUB; cc_sub (ei); break; case ASSIGNM: ee [ei].action = MUL; cc_muldiv (ei); break; case ASSIGND: ee [ei].action = DIV; cc_muldiv (ei); break; case ASSIGNR: cc_bintg (ei, '%'); break; case ASSIGNBA: cc_bintg (ei, '&'); break; case ASSIGNBX: cc_bintg (ei, '^'); break; case ASSIGNBO: cc_bintg (ei, '|'); break; case ASSIGNRS: cc_bintg (ei, RSH); break; case ASSIGNLS: cc_bintg (ei, LSH); break; } cc_assign (op1, *this); } //################################### // function call () //################################### void ccsub::cc_fcall (exprID ei) { int argcc [100], aap = 0, i; typeID *tp; subexpr e = ee [ei]; if (ee [e.voici.e].action == SYMBOL) { lookup_function lf (ee [e.voici.e].voici.symbol); base = lf.base; intcpy (spec, lf.spec + 2); tp = ret_arglist (lf.spec [1]); argcc [aap++] = ESMB; argcc [aap++] = e.voici.e; } else { ccsub fn (ee [ei].voici.e); base = fn.base; if (fn.spec [0] != '(') { if (fn.spec [0] != '*' || fn.spec [1] != '(') half_error ("not a function"); intcpy (spec, fn.spec + 3); tp = ret_arglist (fn.spec [2]); } else { intcpy (spec, fn.spec + 2); tp = ret_arglist (fn.spec [1]); } aap += fn.putthis (argcc + aap); } argcc [aap++] = '('; if ((ei = ee [ei].e) != -1) { for (; ee [ei].action == ARGCOMMA; ei = ee [ei].e) { ccsub o (ee [ei].voici.e); aap += o.putthis (argcc + aap); if (*tp == -1) half_error ("too many arguments"); if (*tp > 0) o.argtype (*tp++); } ccsub o (ei); aap += o.putthis (argcc + aap); if (*tp == -1) half_error ("too many arguments"); if (*tp > 0) o.argtype (*tp++); } if (*tp > 0) half_error ("too few arguments"); argcc [aap++] = ')'; putthis (SP++); PASM2 ('=', CALL); for (i = 0; i < aap; i++) PASM1 (argcc [i]); ENDASM; } //################################### // result of the last subexpression // of a compound statement in expression // (a horrible violation) //################################### void ccsub::cc_compound_result (exprID ei) { type t; opentype (ee [ei].voila.result_type, t); base = t.base; intcpy (spec, t.spec); pobj = RESULT; obj = ee [ei].voici.using_result; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// void ccsub::cc_binwconv (exprID ei, ccsub &o1, ccsub &o2) { if (!o2.arithmetic () || !o1.arithmetic ()) half_error ("Need arithmetic operands"); arithmetic_convert (o1, o2); if (o1.pobj == ENI && o2.pobj == ENI) { int v = 0, v1 = ee [o1.obj].voici.value; int v2 = ee [o2.obj].voici.value; pobj = ENI; switch (ee [ei].action) { case ADD: v = v1 + v2; break; case SUB: v = v1 - v2; break; case MUL: v = v1 * v2; break; case DIV: if (v2 == 0) { if (include_values && !usage_only) fprintf (stderr, "Divizion by zero" " in arithmetic\n"); v = v1; } else v = v1 / v2; break; } ee [obj = ei].voici.value = v; ee [ei].action = VALUE; } else if (o1.pobj == ENF && (o2.pobj == ENF || o2.pobj == ENI) || o2.pobj == ENF && o1.pobj == ENI) { float v = 0; float v1 = (o1.pobj == ENF) ? ee [o1.obj].voici.fvalue : (float) ee [o1.obj].voici.value; float v2 = (o2.pobj == ENF) ? ee [o2.obj].voici.fvalue : (float) ee [o2.obj].voici.value; pobj = ENF; switch (ee [ei].action) { case ADD: v = v1 + v2; break; case SUB: v = v1 - v2; break; case MUL: v = v1 * v2; break; case DIV: v = v1 / v2; break; } ee [obj = ei].voici.fvalue = v; ee [ei].action = FVALUE; } else { putthis (SP++); PASM1 ('='); o1.putthis (); switch (ee [ei].action) { case ADD: PASM1 ('+'); break; case SUB: PASM1 ('-'); break; case MUL: PASM1 ('*'); break; case DIV: PASM1 ('/'); break; } o2.putthis (); ENDASM; } settype (o1.base); } void ccsub::cc_addptr (ccsub &o1, ccsub &o2) { bool b1 = o1.arithmetic (), b2 = o2.arithmetic (); if (b1 && b2) half_error ("No pointer operand"); if (!b1 && !b2) half_error ("pointer + pointer"); if (b2) { o2.ptrfix (o1); copytype (o1); } else { o1.ptrfix (o2); copytype (o2); } putthis (SP++); PASM1 ('='); o1.putthis (); PASM1 ('+'); o2.putthis (); PASM1 (EEND); } void ccsub::copytype (ccsub &o) { base = o.base; intcpy (spec, o.spec); } void ccsub::degrade (ccsub &o) { base = o.base; if (o.spec [0] == -1 || o.spec [0] == '(') half_error ("* not effective"); intcpy (spec, o.spec + (o.spec [0] == '*' ? 1 : 2)); } int ccsub::ptrfix (ccsub &o) { int pi = ptr_increment (o.base, o.spec); if (!integral ()) half_error ("pointer + float"); if (pobj == ENI) { pobj = EIN; obj = pi * ee [obj].voici.value; } else { PASM6 (EST, SP, '=', EIN, pi, '*'); putthis (); PASM1 (EEND); pobj = EST; obj = SP++; lv = false; } return pi; } int ccsub::sub_ptri (ccsub &o1, ccsub &o2) { int i = ptr_increment (o1.base, o1.spec); if (ptr_increment (o2.base, o2.spec) != i) half_error ("'-' on different pointers"); return i; } int ccsub::putthis (int *p) { int *sa = assembly; int sap = ap, r; assembly = p; ap = 0; putthis (); assembly = sa; r = ap; ap = sap; return r; } void ccsub::putthis () { if (lv) { PASM1 ('*'); if (spec [0] == '*') { PASM1 (TPTR); } else if (base >= 0) { PASM2 (TREC, base); } else switch (base) { case S_CHAR: PASM2 (TSI, 1); break; case U_CHAR: PASM2 (TUI, 1); break; case S_SINT: PASM2 (TSI, 2); break; case U_SINT: PASM2 (TUI, 2); break; case S_LINT: PASM2 (TSI, 4); break; case U_LINT: PASM2 (TUI, 4); break; case S_INT : PASM2 (TSI, 4); break; case U_INT : PASM2 (TUI, 4); break; case S_LONG: PASM2 (TSI, 8); break; case U_LONG: PASM2 (TUI, 8); break; case FLOAT : PASM1 (TFP); break; default: PASM2 (TBF, base); break; } } PASM2 (pobj, obj); } void ccsub::putthis (int nobj) { PASM2 (pobj, obj = nobj); } void ccsub::lvcopy (ccsub &o) { if (o.lv) { PASM3 (EST, obj = SP++, '='); o.putthis (); PASM1 (EEND); } else { obj = o.obj; pobj = o.pobj; } } bool ccsub::structure () { return spec [0] == -1 && base >= 0; } bool ccsub::arithmetic () { return spec [0] == -1 && base < VOID || spec [0] == ':'; } bool ccsub::integral () { return spec [0] == -1 && base < FLOAT || spec [0] == ':'; } bool ccsub::constant () { return pobj == ENI || pobj == ENF; } void ccsub::lvaluate () { lv = !(spec [0] =='[' || spec [0] ==-1 && base >=0 || spec [0] =='('); } void ccsub::settype (int b) { base = b; spec [0] = -1; } void ccsub::assign_convert (ccsub &o, bool casted) { if (o.arithmetic ()) { if (o.base != FLOAT) { if (arithmetic ()) { if (base == FLOAT) iconv (); } } else { if (arithmetic ()) { if (base != FLOAT) fconv (); } else half_error ("ptr to float conv"); } } else if (o.spec [0] == -1) { if (!casted) if (spec [0] != -1 || o.base != base) half_error ("incompatible types"); } else { if (arithmetic ()) { if (base == FLOAT) half_error ("make ptr from float"); //else if (!casted) warning... } } base = o.base; intcpy (spec, o.spec); } void ccsub::argtype (typeID i) { ccsub pseudo (i, true); assign_convert (pseudo, false); } void ccsub::ptr_compare (ccsub &o1, ccsub &o2) { if (o1.arithmetic () && !o1.integral () || o2.arithmetic () && !o2.integral ()) half_error ("compare pointer w/ float, duh???"); } void ccsub::arithmetic_convert (ccsub &o1, ccsub &o2) { if (o1.base == FLOAT || o2.base == FLOAT) { if (o1.base != o2.base) if (o1.base == FLOAT) o2.fconv (); else o1.fconv (); } } void ccsub::fconv () { if (pobj == ENI) { ee [obj].voici.fvalue = (double) ee [obj].voici.value; pobj = ENF; } else { PASM4 (EST, SP, '=', CONVF); putthis (); ENDASM; pobj = EST; obj = SP++; lv = false; } settype (FLOAT); } void ccsub::iconv () { if (pobj == ENF) { ee [obj].voici.value = (int) ee [obj].voici.fvalue; pobj = ENI; } else { PASM4 (EST, SP, '=', CONVI); putthis (); PASM1 (EEND); pobj = EST; obj = SP++; lv = false; } settype (S_INT); } //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ccsub::ccsub (exprID ei) { if (ei == -1) return; advance: subexpr e = ee [ei]; pobj = EST; lv = false; op1return = false; switch (e.action) { case VALUE: case UVALUE: cc_tival (ei); break; case FVALUE: cc_tfval (ei); break; case SVALUE: cc_tsval (ei); break; case AVALUE: cc_tlval (ei); break; case SYMBOL: cc_terminal (ei); break; case PPPOST: case MMPOST: cc_postfix (ei); break; case FCALL: cc_fcall (ei); break; case MEMB: cc_dot (ei); break; case ARRAY: cc_array (ei); break; case ADDROF: cc_addrof (ei); break; case PTRIND: cc_star (ei); break; case PPPRE: case MMPRE: cc_prefix (ei); break; case CAST: cc_ecast (ei); break; case LNEG: cc_nbool (ei); break; case OCPL: cc_compl (ei); break; case UPLUS: case UMINUS: cc_usign (ei); break; case SIZEOF: cc_sizeof (ei); break; case MUL: case DIV: cc_muldiv (ei); break; case ADD: cc_add (ei); break; case SUB: cc_sub (ei); break; case REM: cc_bintg (ei, '%'); break; case SHR: cc_bintg (ei, RSH); break; case SHL: cc_bintg (ei, LSH); break; case BOR: cc_bintg (ei, '|'); break; case BAND: cc_bintg (ei, '&'); break; case BXOR: cc_bintg (ei, '^'); break; case IAND: case IOR: cc_bool (ei); break; case BEQ: cc_cmp (ei, EQCMP); break; case BNEQ: cc_cmp (ei, NEQCMP); break; case CGR: cc_cmp (ei, '>'); break; case CGRE: cc_cmp (ei, GEQCMP); break; case CLE: cc_cmp (ei, '<'); break; case CLEE: cc_cmp (ei, LEQCMP); break; case COND: cc_conditional (ei); break; case COMPOUND_RESULT: cc_compound_result (ei); break; case COMMA: { ccsub o (e.voici.e); ei = e.e; (void) o.lv; goto advance; } default: if (e.action == '=') cc_assign (ei); else cc_oassign (ei); } } ccsub::ccsub (typeID ti, bool) { type t; opentype (ti, t); base = t.base; intcpy (spec, t.spec); } /* ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ now we can implement typeof expression ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ */ typeID typeof_expression () { if (CExpr.first == -1) syntax_error (ExpressionPtr, "this has no type"); typeID ret = 0; SP = LP = 0; ap = 0; assembly = (int*) alloca (17 * CExpr.ne * sizeof (int) + 4); try { ccsub CC (CExpr.first); type t; t.base = CC.base; t.spec = CC.spec; ret = gettype (t) + TYPEDEF_BASE; } catch (EXPR_ERROR) { syntax_error (ExpressionPtr, "expression trivial for typeof()"); } return ret; } /****************************************************************************** (note to self: attempt not to reorder commands unless sure) ******************************************************************************/ // // this here, is a mini parser of the bytecode assembly object code // to readable bytecode assembly, for now the only interesting thing // is to show the generated bytecode readably. In the future, we may // just dump the assembly tokens to an object file, so this here will // be gone // static inline bool opstart (int o) { return o=='*'||o==EST||o==EIN||o==ENI|| o==ENF||o==ESMB||o==ENP||o==ENL || o==RESULT; } int putoperand (int i) { if (assembly [i] == '*') { ++i; if (assembly [i] == TFP || assembly [i] == TPTR) { printf (" *{%c}", assembly [i]); } else { printf (" *{%c%i}", assembly [i], assembly [i+1]); i++; } i++; } switch (assembly [i++]) { case EST: printf ("$%i ", assembly [i]); break; case EIN: printf ("%i ", assembly [i]); break; case ENI: printf ("%li ", ee [assembly [i]].voici.value); break; case ENF: printf ("%f ", ee [assembly [i]].voici.fvalue);break; case ENP: printf ("\"%s\" ", C_Strings [ee [assembly [i]].voici.value]);break; case ESMB: printf ("%s ", expand (ee [assembly [i]].voici.symbol)); break; case ENL: printf (".label %s ", expand (ee [assembly [i]].voici.symbol)); break; case RESULT: printf (".result_%i ", assembly [i]); break; } return i + 1; } void putthis () { int *i; for (i = assembly; *i!= -1;) { if (*i == EEND) { printf ("\n"); i++; continue; } if (*i == NOOP) { i++; continue; } if (*i == LABEL) { printf ("L%i:\t", *++i); i++; } else printf ("\t"); if (*i == TEST) { printf ("test "); i = assembly + putoperand (i - assembly + 1); continue; } if (*i == JMP) { switch (*++i) { case JMPT: printf ("jmpt "); break; case JMPF: printf ("jmpf "); break; case JMPA: printf ("jmp "); break; } printf ("L%i\n", *++i); i++; continue; } if (*i == RESULT) { printf (".result_%i ", *++i); i = assembly + putoperand (i - assembly + 1); continue; } if (*i == COPYREC) { printf (".copyrec %i: ", *++i); i = assembly + putoperand (i - assembly + 1); i = assembly + putoperand (i - assembly); continue; } i = assembly + putoperand (i - assembly); if (*i != '=') continue; printf (" = "); i++; if (*i == CALL) { printf (".call "); i = assembly + putoperand (i - assembly + 1) + 1; printf ("("); while (*i != ')') i = assembly + putoperand (i - assembly); printf (")"); i++; continue; } if (!opstart (*i)) { if (*i == CONVI) printf ("\\int "); else if (*i == CONVF) printf ("\\float "); else printf (" %s ", expand (*i)); i++; } i = assembly + putoperand (i - assembly); if (*i == EEND) continue; printf (" %s ", expand (*i)); i = assembly + putoperand (i - assembly + 1); } printf ("\n"); } /* --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ok, we did that. Tomorrow we must go see the persons in charge. They want to speak to us --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- */ void compile (exprID e) { SP = LP = 0; ap = 1; assembly [0] = EEND; ccsub CC (e); #ifdef GNU_VIOLATIONS last_result_type.base = CC.base; intcpy (last_result_type.spec, CC.spec); #endif PASM2 (RESULT, ++last_result); CC.putthis (); assembly [ap++] = EEND; assembly [ap++] = -1; assembly [ap++] = -1; putthis (); } // // // // class ncci_cc : public ncci { public: void cc_expression (); void new_function (Symbol); void inline_assembly (NormPtr, int); }; void ncci_cc::cc_expression () { assembly = (int*) alloca (17 * CExpr.ne * sizeof (int) + 4); try { if (CExpr.first != -1) compile (CExpr.first); } catch (EXPR_ERROR) { } } int cc_int_expression () { int r = 112; assembly = (int*) alloca (17 * CExpr.ne * sizeof (int) + 4); try { SP = LP = 0; ap = 1; assembly [0] = EEND; ccsub CC (CExpr.first); #ifndef FAKE_VARIABLE_ARRAYS if (CC.pobj != ENI) half_error ("couldn't compute constant integer expr"); #endif r = ee [CC.obj].voici.value; } catch (EXPR_ERROR) { syntax_error (ExpressionPtr, "Expression Trivial to continue"); } return r; } void ncci_cc::new_function (Symbol s) { printf ("#########################################################\n"); printf ("# Function %s\n", expand (s)); printf ("#########################################################\n"); } void ncci_cc::inline_assembly (NormPtr p, int n) { printf ("#inline assembly\n"); while (n--) printf ("%s ", expand (CODE [p++])); printf ("\n\n"); } void set_compilation () { ncc = new ncci_cc; } ncc-2.8/usage.C0000644000175000001440000005330511074137072013472 0ustar stanusers00000000000000/***************************************************************************** $ C-flow and data usage analysis. $ $ Stripped-down version of ccexpr. expressions are compiled but $ without producing bytecode assembly. just inform about $ function calls, use of global variables and use of members $ of structures *****************************************************************************/ #include #include #include "global.h" #include "inttree.h" bool infuncs = false; static intTree printed; static intTree printed_function; //*********************************************************** // Output Formats & Text //*********************************************************** static char *txt_func = "\nD: %s()\n"; static char *txt_fcall = "F: %s()\n"; static char *txt_virt = "F: (*virtual)()\n"; static char *txt_error = "F: NCC:syntax_error()\n"; static char *txt_gvar_r = "g: %s\n"; static char *txt_evar_r = "g: %s\n"; static char *txt_gvar_ra = "g: %s[]\n"; static char *txt_evar_ra = "g: %s[]\n"; static char *txt_gvar = "G: %s\n"; static char *txt_gvar_a = "G: %s[]\n"; static char *txt_evar = "G: %s\n"; static char *txt_evar_a = "G: %s[]\n"; static char *txt_memb_r = "s: %s.%s\n"; static char *txt_memb = "S: %s.%s\n"; static char *txt_memb_ra = "s: %s.%s[]\n"; static char *txt_memb_a = "S: %s.%s[]\n"; static char *txt_fpcall = "F: *%s()\n"; static char *txt_fcallback = "F: *%s.%s()\n"; static char *farg_named_call = "F: %s/%s()\n"; static char *farg_call_redirect = "\nD: %s/%s()\nF: *%s()__farg%i()\n"; static char *txt_argval = "R: *%s()__farg%i() %s()\n"; static char *txt_argvalf = "R: *%s()__farg%i() *%s()\n"; static char *txt_argvalfl = "R: *%s()__farg%i() %s/%s()\n"; static char *txt_argvalargval = "R: *%s()__farg%i() *%s()__farg%i()\n"; static char *txt_membargval = "R: *%s()__farg%i() *%s.%s()\n"; //******************************************** char *structname (Symbol s) { return s == -1 ? (char*) "{anonymous}" : C_Syms [SYMBOLID (s)]; } void report (Symbol s, int frame, bool wrt, bool arr) { if (INGLOBAL || !infuncs) return; if (!multiple) { if (printed.intFind ((wrt ? 1000000 : 0) + (arr ? 2000000 : 0) + frame * 100000 + s)) return; else new intNode (&printed); } if (frame == -1 ) PRINTF (wrt ? (arr ? txt_evar_a : txt_evar) : arr ? txt_evar_ra : txt_evar_r, C_Syms [SYMBOLID (s)]); else if (frame == 0 ) PRINTF (wrt ? (arr ? txt_gvar_a : txt_gvar) : arr ? txt_gvar_ra : txt_gvar_r, C_Syms [SYMBOLID (s)]); else PRINTF (wrt ? (arr ? txt_memb_a : txt_memb) : arr ? txt_memb_ra : txt_memb_r, structname (struct_by_name (frame)), C_Syms [SYMBOLID (s)]); if (arr) report (s, frame, false, false); } static Symbol current_function; void newfunction (Symbol s) { current_function = s; PRINTF (txt_func, C_Syms [SYMBOLID (s)]); if (printed.root) { delete printed.root; printed.root = NULL; } if (printed_function.root) { delete printed_function.root; printed_function.root = NULL; } } static bool local_object (Symbol s) { lookup_object L (s); return L.FRAME > 0; } void report_call (Symbol s, bool pointer) { Symbol func = pointer && local_object (s) ? current_function : 0; if (!multiple) if (printed_function.intFind (s + func)) return; else new intNode (&printed_function); if (func) PRINTF (farg_named_call, C_Syms [SYMBOLID (func)], C_Syms [SYMBOLID (s)]); else PRINTF (pointer ? txt_fpcall : txt_fcall, C_Syms [SYMBOLID (s)]); } void report_callback (Symbol rec, Symbol s) { PRINTF (txt_fcallback, structname (rec), C_Syms [SYMBOLID (s)]); } struct argnameassign { Symbol s1, s2; int argi; }; static earray arg_names; static void stor_named_conv (Symbol f, Symbol a, int n) { int i = arg_names.alloc (); arg_names.x [i].s1 = SYMBOLID (f); arg_names.x [i].s2 = SYMBOLID (a); arg_names.x [i].argi = n; } void report_named_convert () { int i; if (arg_names.nr) PRINTF ("\n"); for (i = 0; i < arg_names.nr; i++) PRINTF (farg_call_redirect, C_Syms [arg_names.x [i].s1], C_Syms [arg_names.x [i].s2], C_Syms [arg_names.x [i].s1], arg_names.x [i].argi); } void report_fargcall (int a, Symbol s) { stor_named_conv (current_function, s, a); PRINTF (farg_named_call, C_Syms [SYMBOLID (current_function)], C_Syms [SYMBOLID (s)]); } struct fptrassign { Symbol s1, s2, m1, m2, fn, fn1; bool sptr, a1, a2; }; static earray fptr_assignments; // some help with the arguments // 's1' and 's2' are the names of the functions assigned. // For example 'funcptr=foo', s1=funcptr and s2=foo // 'm1' and 'm2' are useful for the case one of the two is a member // For example 'obj->funcptr=foo' or 'obj->funcptr=x.f' // if 'fn' is present then the second call is converted to 'fn/s2()' // if 'fn1' is present then the first call is of 'f1/s1()' form // This is all too hairy but this function is the result of hacking // and extending ncc itself. // void functionptr (Symbol s1, Symbol s2, bool pointer, Symbol m1, Symbol m2, Symbol fn, Symbol fn1, bool a1, bool a2) { int i; for (i = 0; i < fptr_assignments.nr; i++) { fptrassign fp = fptr_assignments.x [i]; if (fp.s1 == s1 && fp.s2 == s2 && fp.fn == fn && fp.fn1 == fn1 && fp.a1 == a1 && fp.a2 == a2 && fp.m1 == m1 && fp.m2 == m2) return; } i = fptr_assignments.alloc (); fptr_assignments.x [i].s1 = s1; fptr_assignments.x [i].s2 = s2; fptr_assignments.x [i].m1 = m1; fptr_assignments.x [i].m2 = m2; fptr_assignments.x [i].sptr = pointer; fptr_assignments.x [i].fn = fn; fptr_assignments.x [i].a1 = a1; fptr_assignments.x [i].a2 = a2; fptr_assignments.x [i].fn1 = fn1; } void report_fptrs () { int i; for (i = 0; i < fptr_assignments.nr; i++) { fptrassign fp = fptr_assignments.x [i]; PRINTF ("\nD: "); if (fp.m1 == -1 && fp.fn1 != -1) PRINTF ("%s/", C_Syms [SYMBOLID (fp.fn1)]); else if (fp.m1 != -1) PRINTF ("*%s.", structname (fp.s1)); else PRINTF ("*"); PRINTF ("%s", C_Syms [fp.m1 == -1 ? SYMBOLID (fp.s1) : SYMBOLID (fp.m1)]); if (fp.a1) PRINTF ("[]"); PRINTF ("()\n"); PRINTF ("F: "); if (fp.m2 == -1 && fp.fn != -1) PRINTF ("%s/", C_Syms [SYMBOLID (fp.fn)]); else if (fp.m2 == -1 && fp.fn == -1 && fp.sptr) PRINTF ("*"); else if (fp.m2 != -1) PRINTF ("*%s.", structname (fp.s2)); PRINTF ("%s", C_Syms [fp.m2 == -1 ? SYMBOLID (fp.s2) : SYMBOLID (fp.m2)]); if (fp.a2) PRINTF ("[]"); PRINTF ("()\n"); } } void report_virtual () { PRINTF (txt_virt); } void report_error () { PRINTF (txt_error); } struct fargsave { Symbol f, f2, s, lf; int ia, ia2; bool ptr; }; static earray farg_saves; void report_fargval (Symbol f, int ia, Symbol f2, int ia2, bool ptr = false) { int i = farg_saves.alloc (); farg_saves.x [i].f = f; farg_saves.x [i].f2 = f2; farg_saves.x [i].ia = ia; farg_saves.x [i].ia2 = ia2; farg_saves.x [i].ptr = ptr; farg_saves.x [i].lf = farg_saves.x [i].s = -1; } void report_fargval_memb (Symbol f, int ia, Symbol s, Symbol m) { int i = farg_saves.alloc (); farg_saves.x [i].f = f; farg_saves.x [i].f2 = m; farg_saves.x [i].ia = ia; farg_saves.x [i].s = s; farg_saves.x [i].lf = -1; } void report_fargval_locf (Symbol f, int ia, Symbol f2, Symbol l) { int i = farg_saves.alloc (); farg_saves.x [i].f = f; farg_saves.x [i].f2 = f2; farg_saves.x [i].ia = ia; farg_saves.x [i].lf = l; farg_saves.x [i].s = -1; } void report_argument_calls () { int i; if (farg_saves.nr) PRINTF ("\n"); for (i = 0; i < farg_saves.nr; i++) if (farg_saves.x [i].s != -1) PRINTF (txt_membargval, C_Syms [SYMBOLID (farg_saves.x [i].f)], farg_saves.x [i].ia, C_Syms [SYMBOLID (farg_saves.x [i].s)], C_Syms [SYMBOLID (farg_saves.x [i].f2)]); else if (farg_saves.x [i].lf != -1) PRINTF (txt_argvalfl, C_Syms [SYMBOLID (farg_saves.x [i].f)], farg_saves.x [i].ia, C_Syms [SYMBOLID (farg_saves.x [i].lf)], C_Syms [SYMBOLID (farg_saves.x [i].f2)]); else if (farg_saves.x [i].ia2 == -1) PRINTF (farg_saves.x [i].ptr ? txt_argvalf : txt_argval, C_Syms [SYMBOLID (farg_saves.x [i].f)], farg_saves.x [i].ia, C_Syms [SYMBOLID (farg_saves.x [i].f2)]); else PRINTF (txt_argvalargval, C_Syms [SYMBOLID (farg_saves.x [i].f)], farg_saves.x [i].ia, C_Syms [SYMBOLID (farg_saves.x [i].f2)], farg_saves.x [i].ia2); } class ccsub_small { inline void fconv (); inline void iconv (); inline void settype (int); inline void lvaluate (); inline void copytype (ccsub_small&); inline void degrade (ccsub_small&); inline void arithmetic_convert (ccsub_small&, ccsub_small&); inline bool arithmetic (); inline bool voidp (); inline bool structure (); inline void assign_convert (ccsub_small&); bool op1return; static ccsub_small op1; inline void cc_binwconv (ccsub_small&, ccsub_small&); inline void cc_addptr (ccsub_small&, ccsub_small&); bool lv, modify, array; void cc_fcall (exprID); void cc_prepostfix (exprID); inline void cc_terminal (exprID); inline void cc_dot (exprID); inline void cc_array (exprID); inline void cc_star (exprID); inline void cc_addrof (exprID); inline void cc_ecast (exprID); inline void cc_usign (exprID); inline void cc_nbool (exprID); inline void cc_compl (exprID); inline void cc_add (exprID, bool = false); void cc_sub (exprID, bool = false); void cc_muldiv (exprID, bool = false); void cc_bintg (exprID, bool = false); inline void cc_cmp (exprID); void cc_bool (exprID); void cc_conditional (exprID, exprID = -2); void cc_assign (exprID); void cc_oassign (exprID); inline void cc_compound (exprID); public: inline ccsub_small (exprID, bool = false, bool = false); ccsub_small () {} ccsub_small (typeID, char*); int base, spec [MSPEC]; }; ccsub_small ccsub_small::op1; void ccsub_small::cc_terminal (exprID ei) { subexpr e = ee [ei]; lookup_object ll (e.voici.symbol); if (ll.enumconst) { settype (S_INT); return; } base = ll.base; intcpy (spec, ll.spec); if (ll.FRAME <= 0) report (e.voici.symbol, ll.FRAME, modify, array); lvaluate (); } void ccsub_small::cc_addrof (exprID ei) { ccsub_small o (ee [ei].voici.e); base = o.base; if (o.lv || o.structure ()) { spec [0] = '*'; intcpy (&spec [1], o.spec); } else if (o.spec [0] != -1) intcpy (spec, o.spec); else half_error ("&address_of not addressable"); } void ccsub_small::cc_star (exprID e) { ccsub_small o (ee [e].voici.e); degrade (o); lvaluate (); } void ccsub_small::cc_array (exprID ei) { ccsub_small o1 (ee [ei].voici.e, modify, true), o2 (ee [ei].e); cc_addptr (o1, o2); degrade (*this); lvaluate (); } void ccsub_small::cc_dot (exprID ei) { subexpr e = ee [ei]; ccsub_small o (e.voici.e, modify); lookup_member lm (e.voila.member, o.base); base = lm.base; intcpy (spec, lm.spec); report (e.voila.member, o.base, modify, array); lvaluate (); } void ccsub_small::cc_ecast (exprID ei) { subexpr e = ee [ei]; ccsub_small o (e.voici.e), pseudo (e.voila.cast, ""); o.assign_convert (pseudo); copytype (o); *this = o; } void ccsub_small::cc_usign (exprID ei) { subexpr e = ee [ei]; ccsub_small o (e.voici.e); copytype (o); } void ccsub_small::cc_nbool (exprID ei) { ccsub_small o (ee [ei].voici.e); (void) o; settype (S_INT); } void ccsub_small::cc_compl (exprID ei) { ccsub_small o (ee [ei].voici.e); (void) o; settype (S_INT); } void ccsub_small::cc_bintg (exprID ei, bool m1) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e, m1), o2 (e.e); (void) o2; if (op1return) op1 = o1; settype (S_INT); } void ccsub_small::cc_muldiv (exprID ei, bool m1) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e, m1), o2 (e.e); if (op1return) op1 = o1; cc_binwconv (o1, o2); } void ccsub_small::cc_prepostfix (exprID ei) { subexpr e = ee [ei]; ccsub_small o (e.voici.e, true); copytype (o); } void ccsub_small::cc_add (exprID ei, bool m1) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e, m1), o2 (e.e); if (op1return) op1 = o1; if (o1.arithmetic () && o2.arithmetic ()) cc_binwconv (o1, o2); else cc_addptr (o1, o2); } void ccsub_small::cc_sub (exprID ei, bool m1) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e, m1), o2 (e.e); if (op1return) op1 = o1; if (o1.arithmetic () && o2.arithmetic ()) { cc_binwconv (o1, o2); return; } if (!o1.arithmetic () && !o2.arithmetic ()) settype (S_INT); else copytype (o1); } void ccsub_small::cc_cmp (exprID ei) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e), o2 (e.e); if (o1.arithmetic () && o1.arithmetic ()) arithmetic_convert (o1, o2); settype (S_INT); } void ccsub_small::cc_bool (exprID ei) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e); ccsub_small o2 (e.e); (void) o1; (void) o2; settype (S_INT); } void ccsub_small::cc_conditional (exprID ei, exprID args) { subexpr e = ee [ei]; if (args != -2) { // we have the: // (i ? a : b) () // case. Convert it to: // (i ? a () : b ()) ee [NeTop] = ee [e.e]; ee [e.e].action = FCALL; ee [e.e].voici.e = NeTop++; ee [e.e].e = args; ee [NeTop] = ee [e.voila.eelse]; ee [e.voila.eelse].action = FCALL; ee [e.voila.eelse].voici.e = NeTop++; ee [e.voila.eelse].e = args; } ccsub_small o (e.voici.e); ccsub_small o1 (e.e); ccsub_small o2 (e.voila.eelse); ccsub_small *po = e.e==-1 ? &o : &o1; (void) o1; (void) o2; (void) o; if (po->arithmetic () || po->voidp ()) copytype (o2); else copytype (*po); } static void callbacks (exprID e1, exprID e2) { Symbol s1, s2, m1 = -1, m2 = -1, fn = -1, fn1 = -1; bool ptrs = false; int a1 = 0, a2 = 0; again1: if (ee [e1].action == SYMBOL) { --a1; s1 = ee [e1].voici.symbol; if (local_object (s1)) fn1 = current_function; } else if (ee [e1].action == MEMB) { ccsub_small os (ee [e1].voici.e); --a1; s1 = struct_by_name (os.base); m1 = ee [e1].voila.member; } else if (ee [e1].action == ARRAY || ee [e1].action == PTRIND) { e1 = ee [e1].voici.e; a1 = 2; goto again1; } else return; bool casted = false; again: if (ee [e2].action == SYMBOL) { --a2; s2 = ee [e2].voici.symbol; if ((ptrs = !have_function (s2))) { lookup_function lf (s2, false); if (!lf.found) return; if (lf.ARGFUNC) { fn = current_function; // fn = s2; // s2 = current_function; stor_named_conv (s2, fn, lf.displacement); } else if (lf.fptr && lf.FRAME > 0) { // fn = s2; // s2 = current_function; fn = current_function; } } } else if (ee [e2].action == MEMB) { --a2; ccsub_small os (ee [e2].voici.e); s2 = struct_by_name (os.base); m2 = ee [e2].voila.member; } else if (casted = ee [e2].action == CAST) { e2 = ee [e2].voici.e; goto again; } else if (ee [e2].action == ARRAY || ee [e2].action == PTRIND) { e2 = ee [e2].voici.e; a2 = 2; goto again; } else if (ee [e2].action == COND) { callbacks (e1, ee [e2].e == -1 ? ee [e2].voici.e : ee [e2].e); callbacks (e1, ee [e2].voila.eelse); return; } else return; functionptr (s1, s2, ptrs, m1, m2, fn, fn1, a1 == 1, a2 == 1); } void ccsub_small::cc_assign (exprID ei) { subexpr e = ee [ei]; ccsub_small o1 (e.voici.e, true), o2 (e.e); if (o1.spec[0] == '*' && o1.spec [1] == '(') callbacks (e.voici.e, e.e); (void) o2.lv; copytype (o1); } void ccsub_small::cc_oassign (exprID ei) { op1return = true; switch (ee [ei].action) { case ASSIGNA: cc_add (ei, true); break; case ASSIGNS: cc_sub (ei, true); break; case ASSIGNM: case ASSIGND: cc_muldiv (ei, true); break; case ASSIGNBA: case ASSIGNBX: case ASSIGNBO: case ASSIGNRS: case ASSIGNLS: case ASSIGNR: cc_bintg (ei, true); break; } copytype (op1); } static void fargs (exprID ei, Symbol fs, int i) { again: if (ee [ei].action == SYMBOL) { Symbol t = ee [ei].voici.symbol; if (have_function (t)) report_fargval (fs, i, t, -1); else { lookup_function lf (t, false); if (!lf.found) return; if (lf.ARGFUNC) report_fargval (fs, i, current_function, lf.displacement); else if (!local_object (t)) report_fargval (fs, i, t, -1, true); else report_fargval_locf (fs, i, t, current_function); } } else if (ee [ei].action == MEMB) { ccsub_small oo (ee [ei].voici.e); report_fargval_memb (fs, i, struct_by_name (oo.base), ee [ei].voila.member); } else if (ee [ei].action == CAST) { ei = ee [ei].voici.e; goto again; } } void ccsub_small::cc_fcall (exprID ei) { int i = 2; subexpr e = ee [ei]; subexpr fe = ee [e.voici.e]; Symbol fs = -1; if (fe.action == SYMBOL) { lookup_function lf (fe.voici.symbol); fs = fe.voici.symbol; if (!lf.ARGFUNC) report_call (fe.voici.symbol, lf.fptr); else report_fargcall (lf.displacement, fe.voici.symbol); base = lf.base; intcpy (spec, lf.spec + 2); } else if (fe.action == COND) { cc_conditional (e.voici.e, e.e); return; } else { ccsub_small fn (e.voici.e); if (fn.spec [0] != '(') { if (fn.spec [0] == '*' && fn.spec [1] == '(') i = 3; else half_error ("not a function"); } base = fn.base; intcpy (spec, fn.spec + i); if (fe.action == MEMB) { ccsub_small os (fe.voici.e); report_callback (struct_by_name (os.base), fe.voila.member); } else if (fe.action == PTRIND) { if (ee [fe.voici.e].action == MEMB) { subexpr effi = ee [fe.voici.e]; ccsub_small os (effi.voici.e); report_callback (struct_by_name (os.base), effi.voila.member); } else if (ee [fe.voici.e].action == SYMBOL) { lookup_function lf (ee [fe.voici.e].voici.symbol, false); if (!lf.ARGFUNC) report_call (fs = ee [fe.voici.e].voici.symbol, true); else report_fargcall (lf.displacement, ee [fe.voici.e].voici.symbol); } } else report_virtual (); } if ((ei = e.e) != -1) { int i; bool fsok = fs != -1 && fs != current_function; for (i = 0; ee [ei].action == ARGCOMMA; ei = ee [ei].e, i++) { exprID eii = ee [ei].voici.e; ccsub_small o (eii); o.lv = o.lv; if (fsok && o.spec [0] == '*' && o.spec [1] == '(') fargs (eii, fs, i); } ccsub_small o (ei); o.lv = o.lv; if (fsok && o.spec [0] == '*' && o.spec [1] == '(') fargs (ei, fs, i); } } void ccsub_small::cc_compound (exprID ei) { base = base_of (ee [ei].voila.result_type); intcpy (spec, spec_of (ee [ei].voila.result_type)); } //////////////////////////////////////////////////////////////////////////// void ccsub_small::cc_binwconv (ccsub_small &o1, ccsub_small &o2) { arithmetic_convert (o1, o2); settype (o1.base); } void ccsub_small::cc_addptr (ccsub_small &o1, ccsub_small &o2) { bool b2 = o2.arithmetic (); if (b2) { o2.lv = false; copytype (o1); } else { o1.lv = false; copytype (o2); } } void ccsub_small::copytype (ccsub_small &o) { base = o.base; intcpy (spec, o.spec); } void ccsub_small::degrade (ccsub_small &o) { base = o.base; if (o.spec [0] == -1) half_error ("Not a pointer"); intcpy (spec, o.spec + (o.spec [0] == '(' ? 0 : o.spec [0] == '*' ? 1 : 2)); } bool ccsub_small::structure () { return spec [0] == -1 && base >= 0; } bool ccsub_small::arithmetic () { return spec [0] == -1 && base < VOID || spec [0] == ':'; } bool ccsub_small::voidp () { return spec [0] == '*' && base == VOID; } void ccsub_small::lvaluate () { lv = !(spec [0] =='[' || spec [0] ==-1 && base >=0 || spec [0] =='('); } void ccsub_small::settype (int b) { base = b; spec [0] = -1; } void ccsub_small::assign_convert (ccsub_small &o) { if (o.arithmetic ()) if (o.base != FLOAT) iconv (); else fconv (); base = o.base; intcpy (spec, o.spec); } void ccsub_small::arithmetic_convert (ccsub_small &o1, ccsub_small &o2) { if (o1.base == FLOAT || o2.base == FLOAT) { if (o1.base != o2.base) if (o1.base == FLOAT) o2.fconv (); else o1.fconv (); } } void ccsub_small::fconv () { settype (FLOAT); } void ccsub_small::iconv () { settype (S_INT); } //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ccsub_small::ccsub_small (exprID ei, bool mod, bool arr) { modify = mod; array = arr; if (ei == -1) return; advance: subexpr e = ee [ei]; lv = false; op1return = false; switch (e.action) { case VALUE: case UVALUE: settype (S_INT); break; case FVALUE: settype (FLOAT); break; case SVALUE: base = S_CHAR; spec [0] = '*'; spec [1] = -1; break; case AVALUE: base = VOID; spec [0] = '*'; spec [1] = -1; break; case SYMBOL: cc_terminal (ei); break; case FCALL: cc_fcall (ei); break; case MEMB: cc_dot (ei); break; case ARRAY: cc_array (ei); break; case ADDROF: cc_addrof (ei); break; case PTRIND: cc_star (ei); break; case MMPOST: case PPPOST: case PPPRE: case MMPRE: cc_prepostfix (ei); break; case CAST: cc_ecast (ei); break; case LNEG: cc_nbool (ei); break; case OCPL: cc_compl (ei); break; case UPLUS: case UMINUS: cc_usign (ei); break; case SIZEOF: settype (S_INT); break; case MUL: case DIV: cc_muldiv (ei); break; case ADD: cc_add (ei); break; case SUB: cc_sub (ei); break; case SHR: case SHL: case BOR: case BAND: case BXOR: case REM: cc_bintg (ei); break; case IAND: case IOR: cc_bool (ei); break; case BNEQ: case CGR: case CGRE: case CLE: case CLEE: case BEQ: cc_cmp (ei); break; case COND: cc_conditional (ei); break; case COMPOUND_RESULT: cc_compound (ei); break; case COMMA: { ccsub_small o (e.voici.e); ei = e.e; (void) o; goto advance; } default: if (e.action == '=') cc_assign (ei); else cc_oassign (ei); } } ccsub_small::ccsub_small (typeID t, char*) { base = base_of (t); intcpy (spec, spec_of (t)); } // // // // class ncci_usage : public ncci { public: void cc_expression (); void new_function (Symbol); void inline_assembly (NormPtr, int); void finir (); }; void ncci_usage::cc_expression () { try { if (CExpr.first != -1) { ccsub_small CC (CExpr.first); #ifdef GNU_VIOLATIONS last_result_type.base = CC.base; intcpy (last_result_type.spec, CC.spec); #endif } } catch (EXPR_ERROR) { } last_result++; } void ncci_usage::new_function (Symbol s) { newfunction (s); } void ncci_usage::inline_assembly (NormPtr p, int n) { // hoping that inline assembly won't use global variables, // nor call functions, it is ignored (for now at least) } void ncci_usage::finir () { report_fptrs (); report_named_convert (); report_argument_calls (); } void set_usage_report () { ncc = new ncci_usage; } ncc-2.8/mem_pool.h0000644000175000001440000000435310157262427014245 0ustar stanusers00000000000000//-------------------------------------------------------------------------- // // arrays of automatically increasing size. // // earray for few elements, reallocation + memcpy every 64 // mem_pool for up to a LOT of elements (64*128*256) // //------------------------------------------------------------------------- #ifndef mem_pool_INCLUDED #define mem_pool_INCLUDED #include #define TXX template TXX class mem_pool { #define PSEG 256 #define NSEG 128 #define TSEG 64 X ***p; int sp; public: mem_pool (); int alloc (); inline X &operator [] (int); void pop (); int nr () { return sp; } void copy (X**); void destroy (); }; #define I1A(x) (x / (PSEG*NSEG)) #define I2A(x) ((x % (PSEG*NSEG)) / PSEG) #define I3A(x) ((x % (PSEG*NSEG)) % PSEG) TXX mem_pool::mem_pool () { sp = 0; p = new X** [TSEG]; } TXX int mem_pool::alloc () { if (sp % (PSEG*NSEG) == 0) p [I1A (sp)] = new X* [NSEG]; if (sp % PSEG == 0) { p [I1A (sp)] [I2A (sp)] = new X [PSEG]; memset (p [I1A (sp)] [I2A (sp)], 0, PSEG * sizeof (X)); } return sp++; } TXX X &mem_pool::operator [] (int i) { return p [I1A (i)] [I2A (i)] [I3A (i)]; } TXX void mem_pool::pop () { if (sp) sp--; } TXX void mem_pool::copy (X **di) { int i; X *d; if (sp == 0) return; if (*di == NULL) *di = new X [sp]; d = *di; for (i = 0; i + PSEG < sp; i += PSEG) memcpy (&d [i], p [I1A(i)][I2A(i)], PSEG * sizeof (X)); memcpy (&d [i], p [I1A(i)][I2A(i)], (sp - i) * sizeof (X)); } TXX void mem_pool::destroy () { int i; for (i = 0; i < sp; i += PSEG) delete [] p [I1A (i)] [I2A (i)]; for (i = 0; i < sp; i += PSEG*NSEG) delete [] p [I1A (i)]; delete [] p; } TXX class earray { #define SSEG 64 int ms; X *Realloc (int); public: int nr; X *x; earray (); inline int alloc (); void freeze (); void erase (); }; TXX earray::earray () { ms = nr = 0; x = NULL; } TXX X *earray::Realloc (int s) { X *r = new X [s]; if (x) { memcpy (r, x, nr * sizeof (X)); delete [] x; } return x = r; } TXX int earray::alloc () { if (ms == nr) x = Realloc (ms += SSEG); return nr++; } TXX void earray::freeze () { if (nr) x = Realloc (ms = nr); } TXX void earray::erase () { if (x) delete [] x; x = NULL; ms = nr = 0; } #endif ncc-2.8/cexpand.C0000644000175000001440000001041211074137374014005 0ustar stanusers00000000000000/****************************************************************************** $ Expands tokens from the normalized C source to readable strings, $ and other similar routines representing internal compiler data. $ The inverse of lexical analyser ******************************************************************************/ #include #include #include #include "global.h" int cfile_of (NormPtr p) { int s = 0, e = C_Nfiles, m; while (e - s > 1) if (C_Files [m = (e + s) / 2].indx < p) s = m; else e = m; return s; } int cline_of (NormPtr p) { int s = 0, e = C_Nlines, m; while (e - s > 1) if (C_Lines [m = (e + s) / 2].ftok < p) s = m; else e = m; return C_Lines [s].line; } char *in_file (NormPtr p) { return C_Files [cfile_of (p)].file; } #define rcase(x) case RESERVED_ ## x: strcpy (tmp, #x); break #define ocase(x, y) case x: strcpy (tmp, y); break char *expand (int token) { static char tmp [128]; if (token < 0) return "internal BUG"; if (ISSYMBOL (token)) { if (SYMBOLID (token) > C_Nsyms) return strcpy (tmp, "***Fatal Flaw**"); return strcpy (tmp, C_Syms [SYMBOLID (token)]); } if (ISSTRING (token)) snprintf (tmp, sizeof tmp, "\"%s\"", C_Strings [token - STRINGBASE]); else if (ISNUMBER (token)) { if (token >= INUMBER) sprintf (tmp, "%i", token - INUMBER); else if (token >= FLOATBASE) sprintf (tmp, "%e", C_Floats [token - FLOATBASE]); else if (token >= UINT32BASE) sprintf (tmp, "%lu", C_Unsigned [token - UINT32BASE]); else if (token >= INT32BASE) sprintf (tmp, "%li", C_Ints [token - INT32BASE]); else if (token >= INT16BASE) sprintf (tmp, "%i", C_Shortints [token - INT16BASE]); else sprintf (tmp, "%i", C_Chars [token - INT8BASE]); } else if (token < 127) sprintf (tmp, "%c", token); else if (ISOPERATOR(token)) switch (token) { ocase (ELLIPSIS, "..."); ocase (POINTSAT, "->"); ocase (MINUSMINUS, "--"); ocase (ASSIGNA, "+="); ocase (ASSIGNS, "-="); ocase (ASSIGNM, "*="); ocase (ASSIGND, "/="); ocase (ASSIGNR, "%="); ocase (ASSIGNBA, "&="); ocase (ASSIGNBO, "|="); ocase (ASSIGNBX, "^="); ocase (ASSIGNRS, ">>="); ocase (ASSIGNLS, "<<="); ocase (PLUSPLUS, "++"); ocase (GEQCMP, ">="); ocase (LSH, "<<"); ocase (OROR, "||"); ocase (ANDAND, "&&"); ocase (EQCMP, "=="); ocase (NEQCMP, "!="); ocase (RSH, ">>"); ocase (LEQCMP, "<="); default: strcpy (tmp, "n/A\n"); } else switch (token) { rcase (case); ocase (FORCEERROR, "//-*-* END OF THE FILE *-*-"); rcase (auto); rcase (__asm__); rcase (const); rcase (extern); rcase (inline); rcase (long); rcase (register); rcase (short); rcase (signed); rcase (static); rcase (typedef); rcase (unsigned); rcase (volatile); rcase (void); rcase (char); rcase (int); rcase (float); rcase (double); rcase (struct); rcase (union); rcase (continue); rcase (do); rcase (enum); rcase (for); rcase (goto); rcase (if); rcase (else); rcase (return); #ifdef GNU_VIOLATIONS rcase (__label__); rcase (__typeof__); #endif rcase (sizeof); rcase (switch); rcase (while); rcase (break); rcase (default); default: strcpy (tmp, "n/A\n"); } return tmp; } void debug (const char *s, NormPtr i, int j) { FILE *of = stderr; if (i < 0) i = 0; if (i + j >= C_Ntok) j = C_Ntok - i - 1; fprintf (of, "%s (%i): %s\"", in_file (i+j/2), cline_of (i+j/2), s); while (j--) fprintf (of, "%s ", expand (CODE [i++])); fprintf (of, "\"\n"); } void prcode (NormPtr i, int j) { PRINTF ("#expression: "); if (i < 0) i = 0; if (i + j > C_Ntok) j = C_Ntok - i; while (j--) { bool b = ISSYMBOL (CODE [i]) || ISRESERVED (CODE [i]); PRINTF ("%s", expand (CODE [i++])); if (b && (ISSYMBOL (CODE [i]) || ISRESERVED (CODE [i]))) PRINTF (" "); } PRINTF ("\n"); } void prcode (NormPtr i, int j, Symbol p[]) { PRINTF ("#initialization expression: "); for (int c = 0; p [c] != ';'; c++) PRINTF ("%s", expand (p [c])); PRINTF (" = "); if (i < 0) i = 0; if (i + j > C_Ntok) j = C_Ntok - i; while (j--) { bool b = ISSYMBOL (CODE [i]) || ISRESERVED (CODE [i]); PRINTF ("%s", expand (CODE [i++])); if (b && (ISSYMBOL (CODE [i]) || ISRESERVED (CODE [i]))) PRINTF (" "); } PRINTF ("\n"); } void prcode (NormPtr i, int j, Symbol p) { Symbol pp [2] = { p, ';' }; prcode (i, j, pp); } ncc-2.8/COPYING0000777000175000001440000000000010545510651015056 2doc/LICENSEustar stanusers00000000000000ncc-2.8/dbstree.C0000644000175000001440000000176410153240200014001 0ustar stanusers00000000000000/****************************************************************************** dbstree.C Dynamic Binary Search Tree used throughout the entire project. A dbsNode applied as a parent class to a specific class will provide transparent storage in a dbstree. As a result no duplicates may be stored and locating stored data is performed in less than 32 steps. Check dbstree.tex for technical information on this tree. *****************************************************************************/ #include #include #include #include "dbstree.h" //*************************************************************** // A tree of case sensitive strings -- char *Name //*************************************************************** char *dbsNodeStr::Query; int dbsNodeStr::compare (dbsNodeStr *n) { return strcmp (Name, n->Name); } int dbsNodeStr::compare () { return strcmp (Name, Query); } dbsNodeStr::dbsNodeStr () { Name = Query; } dbsNodeStr::~dbsNodeStr () { } ncc-2.8/dbstree.h0000644000175000001440000001355710454206506014070 0ustar stanusers00000000000000/****************************************************************************** Dynamic Binary Search Tree. Check dbstree.tex for info. ******************************************************************************/ #include #ifndef HAVE_DBSTREE #define HAVE_DBSTREE #define DBS_MAGIC 32 extern char *StrDup (char*); #define TX template template class dbsTree { dbsNode **FoundSlot; int FoundDepth; void tree_to_array (dbsNode*); void walk_tree (dbsNode*, void (*)(dbsNode*)); void walk_tree_io (dbsNode*, void (*)(dbsNode*)); void walk_tree_d (dbsNode*, void (*)(dbsNode*)); public: dbsNode *root; int nnodes; dbsTree (); void dbsBalance (); dbsNode* dbsFind (); void dbsRemove (dbsNode*); void copytree (void (*)(dbsNode*)); void foreach (void (*)(dbsNode*)); void deltree (void (*)(dbsNode*)); dbsNode* parentOf (dbsNode*); void addself (dbsNode*); }; #define DBS_STRQUERY dbsNodeStr::Query class dbsNodeStr { public: int compare (dbsNodeStr*); int compare (); dbsNodeStr (); char *Name; ~dbsNodeStr (); static char *Query; }; TX dbsTree::dbsTree () { nnodes = 0; root = NULL; FoundSlot = NULL; } /* * Balancing a binary tree. This happens whenever the height reaches 32. */ TX void dbsTree::tree_to_array (dbsNode *n) { if (n->less) tree_to_array (n->less); *FoundSlot++ = n; if (n->more) tree_to_array (n->more); } TX void dbsTree::dbsBalance () // O(n) { dbsNode **npp; unsigned long long i, j, k, D, Y, k2, k3; if (!root) return; npp = FoundSlot = (dbsNode**) alloca (sizeof (dbsNode*) * nnodes); tree_to_array (root); root = npp [nnodes / 2]; for (D = nnodes + 1, i = 4; i <= D; i *= 2) for (j = 2; j < i; j += 4) { k3 = nnodes * j / i; npp [k3]->less = npp [nnodes * (j - 1) / i], npp [k3]->more = npp [nnodes * (j + 1) / i]; } k = nnodes + 1 - (Y = i / 2); if (k == 0) { for (i /=2, j = 1; j < i; j += 2) k3 = nnodes * j / i, npp [k3]->less = npp [k3]->more = NULL; return; } for (j = 2; j < i; j += 4) { k3 = nnodes * j / i; D = (k2 = (j - 1) * nnodes / i) * Y % nnodes; if (D >= k || D == 0) npp [k3]->less = NULL; else { npp [k3]->less = npp [k2]; npp [k2]->less = npp [k2]->more = NULL; } D = (k2 = (j + 1) * nnodes / i) * Y % nnodes; if (D >= k || D == 0) npp [k3]->more = NULL; else { npp [k3]->more = npp [k2]; npp [k2]->less = npp [k2]->more = NULL; } } dbsNode *np; for (np = root; np->less; np = np->less); np->less = npp [0]; npp [0]->less = npp [0]->more = NULL; } TX void dbsTree::addself (dbsNode *t) { t->less = t->more = 0; *FoundSlot = t; ++nnodes; if (FoundDepth >= DBS_MAGIC) dbsBalance (); FoundSlot = NULL; // Bug traper } /* */ TX void dbsTree::dbsRemove (dbsNode *t) // O(log n) { dbsNode *np, *nl, *nr, *nlp, *nrp; int isroot; unsigned int i, j; isroot = (np = parentOf (t)) == NULL; --nnodes; if (!(t->less && t->more)) { if (isroot) root = (t->less) ? t->less : t->more; else if (np->less == t) np->less = (t->less) ? t->less : t->more; else np->more = (t->less) ? t->less : t->more; return; } for (i = 0, nlp = NULL, nl = t->less; nl->more; i++) nlp = nl, nl = nl->more; for (j = 0, nrp = NULL, nr = t->more; nr->less; j++) nrp = nr, nr = nr->less; if (i >= j) // the smallest from bigger ones { if (isroot) root = nl; else if (np->less == t) np->less = nl; else np->more = nl; if (nlp) { nlp->more = nl->less; nl->less = t->less; } nl->more = t->more; } else // Mirror situation { if (isroot) root = nr; else if (np->less == t) np->less = nr; else np->more = nr; if (nrp) { nrp->less = nr->more; nr->more = t->more; } nr->less = t->less; } } TX dbsNode *dbsTree::parentOf (dbsNode *t) // O(log n) { dbsNode *np; if ((np = root) == t) return NULL; while (np) if (t->compare (np) < 0) if (np->less == t) break; else np = np->less; else if (np->more == t) break; else np = np->more; assert (np); return np; } TX dbsNode *dbsTree::dbsFind () // O (log n) { dbsNode *d; int i; FoundDepth = 0; if (!(d = root)) { FoundSlot = &root; return NULL; } ++FoundDepth; for (;; ++FoundDepth) { if ((i = d->compare ()) == 0) { FoundSlot = NULL; return d; } if (i < 0) if (d->more) d = d->more; else { FoundSlot = &d->more; return NULL; } else if (d->less) d = d->less; else { FoundSlot = &d->less; return NULL; } } } /* * Moving in the tree. * If we want for every node of the tree: foreach () O(n) * To copy the tree - preorder: copytree () O(n) * Safe to node deletion (but no dbsRemove, just * root=NULL, cnt=0) - postorder: deltree () O(n) * To a specific index: operator [i] O(n) ...(n/16) * But for every node with operator[] total is ... n^2 CAREFUL! * Inorder next/prev dbsNext, dbsPrev: O(log n) * For a scroller area Use dbsNext, dbsPrev and keep a sample node * if the tree is modified, reget the sample node from operator []. * */ TX void dbsTree::walk_tree (dbsNode *n, void (*foo)(dbsNode*)) { foo (n); if (n->less) walk_tree (n->less, foo); if (n->more) walk_tree (n->more, foo); } TX void dbsTree::walk_tree_io (dbsNode *n, void (*foo)(dbsNode*)) { if (n->less) walk_tree_io (n->less, foo); foo (n); if (n->more) walk_tree_io (n->more, foo); } TX void dbsTree::walk_tree_d (dbsNode *n, void (*foo)(dbsNode*)) { if (n->less) walk_tree_d (n->less, foo); if (n->more) walk_tree_d (n->more, foo); foo (n); } TX void dbsTree::copytree (void (*f)(dbsNode*)) { if (root) walk_tree (root, f); } TX void dbsTree::foreach (void (*f)(dbsNode*)) { if (root) walk_tree_io (root, f); } TX void dbsTree::deltree (void (*f)(dbsNode*)) { if (root) walk_tree_d (root, f); } /* * Indexed by inorder access */ #endif