1 /* Copyright (c) 2002 Hewlett-Packard under GPL version 2 or later */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 #include "pmccabe.h"
6 #include "dmain.h"
7 
8 /* $Id: cparse.c,v 1.24 2001/01/26 23:00:30 bame Exp $ */
9 
10 int
fancygettoken(char * buf,int classflag,int * line,int * nLine)11 fancygettoken(char *buf, int classflag, int *line, int *nLine)
12 {
13     int c;
14     char tmpbuf[256];
15 
16     if ((c = gettoken(buf, line, nLine)) == T_IDENT)
17     {
18 	if ((c = gettoken(tmpbuf, NULL, NULL)) == ':')
19 	{
20 	    if ((c = ncss_Getchar()) == ':')
21 	    {
22 		buf += strlen(buf); *buf++ = ':'; *buf++ = ':';
23 		*buf = '\0';
24 		switch (c = gettoken(tmpbuf, NULL, NULL))
25 		{
26 		case T_OPERATOR:
27 		    strcat(buf, tmpbuf);
28 		    buf += strlen(buf);
29 		    getoverloadedop(buf);
30 		    break;
31 		case T_IDENT:
32 		    /* ident::ident - could be recursive */
33 		    ncss_Ungets(tmpbuf);
34 		    if ((c = fancygettoken(buf, 1, NULL, NULL)) != T_IDENT)
35 			ncss_Ungetc(c);
36 		    break;
37 		case '~':	/* destructor, collect the identifier */
38 		    *buf++ = c;
39 		    gettoken(buf, NULL, NULL);
40 		    break;
41 		default:
42 		    ncss_Ungetc(c);
43 		    *buf++ = '\0';
44 		    break;
45 		}
46 	    }
47 	    else
48 	    {
49 		/* only got ':', who knows what this is */
50 		ncss_Ungets("::");
51 	    }
52 	}
53 	else
54 	{
55 	    ungettoken(c, tmpbuf);
56 	}
57 
58 	c = T_IDENT;
59     }
60     else if (classflag && c == T_OPERATOR)
61     {
62 	/* strcat(buf, tmpbuf); */
63 	buf += strlen(buf);
64 	getoverloadedop(buf);
65     }
66     else if (classflag && c == '~')
67     {
68         *buf++ = c;
69 	c = gettoken(buf, NULL, NULL);
70 	if (c != T_IDENT)
71 	{
72 	    fprintf(stderr, "fatal error file %s line %d\n",
73 	    	__FILE__, __LINE__);
74 	    exit(3);
75 	}
76     }
77 
78     return c;
79 }
80 
81 int
toplevelstatement(stats_t * stats)82 toplevelstatement(stats_t *stats)
83 /*
84  *	At the top level of a C file, the statements are blocks of
85  *	tokens ending in either ; or are function definitions which
86  *	end in }.  Interesting types of statements include class
87  *	and struct definitions - because they may contain inline functions,
88  *	and function definitions.  All others are merely counted.
89  */
90 {
91     int endofstatement = FALSE;
92     int c;
93     char buf[1024];
94     int functionFirstLine = -1;
95     int functionFirstNLine = -1;
96     int functionDefLine;
97     int line, nLine;
98 
99     buf[0] = '\0';
100 
101     c = skipws();
102     ncss_Ungetc(c);
103 
104     /* gettoken eats whitespace */
105     while (!endofstatement &&
106     	(c = fancygettoken(buf, stats->type == STATS_CLASS, &line, &nLine)) != EOF)
107     {
108 	if (functionFirstLine == -1)
109 	{
110 	    functionFirstLine = line;
111 	    functionFirstNLine = nLine;
112 	}
113 	switch (c)
114 	{
115 	case T_CLASS:
116 	case T_STRUCT:
117 	case T_UNION:
118 	    if (maybeclass())
119 	    {
120 		stats->nsemicolons--;
121 		endofstatement = TRUE;
122 	    }
123 	    break;
124 
125 	case T_NAMESPACE:
126 	    if (maybenamespace())
127 	    {
128 		/* no trailing semicolon for namespaces */
129 		endofstatement = TRUE;
130 	    }
131 	    break;
132 
133 	case '(':
134 	    /* possible start of function */
135 	    functionDefLine = Line;
136 	    possiblefn(stats, buf, functionFirstLine, functionDefLine, functionFirstNLine);
137 	    endofstatement = TRUE;
138 	    break;
139 
140 	case '}':
141 	case ')':
142 	    Exit = 2;
143 	    {
144 		char _buf[100];
145 		sprintf(_buf, "too many %c's", c);
146 		fileerror(_buf);
147 	    }
148 	    break;
149 
150 	case '{':
151 	    c = matchcurly();
152 	    break;
153 
154 	case ':':	/* This should catch C++ "class foo { public: } */
155 	case ';':
156 	    /* end of statement */
157 	    endofstatement = TRUE;
158 	    break;
159 
160 	default:
161 	    break;
162 	}
163     }
164 
165     return c;
166 }
167 
168 int
findchar(char fc)169 findchar(char fc)
170 {
171     int c;
172 
173     while ((c = ncss_Getchar()) != EOF && c != fc)
174     {
175     }
176 
177     return c;
178 }
179 
180 int
maybeclass()181 maybeclass()
182 /*
183  * We've just seen "class" at the top level in a file so
184  * we may be entering a definition of same.  If so, we want to be
185  * on the lookout for inline functions.  Return 1 if this is a
186  * class definition else restore the function name and return 0.
187  */
188 {
189     char classname[256], dummy[256];
190     int isclass = 0;
191     int c;
192 
193     if ((c =gettoken(classname, NULL, NULL)) == T_IDENT)
194     {
195 	/* "class name" */
196 	switch(c = gettoken(dummy, NULL, NULL))
197 	{
198 	case '{':	/* "class name {" */
199 	    break;
200 
201 	case ':':	/* "class name :" */
202             c = gettoken(dummy, NULL, NULL);
203 	    if (c == ':')
204 	    {
205 	        /* "class name :: */
206 		/* this is a namespace-qualified declaration since cannot
207 		   have a definition like this. */
208 		break;
209 	    }
210 	    else
211 	    {
212 		/* "clas name : [one or more initializers]" */
213 	        while (c != '{')
214 		{
215 		    c = gettoken(dummy, NULL, NULL);
216 		}
217 	    }
218 	    break;
219 
220 	default:
221 	    /* if we fail to get "class name [:.*] {" */
222 	    ungettoken(c, dummy);
223 	}
224     }
225     else if (c == '{')	/* Unnamed class */
226     {
227 	/* "class {" */
228 	strcpy(classname, "unnamed");
229     }
230     else
231     {
232 	/* "class BOGUS" -- perhaps this is C code using a variable "class" */
233 	ungettoken(c, dummy);
234     }
235 
236     if (isclass = (c == '{'))
237     {
238 	stats_t *class = stats_push(classname, STATS_CLASS);
239 
240 	while ((c = gettoken(dummy, NULL, NULL)) != '}')
241 	{
242 	    if (c == EOF)
243 	    {
244 		fileerror("unexpected EOF");
245 		break;
246 	    }
247 	    else
248 		ungettoken(c, dummy);
249 
250 	    toplevelstatement(class);
251 	}
252 
253 	stats_pop(class);
254     }
255 
256     return isclass;
257 }
258 
259 int
maybenamespace()260 maybenamespace()
261 /*
262  * We've just seen "namespace" at the top level in a file so
263  * we may be entering a definition of same (if we next find "token {").
264  * Return 1 if this is a
265  * namespace definition else restore the name and return 0.
266  */
267 {
268     char nsname[256], dummy[256];
269     int isns = 0;
270     int c;
271 
272     if ((c = gettoken(nsname, NULL, NULL)) == T_IDENT)
273     {
274 	/* "namespace name" */
275 	switch(c = gettoken(dummy, NULL, NULL))
276 	{
277 	case '{':	/* "namespace name {" */
278 	    break;
279 
280 	default:
281 	    /* if we fail to get "namespace name {" */
282 	    ungettoken(c, dummy);
283 	}
284     }
285     else if (c == '{')	/* Unnamed namespace */
286     {
287 	/* "namespace {" */
288 	strcpy(nsname, "anonymous_namespace");
289     }
290     else
291     {
292 	/* "namespace BOGUS" -- is C code using a variable "namespace"? */
293 	ungettoken(c, dummy);
294     }
295 
296     if (isns = (c == '{'))
297     {
298 	stats_t *ns = stats_push(nsname, STATS_NAMESPACE);
299 
300 	while ((c = gettoken(dummy, NULL, NULL)) != '}')
301 	{
302 	    if (c == EOF)
303 	    {
304 		fileerror("unexpected EOF");
305 		break;
306 	    }
307 	    else
308 		ungettoken(c, dummy);
309 
310 	    toplevelstatement(ns);
311 	}
312 
313 	stats_pop(ns);
314     }
315 
316     return isns;
317 }
318 
319 
320 void
findsemicolon()321 findsemicolon()
322 {
323     int c;
324 
325     while ((c = ncss_Getchar()) != EOF && c != ';')
326     {
327 	switch (c)
328 	{
329 	case '(':
330 	    c = matchparen();
331 	    break;
332 	case '{':
333 	    c = matchcurly();
334 	    break;
335 	}
336     }
337 
338     if (c == EOF)
339     {
340 	Exit = 5;
341 	fileerror("expected ';' got EOF");
342     }
343 }
344 
345 int
getoverloadedop(char * buf)346 getoverloadedop(char *buf)
347 /*
348  * Having just read ident::operator, try to read the operator into buf.
349  * If the first non-WS character is a '(', the overloaded thing is a
350  * function call.  Otherwise it's some type of real operator and we
351  * terminate normally on '('.  If we read ; or { we probably should
352  * print a warning and bail out.
353  */
354 {
355     char *savebuf = buf;
356     char tmpbuf[256];
357     int c = gettoken(tmpbuf, NULL, NULL);
358 
359     if (c == '(')
360     {
361 	/* overloaded function call syntax */
362 	*buf++ = c;
363 	/* Match the paren */
364 	while(c != ')')
365 	{
366 	    if ((c = skipws()) == EOF)
367 		break;
368 
369 	    *buf++ = c;
370 	}
371     }
372     else if (c == T_IDENT)
373     {
374 	/* class::operator int() */
375 	/* Overloaded typecast */
376 	*buf++ = '_';
377 	*buf = '\0';
378 	strcat(buf, tmpbuf);
379 	buf += strlen(buf);
380 	*buf++ = '(';
381 	*buf++ = ')';
382 	*buf = '\0';
383     }
384     else if (c != EOF)
385     {
386 	*buf++ = c;
387 	while ((c = ncss_Getchar()) != EOF)
388 	{
389 	    if (!ISSPACE(c))
390 	    {
391 		if (c == '(' || c == ';')
392 		{
393 		    ncss_Ungetc(c);
394 		    break;
395 		}
396 		else
397 		    *buf++ = c;
398 	    }
399 	}
400     }
401 
402     *buf = '\0';
403 
404     return c;
405 }
406 
407 void
possiblefn(stats_t * stats,const char * name,int line1,int defline,int nLine1)408 possiblefn(stats_t *stats, const char *name, int line1, int defline, int nLine1)
409 /*
410  *	We've just read an open parenthesis.  If there's a legal identifier
411  *	in name we may be within a function definition.
412  */
413 {
414     char dummy[257];
415     int nstatements = 0;	/* in case there's code prior to the { */
416     int c;
417 
418     if (strlen(name) == 0)
419     {
420 	/* no function name - must not be a function - return */
421 	findsemicolon();
422     }
423     else
424     {
425 	if ((c = matchparen()) != EOF)
426 	{
427 	    c = gettoken(dummy, NULL, NULL);
428 
429 	    switch (c)
430 	    {
431 	    case T_CONST:
432 		if (strchr(name, ':') != NULL || stats->type == STATS_CLASS)
433 		{
434 		    /* foo::foo() const ^ [;] { */
435 		    /* This'll either be a ; for a declaration or a { */
436 		    c = gettoken(dummy, NULL, NULL);
437 		    break;
438 		}
439 		/* foo() const ^ char *a; { */
440 		/*** FALL THROUGH ***/
441 
442 	    case T_IDENT:
443 	    case T_STRUCT:
444 	    case T_UNION:
445 		/* if (strchr(name, ':') == NULL && stats->type != STATS_CLASS) */
446 		{
447 		    /* K&R function, T_IDENT is part of first parm defn */
448 		    /* Read up to that first '{' */
449 		    /* function foo(a, b, c) int a; */
450 		    /*                          ^   */
451 		    while ((c = ncss_Getchar()) != EOF && c != '{')
452 		    {
453 		    }
454 		}
455 		break;
456 	    case '{':
457 		/* open { of the function */
458 		break;
459 	    case '(':
460 		/* wierd possibility in C++ - what we thought was the */
461 		/* parameter list was really part of an overloaded */
462 		/* operator overloading of an odd typecast or something. */
463 		/* The function name will be wrong but who cares :-> */
464 
465 		c = matchparen();
466 		if (c != EOF)
467 		    c = gettoken(dummy, NULL, NULL);
468 		break;
469 	    case ':':
470 		/* Another C++-ism:	main(args): ident(args), ident(args) */
471 		c = prefunction(&nstatements);
472 		break;
473 	    }
474 
475 	    if (c == '{')
476 	    {
477 		/* This really is a function */
478 		stats_t *fn = stats_push(name, STATS_FUNCTION);
479 		fn->nfunctions = 1;
480 		fn->firstline = line1;
481 		fn->defline = defline;
482 		fn->nsemicolons = nstatements;
483 
484 		c = countfunction(fn);
485 		fn->nLines = ncss_Line - nLine1;
486 		stats->nLines -= fn->nLines;
487 		if (!Totalsonly && !Filesonly)
488 		    printstats(fn);
489 		stats_pop(fn);
490 	    }
491 	}
492     }
493 }
494 
495 int
prefunction(int * nstatements)496 prefunction(int *nstatements)
497 /*
498  * Handle C++ ident(args) : ident(args), ident(args).  Count each
499  * ident(args) after function declaration as a statement.
500  */
501 {
502     int c;
503 
504     (*nstatements)++;
505 
506     while((c = ncss_Getchar()) != EOF)
507     {
508 	switch(c)
509 	{
510 	case '(':
511 	    c = matchparen();
512 	    break;
513 	case ',':
514 	    (*nstatements)++;
515 	    break;
516 	case '{':
517 	    return c;
518 	}
519     }
520     Exit = 9;
521     fileerror("expected { got EOF");
522 
523     return c;
524 }
525 
526 int
countfunction(stats_t * fn)527 countfunction(stats_t *fn)
528 {
529     int nest = 1;
530     int c;
531     char id[257];
532 
533     while (nest > 0 && (c = gettoken2(id, NULL, NULL)) != EOF)
534     {
535 	switch (c)
536 	{
537 	case ';':
538 	    fn->nsemicolons++;
539 	    break;
540 
541 	case '{':
542 	    nest++;
543 	    break;
544 
545 	case '}':
546 	    nest--;
547 	    break;
548 
549 	case '?':
550 	    fn->nq++;
551 	    break;
552 
553 	case T_LOGICAL:
554 	    fn->nor++;
555 	    break;
556 
557 	case '^':
558 	    fn->nor++;	 /* This is XOR but I'm lazy */
559 	    break;
560 
561 	case T_CLASS:
562 	case T_UNION:
563 	case T_STRUCT:
564 	    if (maybeclass())
565 		fn->nsemicolons--;
566 	    break;
567 
568 	default:
569 	    countword(fn, c);
570 	    break;
571 	}
572     }
573 
574     fn->lastline = Line;
575 
576     if (nest > 0 /* && c == EOF */ )
577     {
578 	Exit = 6;
579 	fileerror("not enough }'s");
580     }
581 
582     return c;
583 }
584 
585 void
countword(stats_t * fn,int id)586 countword(stats_t * fn, int id)
587 {
588     switch (id)
589     {
590     case T_IF:
591 	fn->nif++;
592 	break;
593     case T_FOR:
594 	fn->nfor++;
595 	break;
596     case T_WHILE:
597 	fn->nwhile++;
598 	break;
599     case T_SWITCH:
600 	fn->nswitch++;
601 	break;
602     case T_CASE:
603 	fn->ncase++;
604 	break;
605     }
606 }
607