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