1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /* Copyright (c) 1988 AT&T */
23 /* All Rights Reserved */
24
25
26 /*
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 /*
34 * cscope - interactive C symbol cross-reference
35 *
36 * build cross-reference file
37 */
38
39 #include "global.h"
40
41 /* convert long to a string */
42 #define ltobase(value) n = value; \
43 s = buf + (sizeof (buf) - 1); \
44 *s = '\0'; \
45 digits = 1; \
46 while (n >= BASE) { \
47 ++digits; \
48 i = n; \
49 n /= BASE; \
50 *--s = i - n * BASE + '!'; \
51 } \
52 *--s = n + '!';
53
54 #define SYMBOLINC 20 /* symbol list size increment */
55 #define FREAD "r" /* fopen for reading */
56
57 long dboffset; /* new database offset */
58 BOOL errorsfound; /* prompt before clearing messages */
59 long fileindex; /* source file name index */
60 long lineoffset; /* source line database offset */
61 long npostings; /* number of postings */
62 int nsrcoffset; /* number of file name database offsets */
63 long *srcoffset; /* source file name database offsets */
64 int symbols; /* number of symbols */
65
66 static char *filename; /* file name for warning messages */
67 static long fcnoffset; /* function name database offset */
68 static long macrooffset; /* macro name database offset */
69 static int msymbols = SYMBOLINC; /* maximum number of symbols */
70 static struct symbol { /* symbol data */
71 int type; /* type */
72 int first; /* index of first character in text */
73 int last; /* index of last+1 character in text */
74 int length; /* symbol length */
75 } *symbol;
76
77 static void putcrossref(void);
78
79 void
crossref(char * srcfile)80 crossref(char *srcfile)
81 {
82 int i;
83 int length; /* symbol length */
84 int token; /* current token */
85
86 /* open the source file */
87 if ((yyin = vpfopen(srcfile, FREAD)) == NULL) {
88 cannotopen(srcfile);
89 errorsfound = YES;
90 return;
91 }
92 filename = srcfile; /* save the file name for warning messages */
93 putfilename(srcfile); /* output the file name */
94 dbputc('\n');
95 dbputc('\n');
96
97 /* read the source file */
98 initscanner(srcfile);
99 fcnoffset = macrooffset = 0;
100 symbols = 0;
101 if (symbol == NULL) {
102 symbol = mymalloc(msymbols * sizeof (struct symbol));
103 }
104 for (;;) {
105
106 /* get the next token */
107 switch (token = yylex()) {
108 default:
109 /* if requested, truncate C symbols */
110 length = last - first;
111 if (truncatesyms && length > 8 &&
112 token != INCLUDE && token != NEWFILE) {
113 length = 8;
114 last = first + 8;
115 }
116 /* see if the token has a symbol */
117 if (length == 0) {
118 savesymbol(token);
119 break;
120 }
121 /* see if the symbol is already in the list */
122 for (i = 0; i < symbols; ++i) {
123 if (length == symbol[i].length &&
124 strncmp(yytext + first, yytext +
125 symbol[i].first, length) == 0 &&
126 (token == IDENT ||
127 token == symbol[i].type)) {
128 first = yyleng;
129 break;
130 }
131 }
132 if (i == symbols) { /* if not already in list */
133 savesymbol(token);
134 }
135 break;
136
137 case NEWLINE: /* end of line containing symbols */
138 --yyleng; /* remove the newline */
139 putcrossref(); /* output the symbols and source line */
140 lineno = yylineno; /* save the symbol line number */
141 break;
142
143 case LEXEOF: /* end of file; last line may not have \n */
144
145 /*
146 * if there were symbols, output them and the
147 * source line
148 */
149 if (symbols > 0) {
150 putcrossref();
151 }
152 (void) fclose(yyin); /* close the source file */
153
154 /* output the leading tab expected by the next call */
155 dbputc('\t');
156 return;
157 }
158 }
159 }
160
161 /* save the symbol in the list */
162
163 void
savesymbol(int token)164 savesymbol(int token)
165 {
166 /* make sure there is room for the symbol */
167 if (symbols == msymbols) {
168 msymbols += SYMBOLINC;
169 symbol = (struct symbol *)myrealloc(symbol,
170 msymbols * sizeof (struct symbol));
171 }
172 /* save the symbol */
173 symbol[symbols].type = token;
174 symbol[symbols].first = first;
175 symbol[symbols].last = last;
176 symbol[symbols].length = last - first;
177 ++symbols;
178 first = yyleng;
179 }
180
181 /* output the file name */
182
183 void
putfilename(char * srcfile)184 putfilename(char *srcfile)
185 {
186 /* check for file system out of space */
187 /* note: dbputc is not used to avoid lint complaint */
188 if (putc(NEWFILE, newrefs) == EOF) {
189 cannotwrite(newreffile);
190 /* NOTREACHED */
191 }
192 ++dboffset;
193 if (invertedindex) {
194 srcoffset[nsrcoffset++] = dboffset;
195 }
196 dbfputs(srcfile);
197 fcnoffset = macrooffset = 0;
198 }
199
200 /* output the symbols and source line */
201
202 static void
putcrossref(void)203 putcrossref(void)
204 {
205 int i, j;
206 unsigned c;
207 BOOL blank = NO; /* output blank */
208 BOOL newline = NO; /* output newline */
209 int symput = 0; /* symbols output */
210 int type;
211
212 /* output the source line */
213 lineoffset = dboffset;
214 dbfprintf(newrefs, "%d ", lineno);
215 for (i = 0; i < yyleng; ++i) {
216
217 /* change a tab to a blank and compress blanks */
218 if ((c = yytext[i]) == ' ' || c == '\t') {
219 blank = YES;
220 }
221 /* look for the start of a symbol */
222 else if (symput < symbols && i == symbol[symput].first) {
223
224 /* check for compressed blanks */
225 if (blank) {
226 blank = NO;
227 if (newline) {
228 dbputc('\n');
229 }
230 dbputc(' ');
231 }
232 dbputc('\n'); /* symbols start on a new line */
233
234 /* output any symbol type */
235 if ((type = symbol[symput].type) != IDENT) {
236 dbputc('\t');
237 dbputc(type);
238 } else {
239 type = ' ';
240 }
241 /* output the symbol */
242 j = symbol[symput].last;
243 c = yytext[j];
244 yytext[j] = '\0';
245 if (invertedindex) {
246 putposting(yytext + i, type);
247 }
248 putstring(yytext + i);
249 newline = YES;
250 yytext[j] = (char)c;
251 i = j - 1;
252 ++symput;
253 } else {
254 if (newline) {
255 newline = NO;
256 dbputc('\n');
257 }
258 /* check for compressed blanks */
259 if (blank) {
260 if (dicode2[c]) {
261 c = (0200 - 2) + dicode1[' '] +
262 dicode2[c];
263 } else {
264 dbputc(' ');
265 }
266 } else if (dicode1[c] &&
267 (j = dicode2[(unsigned)yytext[i + 1]]) != 0 &&
268 symput < symbols && i + 1 != symbol[symput].first) {
269 /* compress digraphs */
270 c = (0200 - 2) + dicode1[c] + j;
271 ++i;
272 }
273 /*
274 * if the last line of the file is a '}' without a
275 * newline, the lex EOF code overwrites it with a 0
276 */
277 if (c) {
278 dbputc((int)c);
279 } else {
280 dbputc(' ');
281 }
282 blank = NO;
283
284 /* skip compressed characters */
285 if (c < ' ') {
286 ++i;
287
288 /* skip blanks before a preprocesor keyword */
289 /*
290 * note: don't use isspace() because \f and \v
291 * are used for keywords
292 */
293 while ((j = yytext[i]) == ' ' || j == '\t') {
294 ++i;
295 }
296 /* skip the rest of the keyword */
297 while (isalpha(yytext[i])) {
298 ++i;
299 }
300 /* skip space after certain keywords */
301 if (keyword[c].delim != '\0') {
302 while ((j = yytext[i]) == ' ' ||
303 j == '\t') {
304 ++i;
305 }
306 }
307 /* skip a '(' after certain keywords */
308 if (keyword[c].delim == '(' &&
309 yytext[i] == '(') {
310 ++i;
311 }
312 --i; /* compensate for ++i in for() */
313 }
314 }
315 }
316 /* ignore trailing blanks */
317 dbputc('\n');
318 dbputc('\n');
319
320 /* output any #define end marker */
321 /*
322 * note: must not be part of #define so putsource() doesn't discard it
323 * so findcalledbysub() can find it and return
324 */
325 if (symput < symbols && symbol[symput].type == DEFINEEND) {
326 dbputc('\t');
327 dbputc(DEFINEEND);
328 dbputc('\n');
329 dbputc('\n'); /* mark beginning of next source line */
330 macrooffset = 0;
331 }
332 symbols = 0;
333 }
334
335 /* output the inverted index posting */
336
337 void
putposting(char * term,int type)338 putposting(char *term, int type)
339 {
340 long i, n;
341 char *s;
342 int digits; /* digits output */
343 long offset; /* function/macro database offset */
344 char buf[11]; /* number buffer */
345
346 /* get the function or macro name offset */
347 offset = fcnoffset;
348 if (macrooffset != 0) {
349 offset = macrooffset;
350 }
351 /* then update them to avoid negative relative name offset */
352 switch (type) {
353 case DEFINE:
354 macrooffset = dboffset;
355 break;
356 case DEFINEEND:
357 macrooffset = 0;
358 return; /* null term */
359 case FCNDEF:
360 fcnoffset = dboffset;
361 break;
362 case FCNEND:
363 fcnoffset = 0;
364 return; /* null term */
365 }
366 /* ignore a null term caused by a enum/struct/union without a tag */
367 if (*term == '\0') {
368 return;
369 }
370 /* skip any #include secondary type char (< or ") */
371 if (type == INCLUDE) {
372 ++term;
373 }
374 /*
375 * output the posting, which should be as small as possible to reduce
376 * the temp file size and sort time
377 */
378 (void) fputs(term, postings);
379 (void) putc(' ', postings);
380
381 /*
382 * the line offset is padded so postings for the same term will sort
383 * in ascending line offset order to order the references as they
384 * appear withing a source file
385 */
386 ltobase(lineoffset);
387 for (i = PRECISION - digits; i > 0; --i) {
388 (void) putc('!', postings);
389 }
390 do {
391 (void) putc(*s, postings);
392 } while (*++s != '\0');
393
394 /* postings are also sorted by type */
395 (void) putc(type, postings);
396
397 /* function or macro name offset */
398 if (offset > 0) {
399 (void) putc(' ', postings);
400 ltobase(offset);
401 do {
402 (void) putc(*s, postings);
403 } while (*++s != '\0');
404 }
405 if (putc('\n', postings) == EOF) {
406 cannotwrite(temp1);
407 /* NOTREACHED */
408 }
409 ++npostings;
410 }
411
412 /* put the string into the new database */
413
414 void
putstring(char * s)415 putstring(char *s)
416 {
417 unsigned c;
418 int i;
419
420 /* compress digraphs */
421 for (i = 0; (c = s[i]) != '\0'; ++i) {
422 if (dicode1[c] && dicode2[(unsigned)s[i + 1]]) {
423 c = (0200 - 2) + dicode1[c] +
424 dicode2[(unsigned)s[i + 1]];
425 ++i;
426 }
427 dbputc((int)c);
428 }
429 }
430
431 /* print a warning message with the file name and line number */
432
433 void
warning(text)434 warning(text)
435 char *text;
436 {
437 extern int yylineno;
438
439 (void) fprintf(stderr, "cscope: \"%s\", line %d: warning: %s\n",
440 filename, yylineno, text);
441 errorsfound = YES;
442 }
443