Rename fileUngetc() to ungetcToInputFile()
[geany-mirror.git] / ctags / main / get.c
blob540b8b29e33e6d0e93887d6122058365f51d07fc
1 /*
2 * Copyright (c) 1996-2002, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License.
7 * This module contains the high level source read functions (preprocessor
8 * directives are handled within this level).
9 */
12 * INCLUDE FILES
14 #include "general.h" /* must always come first */
16 #include <string.h>
17 #include <glib.h>
19 #include "entry.h"
20 #include "get.h"
21 #include "options.h"
22 #include "read.h"
23 #include "vstring.h"
26 * MACROS
28 #define stringMatch(s1,s2) (strcmp (s1,s2) == 0)
29 #define isspacetab(c) ((c) == SPACE || (c) == TAB)
32 * DATA DECLARATIONS
34 typedef enum { COMMENT_NONE, COMMENT_C, COMMENT_CPLUS, COMMENT_D } Comment;
36 enum eCppLimits {
37 MaxCppNestingLevel = 20,
38 MaxDirectiveName = 10
41 /* Defines the one nesting level of a preprocessor conditional.
43 typedef struct sConditionalInfo {
44 boolean ignoreAllBranches; /* ignoring parent conditional branch */
45 boolean singleBranch; /* choose only one branch */
46 boolean branchChosen; /* branch already selected */
47 boolean ignoring; /* current ignore state */
48 } conditionalInfo;
50 enum eState {
51 DRCTV_NONE, /* no known directive - ignore to end of line */
52 DRCTV_DEFINE, /* "#define" encountered */
53 DRCTV_HASH, /* initial '#' read; determine directive */
54 DRCTV_IF, /* "#if" or "#ifdef" encountered */
55 DRCTV_PRAGMA, /* #pragma encountered */
56 DRCTV_UNDEF /* "#undef" encountered */
59 /* Defines the current state of the pre-processor.
61 typedef struct sCppState {
62 int ungetch, ungetch2; /* ungotten characters, if any */
63 boolean resolveRequired; /* must resolve if/else/elif/endif branch */
64 boolean hasAtLiteralStrings; /* supports @"c:\" strings */
65 boolean hasCxxRawLiteralStrings; /* supports R"xxx(...)xxx" strings */
66 struct sDirective {
67 enum eState state; /* current directive being processed */
68 boolean accept; /* is a directive syntactically permitted? */
69 vString * name; /* macro name */
70 unsigned int nestLevel; /* level 0 is not used */
71 conditionalInfo ifdef [MaxCppNestingLevel];
72 } directive;
73 } cppState;
76 * DATA DEFINITIONS
79 /* Use brace formatting to detect end of block.
81 static boolean BraceFormat = FALSE;
83 static cppState Cpp = {
84 '\0', '\0', /* ungetch characters */
85 FALSE, /* resolveRequired */
86 FALSE, /* hasAtLiteralStrings */
87 FALSE, /* hasCxxRawLiteralStrings */
89 DRCTV_NONE, /* state */
90 FALSE, /* accept */
91 NULL, /* tag name */
92 0, /* nestLevel */
93 { {FALSE,FALSE,FALSE,FALSE} } /* ifdef array */
94 } /* directive */
98 * FUNCTION DEFINITIONS
101 extern boolean isBraceFormat (void)
103 return BraceFormat;
106 extern unsigned int getDirectiveNestLevel (void)
108 return Cpp.directive.nestLevel;
111 extern void cppInit (const boolean state, const boolean hasAtLiteralStrings,
112 const boolean hasCxxRawLiteralStrings)
114 BraceFormat = state;
116 Cpp.ungetch = '\0';
117 Cpp.ungetch2 = '\0';
118 Cpp.resolveRequired = FALSE;
119 Cpp.hasAtLiteralStrings = hasAtLiteralStrings;
120 Cpp.hasCxxRawLiteralStrings = hasCxxRawLiteralStrings;
122 Cpp.directive.state = DRCTV_NONE;
123 Cpp.directive.accept = TRUE;
124 Cpp.directive.nestLevel = 0;
126 Cpp.directive.ifdef [0].ignoreAllBranches = FALSE;
127 Cpp.directive.ifdef [0].singleBranch = FALSE;
128 Cpp.directive.ifdef [0].branchChosen = FALSE;
129 Cpp.directive.ifdef [0].ignoring = FALSE;
131 if (Cpp.directive.name == NULL)
132 Cpp.directive.name = vStringNew ();
133 else
134 vStringClear (Cpp.directive.name);
137 extern void cppTerminate (void)
139 if (Cpp.directive.name != NULL)
141 vStringDelete (Cpp.directive.name);
142 Cpp.directive.name = NULL;
146 extern void cppBeginStatement (void)
148 Cpp.resolveRequired = TRUE;
151 extern void cppEndStatement (void)
153 Cpp.resolveRequired = FALSE;
157 * Scanning functions
159 * This section handles preprocessor directives. It strips out all
160 * directives and may emit a tag for #define directives.
163 /* This puts a character back into the input queue for the source File.
164 * Up to two characters may be ungotten.
166 extern void cppUngetc (const int c)
168 Assert (Cpp.ungetch2 == '\0');
169 Cpp.ungetch2 = Cpp.ungetch;
170 Cpp.ungetch = c;
173 /* Reads a directive, whose first character is given by "c", into "name".
175 static boolean readDirective (int c, char *const name, unsigned int maxLength)
177 unsigned int i;
179 for (i = 0 ; i < maxLength - 1 ; ++i)
181 if (i > 0)
183 c = getcFromInputFile ();
184 if (c == EOF || ! isalpha (c))
186 ungetcToInputFile (c);
187 break;
190 name [i] = c;
192 name [i] = '\0'; /* null terminate */
194 return (boolean) isspacetab (c);
197 /* Reads an identifier, whose first character is given by "c", into "tag",
198 * together with the file location and corresponding line number.
200 static void readIdentifier (int c, vString *const name)
202 vStringClear (name);
205 vStringPut (name, c);
206 c = getcFromInputFile ();
207 } while (c != EOF && isident (c));
208 ungetcToInputFile (c);
209 vStringTerminate (name);
212 static conditionalInfo *currentConditional (void)
214 return &Cpp.directive.ifdef [Cpp.directive.nestLevel];
217 static boolean isIgnore (void)
219 return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring;
222 static boolean setIgnore (const boolean ignore)
224 return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring = ignore;
227 static boolean isIgnoreBranch (void)
229 conditionalInfo *const ifdef = currentConditional ();
231 /* Force a single branch if an incomplete statement is discovered
232 * en route. This may have allowed earlier branches containing complete
233 * statements to be followed, but we must follow no further branches.
235 if (Cpp.resolveRequired && ! BraceFormat)
236 ifdef->singleBranch = TRUE;
238 /* We will ignore this branch in the following cases:
240 * 1. We are ignoring all branches (conditional was within an ignored
241 * branch of the parent conditional)
242 * 2. A branch has already been chosen and either of:
243 * a. A statement was incomplete upon entering the conditional
244 * b. A statement is incomplete upon encountering a branch
246 return (boolean) (ifdef->ignoreAllBranches ||
247 (ifdef->branchChosen && ifdef->singleBranch));
250 static void chooseBranch (void)
252 if (! BraceFormat)
254 conditionalInfo *const ifdef = currentConditional ();
256 ifdef->branchChosen = (boolean) (ifdef->singleBranch ||
257 Cpp.resolveRequired);
261 /* Pushes one nesting level for an #if directive, indicating whether or not
262 * the branch should be ignored and whether a branch has already been chosen.
264 static boolean pushConditional (const boolean firstBranchChosen)
266 const boolean ignoreAllBranches = isIgnore (); /* current ignore */
267 boolean ignoreBranch = FALSE;
269 if (Cpp.directive.nestLevel < (unsigned int) MaxCppNestingLevel - 1)
271 conditionalInfo *ifdef;
273 ++Cpp.directive.nestLevel;
274 ifdef = currentConditional ();
276 /* We take a snapshot of whether there is an incomplete statement in
277 * progress upon encountering the preprocessor conditional. If so,
278 * then we will flag that only a single branch of the conditional
279 * should be followed.
281 ifdef->ignoreAllBranches = ignoreAllBranches;
282 ifdef->singleBranch = Cpp.resolveRequired;
283 ifdef->branchChosen = firstBranchChosen;
284 ifdef->ignoring = (boolean) (ignoreAllBranches || (
285 ! firstBranchChosen && ! BraceFormat &&
286 (ifdef->singleBranch || !Option.if0)));
287 ignoreBranch = ifdef->ignoring;
289 return ignoreBranch;
292 /* Pops one nesting level for an #endif directive.
294 static boolean popConditional (void)
296 if (Cpp.directive.nestLevel > 0)
297 --Cpp.directive.nestLevel;
299 return isIgnore ();
302 static void makeDefineTag (const char *const name, boolean parameterized)
304 const boolean isFileScope = (boolean) (! isHeaderFile ());
306 if (includingDefineTags () &&
307 (! isFileScope || Option.include.fileScope))
309 tagEntryInfo e;
311 initTagEntry (&e, name);
313 e.lineNumberEntry = (boolean) (Option.locate != EX_PATTERN);
314 e.isFileScope = isFileScope;
315 e.truncateLine = TRUE;
316 e.kindName = "macro";
317 e.kind = 'd';
318 if (parameterized)
320 e.extensionFields.signature = getArglistFromFilePos(getInputFilePosition()
321 , e.name);
323 makeTagEntry (&e);
324 if (parameterized)
325 free((char *) e.extensionFields.signature);
329 static void directiveDefine (const int c)
331 boolean parameterized;
332 int nc;
334 if (isident1 (c))
336 readIdentifier (c, Cpp.directive.name);
337 nc = getcFromInputFile ();
338 ungetcToInputFile (nc);
339 parameterized = (boolean) (nc == '(');
340 if (! isIgnore ())
341 makeDefineTag (vStringValue (Cpp.directive.name), parameterized);
343 Cpp.directive.state = DRCTV_NONE;
346 static void directivePragma (int c)
348 if (isident1 (c))
350 readIdentifier (c, Cpp.directive.name);
351 if (stringMatch (vStringValue (Cpp.directive.name), "weak"))
353 /* generate macro tag for weak name */
356 c = getcFromInputFile ();
357 } while (c == SPACE);
358 if (isident1 (c))
360 readIdentifier (c, Cpp.directive.name);
361 makeDefineTag (vStringValue (Cpp.directive.name), FALSE);
365 Cpp.directive.state = DRCTV_NONE;
368 static boolean directiveIf (const int c)
370 const boolean ignore = pushConditional ((boolean) (c != '0'));
372 Cpp.directive.state = DRCTV_NONE;
374 return ignore;
377 static boolean directiveHash (const int c)
379 boolean ignore = FALSE;
380 char directive [MaxDirectiveName];
381 DebugStatement ( const boolean ignore0 = isIgnore (); )
383 readDirective (c, directive, MaxDirectiveName);
384 if (stringMatch (directive, "define"))
385 Cpp.directive.state = DRCTV_DEFINE;
386 else if (stringMatch (directive, "undef"))
387 Cpp.directive.state = DRCTV_UNDEF;
388 else if (strncmp (directive, "if", (size_t) 2) == 0)
389 Cpp.directive.state = DRCTV_IF;
390 else if (stringMatch (directive, "elif") ||
391 stringMatch (directive, "else"))
393 ignore = setIgnore (isIgnoreBranch ());
394 if (! ignore && stringMatch (directive, "else"))
395 chooseBranch ();
396 Cpp.directive.state = DRCTV_NONE;
397 DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); )
399 else if (stringMatch (directive, "endif"))
401 DebugStatement ( debugCppNest (FALSE, Cpp.directive.nestLevel); )
402 ignore = popConditional ();
403 Cpp.directive.state = DRCTV_NONE;
404 DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); )
406 else if (stringMatch (directive, "pragma"))
407 Cpp.directive.state = DRCTV_PRAGMA;
408 else
409 Cpp.directive.state = DRCTV_NONE;
411 return ignore;
414 /* Handles a pre-processor directive whose first character is given by "c".
416 static boolean handleDirective (const int c)
418 boolean ignore = isIgnore ();
420 switch (Cpp.directive.state)
422 case DRCTV_NONE: ignore = isIgnore (); break;
423 case DRCTV_DEFINE: directiveDefine (c); break;
424 case DRCTV_HASH: ignore = directiveHash (c); break;
425 case DRCTV_IF: ignore = directiveIf (c); break;
426 case DRCTV_PRAGMA: directivePragma (c); break;
427 case DRCTV_UNDEF: directiveDefine (c); break;
429 return ignore;
432 /* Called upon reading of a slash ('/') characters, determines whether a
433 * comment is encountered, and its type.
435 static Comment isComment (void)
437 Comment comment;
438 const int next = getcFromInputFile ();
440 if (next == '*')
441 comment = COMMENT_C;
442 else if (next == '/')
443 comment = COMMENT_CPLUS;
444 else if (next == '+')
445 comment = COMMENT_D;
446 else
448 ungetcToInputFile (next);
449 comment = COMMENT_NONE;
451 return comment;
454 /* Skips over a C style comment. According to ANSI specification a comment
455 * is treated as white space, so we perform this substitution.
457 int skipOverCComment (void)
459 int c = getcFromInputFile ();
461 while (c != EOF)
463 if (c != '*')
464 c = getcFromInputFile ();
465 else
467 const int next = getcFromInputFile ();
469 if (next != '/')
470 c = next;
471 else
473 c = SPACE; /* replace comment with space */
474 break;
478 return c;
481 /* Skips over a C++ style comment.
483 static int skipOverCplusComment (void)
485 int c;
487 while ((c = getcFromInputFile ()) != EOF)
489 if (c == BACKSLASH)
490 getcFromInputFile (); /* throw away next character, too */
491 else if (c == NEWLINE)
492 break;
494 return c;
497 /* Skips over a D style comment.
498 * Really we should match nested /+ comments. At least they're less common.
500 static int skipOverDComment (void)
502 int c = getcFromInputFile ();
504 while (c != EOF)
506 if (c != '+')
507 c = getcFromInputFile ();
508 else
510 const int next = getcFromInputFile ();
512 if (next != '/')
513 c = next;
514 else
516 c = SPACE; /* replace comment with space */
517 break;
521 return c;
524 /* Skips to the end of a string, returning a special character to
525 * symbolically represent a generic string.
527 static int skipToEndOfString (boolean ignoreBackslash)
529 int c;
531 while ((c = getcFromInputFile ()) != EOF)
533 if (c == BACKSLASH && ! ignoreBackslash)
534 getcFromInputFile (); /* throw away next character, too */
535 else if (c == DOUBLE_QUOTE)
536 break;
538 return STRING_SYMBOL; /* symbolic representation of string */
541 static int isCxxRawLiteralDelimiterChar (int c)
543 return (c != ' ' && c != '\f' && c != '\n' && c != '\r' && c != '\t' && c != '\v' &&
544 c != '(' && c != ')' && c != '\\');
547 static int skipToEndOfCxxRawLiteralString (void)
549 int c = getcFromInputFile ();
551 if (c != '(' && ! isCxxRawLiteralDelimiterChar (c))
553 ungetcToInputFile (c);
554 c = skipToEndOfString (FALSE);
556 else
558 char delim[16];
559 unsigned int delimLen = 0;
560 boolean collectDelim = TRUE;
564 if (collectDelim)
566 if (isCxxRawLiteralDelimiterChar (c) &&
567 delimLen < (sizeof delim / sizeof *delim))
568 delim[delimLen++] = c;
569 else
570 collectDelim = FALSE;
572 else if (c == ')')
574 unsigned int i = 0;
576 while ((c = getcFromInputFile ()) != EOF && i < delimLen && delim[i] == c)
577 i++;
578 if (i == delimLen && c == DOUBLE_QUOTE)
579 break;
580 else
581 ungetcToInputFile (c);
584 while ((c = getcFromInputFile ()) != EOF);
585 c = STRING_SYMBOL;
587 return c;
590 /* Skips to the end of the three (possibly four) 'c' sequence, returning a
591 * special character to symbolically represent a generic character.
592 * Also detects Vera numbers that include a base specifier (ie. 'b1010).
594 static int skipToEndOfChar (void)
596 int c;
597 int count = 0, veraBase = '\0';
599 while ((c = getcFromInputFile ()) != EOF)
601 ++count;
602 if (c == BACKSLASH)
603 getcFromInputFile (); /* throw away next character, too */
604 else if (c == SINGLE_QUOTE)
605 break;
606 else if (c == NEWLINE)
608 ungetcToInputFile (c);
609 break;
611 else if (count == 1 && strchr ("DHOB", toupper (c)) != NULL)
612 veraBase = c;
613 else if (veraBase != '\0' && ! isalnum (c))
615 ungetcToInputFile (c);
616 break;
619 return CHAR_SYMBOL; /* symbolic representation of character */
622 /* This function returns the next character, stripping out comments,
623 * C pre-processor directives, and the contents of single and double
624 * quoted strings. In short, strip anything which places a burden upon
625 * the tokenizer.
627 extern int cppGetc (void)
629 boolean directive = FALSE;
630 boolean ignore = FALSE;
631 int c;
633 if (Cpp.ungetch != '\0')
635 c = Cpp.ungetch;
636 Cpp.ungetch = Cpp.ungetch2;
637 Cpp.ungetch2 = '\0';
638 return c; /* return here to avoid re-calling debugPutc () */
640 else do
642 c = getcFromInputFile ();
643 process:
644 switch (c)
646 case EOF:
647 ignore = FALSE;
648 directive = FALSE;
649 break;
651 case TAB:
652 case SPACE:
653 break; /* ignore most white space */
655 case NEWLINE:
656 if (directive && ! ignore)
657 directive = FALSE;
658 Cpp.directive.accept = TRUE;
659 break;
661 case DOUBLE_QUOTE:
662 Cpp.directive.accept = FALSE;
663 c = skipToEndOfString (FALSE);
664 break;
666 case '#':
667 if (Cpp.directive.accept)
669 directive = TRUE;
670 Cpp.directive.state = DRCTV_HASH;
671 Cpp.directive.accept = FALSE;
673 break;
675 case SINGLE_QUOTE:
676 Cpp.directive.accept = FALSE;
677 c = skipToEndOfChar ();
678 break;
680 case '/':
682 const Comment comment = isComment ();
684 if (comment == COMMENT_C)
685 c = skipOverCComment ();
686 else if (comment == COMMENT_CPLUS)
688 c = skipOverCplusComment ();
689 if (c == NEWLINE)
690 ungetcToInputFile (c);
692 else if (comment == COMMENT_D)
693 c = skipOverDComment ();
694 else
695 Cpp.directive.accept = FALSE;
696 break;
699 case BACKSLASH:
701 int next = getcFromInputFile ();
703 if (next == NEWLINE)
704 continue;
705 else
706 ungetcToInputFile (next);
707 break;
710 case '?':
712 int next = getcFromInputFile ();
713 if (next != '?')
714 ungetcToInputFile (next);
715 else
717 next = getcFromInputFile ();
718 switch (next)
720 case '(': c = '['; break;
721 case ')': c = ']'; break;
722 case '<': c = '{'; break;
723 case '>': c = '}'; break;
724 case '/': c = BACKSLASH; goto process;
725 case '!': c = '|'; break;
726 case SINGLE_QUOTE: c = '^'; break;
727 case '-': c = '~'; break;
728 case '=': c = '#'; goto process;
729 default:
730 ungetcToInputFile ('?');
731 ungetcToInputFile (next);
732 break;
735 } break;
737 /* digraphs:
738 * input: <: :> <% %> %: %:%:
739 * output: [ ] { } # ##
741 case '<':
743 int next = getcFromInputFile ();
744 switch (next)
746 case ':': c = '['; break;
747 case '%': c = '{'; break;
748 default: ungetcToInputFile (next);
750 goto enter;
752 case ':':
754 int next = getcFromInputFile ();
755 if (next == '>')
756 c = ']';
757 else
758 ungetcToInputFile (next);
759 goto enter;
761 case '%':
763 int next = getcFromInputFile ();
764 switch (next)
766 case '>': c = '}'; break;
767 case ':': c = '#'; goto process;
768 default: ungetcToInputFile (next);
770 goto enter;
773 default:
774 if (c == '@' && Cpp.hasAtLiteralStrings)
776 int next = getcFromInputFile ();
777 if (next == DOUBLE_QUOTE)
779 Cpp.directive.accept = FALSE;
780 c = skipToEndOfString (TRUE);
781 break;
783 else
784 ungetcToInputFile (next);
786 else if (c == 'R' && Cpp.hasCxxRawLiteralStrings)
788 /* OMG!11 HACK!!11 Get the previous character.
790 * We need to know whether the previous character was an identifier or not,
791 * because "R" has to be on its own, not part of an identifier. This allows
792 * for constructs like:
794 * #define FOUR "4"
795 * const char *p = FOUR"5";
797 * which is not a raw literal, but a preprocessor concatenation.
799 * FIXME: handle
801 * const char *p = R\
802 * "xxx(raw)xxx";
804 * which is perfectly valid (yet probably very unlikely). */
805 int prev = fileGetNthPrevC (1, '\0');
806 int prev2 = fileGetNthPrevC (2, '\0');
807 int prev3 = fileGetNthPrevC (3, '\0');
809 if (! isident (prev) ||
810 (! isident (prev2) && (prev == 'L' || prev == 'u' || prev == 'U')) ||
811 (! isident (prev3) && (prev2 == 'u' && prev == '8')))
813 int next = getcFromInputFile ();
814 if (next != DOUBLE_QUOTE)
815 ungetcToInputFile (next);
816 else
818 Cpp.directive.accept = FALSE;
819 c = skipToEndOfCxxRawLiteralString ();
820 break;
824 enter:
825 Cpp.directive.accept = FALSE;
826 if (directive)
827 ignore = handleDirective (c);
828 break;
830 } while (directive || ignore);
832 DebugStatement ( debugPutc (DEBUG_CPP, c); )
833 DebugStatement ( if (c == NEWLINE)
834 debugPrintf (DEBUG_CPP, "%6ld: ", getInputLineNumber () + 1); )
836 return c;
839 extern char *getArglistFromFilePos(MIOPos startPosition, const char *tokenName)
841 MIOPos originalPosition;
842 char *result = NULL;
843 char *arglist = NULL;
844 long pos1, pos2;
846 pos2 = mio_tell(File.fp);
848 mio_getpos(File.fp, &originalPosition);
849 mio_setpos(File.fp, &startPosition);
850 pos1 = mio_tell(File.fp);
852 if (pos2 > pos1)
854 size_t len = pos2 - pos1;
856 result = (char *) g_malloc(len + 1);
857 if (result != NULL && (len = mio_read(File.fp, result, 1, len)) > 0)
859 result[len] = '\0';
860 arglist = getArglistFromStr(result, tokenName);
862 g_free(result);
864 mio_setpos(File.fp, &originalPosition);
865 return arglist;
868 typedef enum
870 st_none_t,
871 st_escape_t,
872 st_c_comment_t,
873 st_cpp_comment_t,
874 st_double_quote_t,
875 st_single_quote_t
876 } ParseState;
878 static void stripCodeBuffer(char *buf)
880 int i = 0, pos = 0;
881 ParseState state = st_none_t, prev_state = st_none_t;
883 while (buf[i] != '\0')
885 switch(buf[i])
887 case '/':
888 if (st_none_t == state)
890 /* Check if this is the start of a comment */
891 if (buf[i+1] == '*') /* C comment */
892 state = st_c_comment_t;
893 else if (buf[i+1] == '/') /* C++ comment */
894 state = st_cpp_comment_t;
895 else /* Normal character */
896 buf[pos++] = '/';
898 else if (st_c_comment_t == state)
900 /* Check if this is the end of a C comment */
901 if (buf[i-1] == '*')
903 if ((pos > 0) && (buf[pos-1] != ' '))
904 buf[pos++] = ' ';
905 state = st_none_t;
908 break;
909 case '"':
910 if (st_none_t == state)
911 state = st_double_quote_t;
912 else if (st_double_quote_t == state)
913 state = st_none_t;
914 break;
915 case '\'':
916 if (st_none_t == state)
917 state = st_single_quote_t;
918 else if (st_single_quote_t == state)
919 state = st_none_t;
920 break;
921 default:
922 if ((buf[i] == '\\') && (st_escape_t != state))
924 prev_state = state;
925 state = st_escape_t;
927 else if (st_escape_t == state)
929 state = prev_state;
930 prev_state = st_none_t;
932 else if ((buf[i] == '\n') && (st_cpp_comment_t == state))
934 if ((pos > 0) && (buf[pos-1] != ' '))
935 buf[pos++] = ' ';
936 state = st_none_t;
938 else if (st_none_t == state)
940 if (isspace(buf[i]))
942 if ((pos > 0) && (buf[pos-1] != ' '))
943 buf[pos++] = ' ';
945 else
946 buf[pos++] = buf[i];
948 break;
950 ++i;
952 buf[pos] = '\0';
953 return;
956 extern char *getArglistFromStr(char *buf, const char *name)
958 char *start, *end;
959 int level;
960 if ((NULL == buf) || (NULL == name) || ('\0' == name[0]))
961 return NULL;
962 stripCodeBuffer(buf);
963 if (NULL == (start = strstr(buf, name)))
964 return NULL;
965 if (NULL == (start = strchr(start, '(')))
966 return NULL;
967 for (level = 1, end = start + 1; level > 0; ++end)
969 if ('\0' == *end)
970 break;
971 else if ('(' == *end)
972 ++ level;
973 else if (')' == *end)
974 -- level;
976 *end = '\0';
977 return strdup(start);
980 /* vi:set tabstop=4 shiftwidth=4: */