Imported Upstream version 15.8a
[platform/upstream/cscope.git] / src / crossref.c
1 /*===========================================================================
2  Copyright (c) 1998-2000, The Santa Cruz Operation 
3  All rights reserved.
4  
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  *Redistributions of source code must retain the above copyright notice,
9  this list of conditions and the following disclaimer.
10
11  *Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14
15  *Neither name of The Santa Cruz Operation nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission. 
18
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  DAMAGE. 
31  =========================================================================*/
32
33
34 /*      cscope - interactive C symbol cross-reference
35  *
36  *      build cross-reference file
37  */
38
39 #include "global.h"
40
41 #include "build.h"
42 #include "scanner.h"
43 #include "alloc.h"
44
45 #include <stdlib.h>
46 #include <sys/stat.h>
47
48 static char const rcsid[] = "$Id: crossref.c,v 1.15 2009/08/28 14:28:27 nhorman Exp $";
49
50
51 /* convert long to a string */
52 #define ltobase(value)  n = value; \
53                         s = buf + (sizeof(buf) - 1); \
54                         *s = '\0'; \
55                         digits = 1; \
56                         while (n >= BASE) { \
57                                 ++digits; \
58                                 i = n; \
59                                 n /= BASE; \
60                                 *--s = i - n * BASE + '!'; \
61                         } \
62                         *--s = n + '!';
63
64 #define SYMBOLINC       20      /* symbol list size increment */
65
66 long    dboffset;               /* new database offset */
67 BOOL    errorsfound;            /* prompt before clearing messages */
68 long    lineoffset;             /* source line database offset */
69 long    npostings;              /* number of postings */
70 int     nsrcoffset;             /* number of file name database offsets */
71 long    *srcoffset;             /* source file name database offsets */
72 unsigned long symbols;          /* number of symbols */
73
74 static  char    *filename;      /* file name for warning messages */
75 static  long    fcnoffset;      /* function name database offset */
76 static  long    macrooffset;    /* macro name database offset */
77 static  unsigned long msymbols = SYMBOLINC; /* maximum number of symbols */
78
79 struct  symbol {        /* symbol data */
80     int type;           /* type */
81     unsigned int first;         /* index of first character in text */
82     unsigned int last;          /* index of last+1 character in text */
83     unsigned int length;        /* symbol length */
84     unsigned int fcn_level;     /* function level of the symbol */
85 };
86 static struct symbol *symbol;
87
88 static  void    putcrossref(void);
89 static  void    savesymbol(int token, int num);
90
91 void
92 crossref(char *srcfile)
93 {
94     unsigned int i;
95     unsigned int length;        /* symbol length */
96     unsigned int entry_no;      /* function level of the symbol */
97     int token;                  /* current token */
98     struct stat st;
99
100     if (! ((stat(srcfile, &st) == 0)
101            && S_ISREG(st.st_mode))) {
102         cannotopen(srcfile);
103         errorsfound = YES;
104         return;
105     }
106         
107     entry_no = 0;
108     /* open the source file */
109     if ((yyin = myfopen(srcfile, "r")) == NULL) {
110         cannotopen(srcfile);
111         errorsfound = YES;
112         return;
113     }
114     filename = srcfile; /* save the file name for warning messages */
115     putfilename(srcfile);       /* output the file name */
116     dbputc('\n');
117     dbputc('\n');
118
119     /* read the source file */
120     initscanner(srcfile);
121     fcnoffset = macrooffset = 0;
122     symbols = 0;
123     if (symbol == NULL) {
124         symbol = mymalloc(msymbols * sizeof(struct symbol));
125     }
126     for (;;) {
127                 
128         /* get the next token */
129         switch (token = yylex()) {
130         default:
131             /* if requested, truncate C symbols */
132             length = last - first;
133             if (trun_syms == YES && length > 8 &&
134                 token != INCLUDE && token != NEWFILE) {
135                 length = 8;
136                 last = first + 8;
137             }
138             /* see if the token has a symbol */
139             if (length == 0) {
140                 savesymbol(token, entry_no);
141                 break;
142             }
143             /* update entry_no if see function entry */
144             if (token == FCNDEF) {
145                 entry_no++;
146             }
147             /* see if the symbol is already in the list */
148             for (i = 0; i < symbols; ++i) {
149                 if (length == symbol[i].length
150                     && strncmp(my_yytext + first,
151                                my_yytext + symbol[i].first,
152                                length) == 0 
153                     && entry_no == symbol[i].fcn_level
154                     && token == symbol[i].type
155                     ) { /* could be a::a() */
156                     break;
157                 }
158             }
159             if (i == symbols) { /* if not already in list */
160                 savesymbol(token, entry_no);
161             }
162             break;
163
164         case NEWLINE:   /* end of line containing symbols */
165             entry_no = 0;       /* reset entry_no for each line */
166 #ifdef USING_LEX
167             --yyleng;   /* remove the newline */
168 #endif
169             putcrossref();      /* output the symbols and source line */
170             lineno = myylineno; /* save the symbol line number */
171 #ifndef USING_LEX
172             /* HBB 20010425: replaced yyleng-- by this chunk: */
173             if (my_yytext)
174                 *my_yytext = '\0';
175             my_yyleng = 0;
176 #endif
177             break;
178                         
179         case LEXERR:    /* Lexer error, abort further parsing of this file */
180         case LEXEOF:    /* end of file; last line may not have \n */
181                         
182                         /* if there were symbols, output them and the source line */
183             if (symbols > 0) {
184                 putcrossref();
185             }
186             (void) fclose(yyin);        /* close the source file */
187
188             /* output the leading tab expected by the next call */
189             dbputc('\t');
190             return;
191         }
192     }
193 }
194
195 /* save the symbol in the list */
196
197 static void
198 savesymbol(int token, int num)
199 {
200     /* make sure there is room for the symbol */
201     if (symbols == msymbols) {
202         msymbols += SYMBOLINC;
203         symbol = myrealloc(symbol, msymbols * sizeof(struct symbol));
204     }
205     /* save the symbol */
206     symbol[symbols].type = token;
207     symbol[symbols].first = first;
208     symbol[symbols].last = last;
209     symbol[symbols].length = last - first;
210     symbol[symbols].fcn_level = num;
211     ++symbols;
212 }
213
214 /* output the file name */
215
216 void
217 putfilename(char *srcfile)
218 {
219         /* check for file system out of space */
220         /* note: dbputc is not used to avoid lint complaint */
221         if (putc(NEWFILE, newrefs) == EOF) {
222                 cannotwrite(newreffile);
223                 /* NOTREACHED */
224         }
225         ++dboffset;
226         if (invertedindex == YES) {
227                 srcoffset[nsrcoffset++] = dboffset;
228         }
229         dbfputs(srcfile);
230         fcnoffset = macrooffset = 0;
231 }
232
233 /* output the symbols and source line */
234
235 static void
236 putcrossref(void)
237 {
238     unsigned int i, j;
239     unsigned char c;
240     BOOL    blank;          /* blank indicator */
241     unsigned int symput = 0;     /* symbols output */
242     int     type;
243
244     /* output the source line */
245     lineoffset = dboffset;
246     dboffset += fprintf(newrefs, "%d ", lineno);
247 #ifdef PRINTF_RETVAL_BROKEN
248     dboffset = ftell(newrefs); /* fprintf doesn't return chars written */
249 #endif
250
251     /* HBB 20010425: added this line: */
252     my_yytext[my_yyleng] = '\0';
253
254     blank = NO;
255     for (i = 0; i < my_yyleng; ++i) {
256                 
257         /* change a tab to a blank and compress blanks */
258         if ((c = my_yytext[i]) == ' ' || c == '\t') {
259             blank = YES;
260         } else if (symput < symbols && i == symbol[symput].first) {
261             /* look for the start of a symbol */
262
263             /* check for compressed blanks */
264             if (blank == YES) {
265                 blank = NO;
266                 dbputc(' ');
267             }
268             dbputc('\n');       /* symbols start on a new line */
269                         
270             /* output any symbol type */
271             if ((type = symbol[symput].type) != IDENT) {
272                 dbputc('\t');
273                 dbputc(type);
274             } else {
275                 type = ' ';
276             }
277             /* output the symbol */
278             j = symbol[symput].last;
279             c = my_yytext[j];
280             my_yytext[j] = '\0';
281             if (invertedindex == YES) {
282                 putposting(my_yytext + i, type);
283             }
284             writestring(my_yytext + i);
285             dbputc('\n');
286             my_yytext[j] = c;
287             i = j - 1;
288             ++symput;
289         } else {
290             /* HBB: try to save some time by early-out handling of 
291              * non-compressed mode */
292             if (compress == NO) {
293                 if (blank == YES) {
294                     dbputc(' ');
295                     blank = NO;
296                 }
297                 j = i + strcspn(my_yytext+i, "\t ");
298                 if (symput < symbols
299                     && j >= symbol[symput].first)
300                     j = symbol[symput].first;
301                 c = my_yytext[j];
302                 my_yytext[j] = '\0';
303                 writestring(my_yytext + i);
304                 my_yytext[j] = c;
305                 i = j - 1;
306                 /* finished this 'i', continue with the blank */
307                 continue;
308             }
309
310             /* check for compressed blanks */
311             if (blank == YES) {
312                 if (dicode2[c]) {
313                     c = DICODE_COMPRESS(' ', c);
314                 } else {
315                     dbputc(' ');
316                 }
317             } else if (IS_A_DICODE(c, my_yytext[i + 1])
318                        && symput < symbols
319                        && i + 1 != symbol[symput].first) {
320                 /* compress digraphs */
321                 c = DICODE_COMPRESS(c, my_yytext[i + 1]);
322                 ++i;
323             }
324             dbputc((int) c);
325             blank = NO;
326                         
327             /* skip compressed characters */
328             if (c < ' ') {
329                 ++i;
330                                 
331                 /* skip blanks before a preprocesor keyword */
332                 /* note: don't use isspace() because \f and \v
333                    are used for keywords */
334                 while ((j = my_yytext[i]) == ' ' || j == '\t') {
335                     ++i;
336                 }
337                 /* skip the rest of the keyword */
338                 while (isalpha((unsigned char)my_yytext[i])) {
339                     ++i;
340                 }
341                 /* skip space after certain keywords */
342                 if (keyword[c].delim != '\0') {
343                     while ((j = my_yytext[i]) == ' ' || j == '\t') {
344                         ++i;
345                     }
346                 }
347                 /* skip a '(' after certain keywords */
348                 if (keyword[c].delim == '('
349                     && my_yytext[i] == '(') {
350                     ++i;
351                 }
352                 --i;    /* compensate for ++i in for() */
353             } /* if compressed char */
354         } /* else: not a symbol */
355     } /* for(i) */
356
357     /* ignore trailing blanks */
358     dbputc('\n');
359     dbputc('\n');
360
361     /* output any #define end marker */
362     /* note: must not be part of #define so putsource() doesn't discard it
363        so findcalledbysub() can find it and return */
364     if (symput < symbols && symbol[symput].type == DEFINEEND) {
365         dbputc('\t');
366         dbputc(DEFINEEND);
367         dbputc('\n');
368         dbputc('\n');   /* mark beginning of next source line */
369         macrooffset = 0;
370     }
371     symbols = 0;
372 }
373
374 /* HBB 20000421: new function, for avoiding memory leaks */
375 /* free the cross reference symbol table */
376 void
377 freecrossref()
378 {
379         if (symbol)
380                 free(symbol);
381         symbol = NULL;
382         symbols = 0;
383 }
384
385 /* output the inverted index posting */
386
387 void
388 putposting(char *term, int type)
389 {
390         long    i, n;
391         char    *s;
392         int     digits;         /* digits output */
393         long    offset;         /* function/macro database offset */
394         char    buf[11];                /* number buffer */
395
396         /* get the function or macro name offset */
397         offset = fcnoffset;
398         if (macrooffset != 0) {
399                 offset = macrooffset;
400         }
401         /* then update them to avoid negative relative name offset */
402         switch (type) {
403         case DEFINE:
404                 macrooffset = dboffset;
405                 break;
406         case DEFINEEND:
407                 macrooffset = 0;
408                 return;         /* null term */
409         case FCNDEF:
410                 fcnoffset = dboffset;
411                 break;
412         case FCNEND:
413                 fcnoffset = 0;
414                 return;         /* null term */
415         }
416         /* ignore a null term caused by a enum/struct/union without a tag */
417         if (*term == '\0') {
418                 return;
419         }
420         /* skip any #include secondary type char (< or ") */
421         if (type == INCLUDE) {
422                 ++term;
423         }
424         /* output the posting, which should be as small as possible to reduce
425            the temp file size and sort time */
426         (void) fputs(term, postings);
427         (void) putc(' ', postings);
428
429         /* the line offset is padded so postings for the same term will sort
430            in ascending line offset order to order the references as they
431            appear withing a source file */
432         ltobase(lineoffset);
433         for (i = PRECISION - digits; i > 0; --i) {
434                 (void) putc('!', postings);
435         }
436         do {
437                 (void) putc(*s, postings);
438         } while (*++s != '\0');
439         
440         /* postings are also sorted by type */
441         (void) putc(type, postings);
442         
443         /* function or macro name offset */
444         if (offset > 0) {
445                 (void) putc(' ', postings);
446                 ltobase(offset);
447                 do {
448                         (void) putc(*s, postings);
449                 } while (*++s != '\0');
450         }
451         if (putc('\n', postings) == EOF) {
452                 cannotwrite(temp1);
453                 /* NOTREACHED */
454         }
455         ++npostings;
456 }
457
458 /* put the string into the new database */
459
460 void
461 writestring(char *s)
462 {
463         unsigned char c;
464         int     i;
465         
466         if (compress == NO) {
467                 /* Save some I/O overhead by using puts() instead of putc(): */
468                 dbfputs(s);
469                 return;
470         } 
471         /* compress digraphs */
472         for (i = 0; (c = s[i]) != '\0'; ++i) {
473                 if (/* dicode1[c] && dicode2[(unsigned char) s[i + 1]] */
474                     IS_A_DICODE(c, s[i + 1])) {
475                         /* c = (0200 - 2) + dicode1[c] + dicode2[(unsigned char) s[i + 1]]; */
476                         c = DICODE_COMPRESS(c, s[i + 1]);
477                         ++i;
478                 }
479                 dbputc(c);      
480         }
481 }
482
483 /* print a warning message with the file name and line number */
484
485 void
486 warning(char *text)
487 {
488         
489         (void) fprintf(stderr, "cscope: \"%s\", line %d: warning: %s\n", filename, 
490                 myylineno, text);
491         errorsfound = YES;
492 }