3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
17 #define LIBCTAGS_DEFINED
20 static GMemChunk
*s_tag_mem_chunk
= NULL
;
23 if (!s_tag_mem_chunk) \
24 s_tag_mem_chunk = g_mem_chunk_new("TMTag memChunk", sizeof(TMTag), 10000 \
25 , G_ALLOC_AND_FREE); \
26 (T) = g_chunk_new0(TMTag, s_tag_mem_chunk);}
28 #define TAG_FREE(T) g_mem_chunk_free(s_tag_mem_chunk, (T))
30 /* Note: To preserve binary compatibility, it is very important
31 that you only *append* to this list ! */
37 TA_POS
, /* Obsolete */
51 static guint
*s_sort_attrs
= NULL
;
52 static gboolean s_partial
= FALSE
;
54 static const char *s_tag_type_names
[] = {
55 "class", /* classes */
56 "enum", /* enumeration names */
57 "enumerator", /* enumerators (values inside an enumeration) */
58 "externvar", /* external variable declarations */
60 "function", /* function definitions */
61 "interface", /* interfaces */
62 "macro", /* macro definitions */
63 "member", /* class, struct, and union members */
64 "method", /* methods */
65 "namespace", /* namespaces */
66 "package", /* packages */
67 "prototype", /* function prototypes */
68 "struct", /* structure names */
69 "typedef", /* typedefs */
70 "union", /* union names */
71 "variable", /* variable definitions */
72 "other" /* Other tag type (non C/C++/Java) */
75 static int s_tag_types
[] = {
96 static int get_tag_type(const char *tag_name
)
100 g_return_val_if_fail(tag_name
, 0);
101 for (i
=0; i
< sizeof(s_tag_type_names
)/sizeof(char *); ++i
)
103 cmp
= strcmp(tag_name
, s_tag_type_names
[i
]);
105 return s_tag_types
[i
];
110 fprintf(stderr
, "Unknown tag type %s\n", tag_name
);
112 return tm_tag_undef_t
;
115 gboolean
tm_tag_init(TMTag
*tag
, TMSourceFile
*file
, const tagEntryInfo
*tag_entry
)
117 if (NULL
== tag_entry
)
119 /* This is a file tag */
124 tag
->name
= g_strdup(file
->work_object
.file_name
);
125 tag
->type
= tm_tag_file_t
;
126 tag
->atts
.file
.timestamp
= file
->work_object
.analyze_time
;
127 tag
->atts
.file
.lang
= file
->lang
;
128 tag
->atts
.file
.inactive
= FALSE
;
134 /* This is a normal tag entry */
135 if (NULL
== tag_entry
->name
)
137 tag
->name
= g_strdup(tag_entry
->name
);
138 tag
->type
= get_tag_type(tag_entry
->kindName
);
139 tag
->atts
.entry
.local
= tag_entry
->isFileScope
;
140 tag
->atts
.entry
.pointerOrder
= 0; /*FIXME: tag_entry->pointerOrder; */
141 tag
->atts
.entry
.line
= tag_entry
->lineNumber
;
142 if (NULL
!= tag_entry
->extensionFields
.signature
)
143 tag
->atts
.entry
.arglist
= g_strdup(tag_entry
->extensionFields
.signature
);
144 if ((NULL
!= tag_entry
->extensionFields
.scope
[1]) &&
145 (isalpha(tag_entry
->extensionFields
.scope
[1][0]) ||
146 tag_entry
->extensionFields
.scope
[1][0] == '_')) {
147 tag
->atts
.entry
.scope
= g_strdup(tag_entry
->extensionFields
.scope
[1]);
150 if (tag_entry
->extensionFields
.inheritance
!= NULL
)
151 tag
->atts
.entry
.inheritance
= g_strdup(tag_entry
->extensionFields
.inheritance
);
152 if (tag_entry
->extensionFields
.typeRef
[0] != NULL
)
153 tag
->atts
.entry
.type_ref
[0] = g_strdup(tag_entry
->extensionFields
.typeRef
[0]);
154 if (tag_entry
->extensionFields
.typeRef
[1] != NULL
)
155 tag
->atts
.entry
.type_ref
[1] = g_strdup(tag_entry
->extensionFields
.typeRef
[1]);
156 if (tag_entry
->extensionFields
.access
!= NULL
)
158 if (0 == strcmp("public", tag_entry
->extensionFields
.access
))
159 tag
->atts
.entry
.access
= TAG_ACCESS_PUBLIC
;
160 else if (0 == strcmp("protected", tag_entry
->extensionFields
.access
))
161 tag
->atts
.entry
.access
= TAG_ACCESS_PROTECTED
;
162 else if (0 == strcmp("private", tag_entry
->extensionFields
.access
))
163 tag
->atts
.entry
.access
= TAG_ACCESS_PRIVATE
;
164 else if (0 == strcmp("friend", tag_entry
->extensionFields
.access
))
165 tag
->atts
.entry
.access
= TAG_ACCESS_FRIEND
;
166 else if (0 == strcmp("default", tag_entry
->extensionFields
.access
))
167 tag
->atts
.entry
.access
= TAG_ACCESS_DEFAULT
;
171 g_warning("Unknown access type %s", tag_entry
->extensionFields
.access
);
173 tag
->atts
.entry
.access
= TAG_ACCESS_UNKNOWN
;
176 if (tag_entry
->extensionFields
.implementation
!= NULL
)
178 if ((0 == strcmp("virtual", tag_entry
->extensionFields
.implementation
))
179 || (0 == strcmp("pure virtual", tag_entry
->extensionFields
.implementation
)))
180 tag
->atts
.entry
.impl
= TAG_IMPL_VIRTUAL
;
184 g_warning("Unknown implementation %s", tag_entry
->extensionFields
.implementation
);
186 tag
->atts
.entry
.impl
= TAG_IMPL_UNKNOWN
;
189 if ((tm_tag_macro_t
== tag
->type
) && (NULL
!= tag
->atts
.entry
.arglist
))
190 tag
->type
= tm_tag_macro_with_arg_t
;
191 tag
->atts
.entry
.file
= file
;
196 TMTag
*tm_tag_new(TMSourceFile
*file
, const tagEntryInfo
*tag_entry
)
201 if (FALSE
== tm_tag_init(tag
, file
, tag_entry
))
209 gboolean
tm_tag_init_from_file(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
214 guchar changed_char
= TA_NAME
;
216 if ((NULL
== fgets(buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
218 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
220 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
222 if (('\0' == *end
) || ('\n' == *end
))
226 if (NULL
== tag
->name
)
228 if (!isprint(*start
))
231 tag
->name
= g_strdup(start
);
238 tag
->atts
.entry
.line
= atol(start
+ 1);
241 tag
->atts
.entry
.local
= atoi(start
+ 1);
244 tag
->type
= (TMTagType
) atoi(start
+ 1);
247 tag
->atts
.entry
.arglist
= g_strdup(start
+ 1);
250 tag
->atts
.entry
.scope
= g_strdup(start
+ 1);
253 tag
->atts
.entry
.pointerOrder
= atoi(start
+ 1);
256 tag
->atts
.entry
.type_ref
[1] = g_strdup (start
+ 1);
259 tag
->atts
.entry
.inheritance
= g_strdup(start
+ 1);
262 if (tm_tag_file_t
!= tag
->type
)
264 g_warning("Got time attribute for non-file tag %s", tag
->name
);
268 tag
->atts
.file
.timestamp
= atol(start
+ 1);
271 if (tm_tag_file_t
!= tag
->type
)
273 g_warning("Got lang attribute for non-file tag %s", tag
->name
);
277 tag
->atts
.file
.lang
= atoi(start
+ 1);
280 if (tm_tag_file_t
!= tag
->type
)
282 g_warning("Got inactive attribute for non-file tag %s", tag
->name
);
286 tag
->atts
.file
.inactive
= (gboolean
) atoi(start
+ 1);
289 tag
->atts
.entry
.access
= *(start
+ 1);
292 tag
->atts
.entry
.impl
= *(start
+ 1);
296 g_warning("Unknown attribute %s", start
+ 1);
303 if (NULL
== tag
->name
)
305 if (tm_tag_file_t
!= tag
->type
)
306 tag
->atts
.entry
.file
= file
;
310 TMTag
*tm_tag_new_from_file(TMSourceFile
*file
, FILE *fp
)
315 if (FALSE
== tm_tag_init_from_file(tag
, file
, fp
))
323 gboolean
tm_tag_write(TMTag
*tag
, FILE *fp
, guint attrs
)
325 fprintf(fp
, "%s", tag
->name
);
326 if (attrs
& tm_tag_attr_type_t
)
327 fprintf(fp
, "%c%d", TA_TYPE
, tag
->type
);
328 if (tag
->type
== tm_tag_file_t
)
330 if (attrs
& tm_tag_attr_time_t
)
331 fprintf(fp
, "%c%ld", TA_TIME
, tag
->atts
.file
.timestamp
);
332 if (attrs
& tm_tag_attr_lang_t
)
333 fprintf(fp
, "%c%d", TA_LANG
, tag
->atts
.file
.lang
);
334 if ((attrs
& tm_tag_attr_inactive_t
) && tag
->atts
.file
.inactive
)
335 fprintf(fp
, "%c%d", TA_INACTIVE
, tag
->atts
.file
.inactive
);
339 if ((attrs
& tm_tag_attr_arglist_t
) && (NULL
!= tag
->atts
.entry
.arglist
))
340 fprintf(fp
, "%c%s", TA_ARGLIST
, tag
->atts
.entry
.arglist
);
341 if (attrs
& tm_tag_attr_line_t
)
342 fprintf(fp
, "%c%ld", TA_LINE
, tag
->atts
.entry
.line
);
343 if (attrs
& tm_tag_attr_local_t
)
344 fprintf(fp
, "%c%d", TA_LOCAL
, tag
->atts
.entry
.local
);
345 if ((attrs
& tm_tag_attr_scope_t
) && (NULL
!= tag
->atts
.entry
.scope
))
346 fprintf(fp
, "%c%s", TA_SCOPE
, tag
->atts
.entry
.scope
);
347 if ((attrs
& tm_tag_attr_inheritance_t
) && (NULL
!= tag
->atts
.entry
.inheritance
))
348 fprintf(fp
, "%c%s", TA_INHERITS
, tag
->atts
.entry
.inheritance
);
349 if (attrs
& tm_tag_attr_pointer_t
)
350 fprintf(fp
, "%c%d", TA_POINTER
, tag
->atts
.entry
.pointerOrder
);
351 if ((attrs
& tm_tag_attr_vartype_t
) && (NULL
!= tag
->atts
.entry
.type_ref
[1]))
352 fprintf(fp
, "%c%s", TA_VARTYPE
, tag
->atts
.entry
.type_ref
[1]);
353 if ((attrs
& tm_tag_attr_access_t
) && (TAG_ACCESS_UNKNOWN
!= tag
->atts
.entry
.access
))
354 fprintf(fp
, "%c%c", TA_ACCESS
, tag
->atts
.entry
.access
);
355 if ((attrs
& tm_tag_attr_impl_t
) && (TAG_IMPL_UNKNOWN
!= tag
->atts
.entry
.impl
))
356 fprintf(fp
, "%c%c", TA_IMPL
, tag
->atts
.entry
.impl
);
358 if (fprintf(fp
, "\n"))
364 static void tm_tag_destroy(TMTag
*tag
)
367 //if (tm_tag_file_t != tag->type)
369 g_free(tag
->atts
.entry
.arglist
);
370 g_free(tag
->atts
.entry
.scope
);
371 g_free(tag
->atts
.entry
.inheritance
);
372 g_free(tag
->atts
.entry
.type_ref
[0]);
373 g_free(tag
->atts
.entry
.type_ref
[1]);
377 void tm_tag_free(gpointer tag
)
381 tm_tag_destroy((TMTag
*) tag
);
386 int tm_tag_compare(const void *ptr1
, const void *ptr2
)
390 TMTag
*t1
= *((TMTag
**) ptr1
);
391 TMTag
*t2
= *((TMTag
**) ptr2
);
393 if ((NULL
== t1
) || (NULL
== t2
))
395 g_warning("Found NULL tag");
398 if (NULL
== s_sort_attrs
)
401 return strncmp(NVL(t1
->name
, ""), NVL(t2
->name
, ""), strlen(NVL(t1
->name
, "")));
403 return strcmp(NVL(t1
->name
, ""), NVL(t2
->name
, ""));
406 for (sort_attr
= s_sort_attrs
; *sort_attr
!= tm_tag_attr_none_t
; ++ sort_attr
)
410 case tm_tag_attr_name_t
:
412 returnval
= strncmp(NVL(t1
->name
, ""), NVL(t2
->name
, ""), strlen(NVL(t1
->name
, "")));
414 returnval
= strcmp(NVL(t1
->name
, ""), NVL(t2
->name
, ""));
418 case tm_tag_attr_type_t
:
419 if (0 != (returnval
= (t1
->type
- t2
->type
)))
422 case tm_tag_attr_file_t
:
423 if (0 != (returnval
= (t1
->atts
.entry
.file
- t2
->atts
.entry
.file
)))
426 case tm_tag_attr_scope_t
:
427 if (0 != (returnval
= strcmp(NVL(t1
->atts
.entry
.scope
, ""), NVL(t2
->atts
.entry
.scope
, ""))))
430 case tm_tag_attr_vartype_t
:
431 if (0 != (returnval
= strcmp(NVL(t1
->atts
.entry
.type_ref
[1], ""), NVL(t2
->atts
.entry
.type_ref
[1], ""))))
434 case tm_tag_attr_line_t
:
435 if (0 != (returnval
= (t1
->atts
.entry
.line
- t2
->atts
.entry
.line
)))
443 gboolean
tm_tags_prune(GPtrArray
*tags_array
)
446 for (i
=0, count
= 0; i
< tags_array
->len
; ++i
)
448 if (NULL
!= tags_array
->pdata
[i
])
449 tags_array
->pdata
[count
++] = tags_array
->pdata
[i
];
451 tags_array
->len
= count
;
455 gboolean
tm_tags_dedup(GPtrArray
*tags_array
, TMTagAttrType
*sort_attributes
)
459 if ((!tags_array
) || (!tags_array
->len
))
461 s_sort_attrs
= sort_attributes
;
463 for (i
= 1; i
< tags_array
->len
; ++i
)
465 if (0 == tm_tag_compare(&(tags_array
->pdata
[i
- 1]), &(tags_array
->pdata
[i
])))
467 tags_array
->pdata
[i
-1] = NULL
;
470 tm_tags_prune(tags_array
);
474 gboolean
tm_tags_custom_dedup(GPtrArray
*tags_array
, TMTagCompareFunc compare_func
)
478 if ((!tags_array
) || (!tags_array
->len
))
480 for (i
= 1; i
< tags_array
->len
; ++i
)
482 if (0 == compare_func(&(tags_array
->pdata
[i
- 1]), &(tags_array
->pdata
[i
])))
483 tags_array
->pdata
[i
-1] = NULL
;
485 tm_tags_prune(tags_array
);
489 gboolean
tm_tags_sort(GPtrArray
*tags_array
, TMTagAttrType
*sort_attributes
, gboolean dedup
)
491 if ((!tags_array
) || (!tags_array
->len
))
493 s_sort_attrs
= sort_attributes
;
495 qsort(tags_array
->pdata
, tags_array
->len
, sizeof(gpointer
), tm_tag_compare
);
498 tm_tags_dedup(tags_array
, sort_attributes
);
502 gboolean
tm_tags_custom_sort(GPtrArray
*tags_array
, TMTagCompareFunc compare_func
, gboolean dedup
)
504 if ((!tags_array
) || (!tags_array
->len
))
506 qsort(tags_array
->pdata
, tags_array
->len
, sizeof(gpointer
), compare_func
);
508 tm_tags_custom_dedup(tags_array
, compare_func
);
512 GPtrArray
*tm_tags_extract(const GPtrArray
*tags_array
, guint tag_types
)
516 if (NULL
== tags_array
)
518 new_tags
= g_ptr_array_new();
519 for (i
=0; i
< tags_array
->len
; ++i
)
521 if (NULL
!= tags_array
->pdata
[i
])
523 if (tag_types
& (((TMTag
*) tags_array
->pdata
[i
])->type
))
524 g_ptr_array_add(new_tags
, tags_array
->pdata
[i
]);
530 void tm_tags_array_free(GPtrArray
*tags_array
, gboolean free_all
)
535 for (i
= 0; i
< tags_array
->len
; ++i
)
536 tm_tag_free(tags_array
->pdata
[i
]);
538 g_ptr_array_free(tags_array
, TRUE
);
540 g_ptr_array_set_size(tags_array
, 0);
544 TMTag
**tm_tags_find (const GPtrArray
*sorted_tags_array
, const char *name
,
545 gboolean partial
, int * tagCount
)
547 static TMTag
*tag
= NULL
;
551 if ((!sorted_tags_array
) || (!sorted_tags_array
->len
))
555 tag
= g_new0(TMTag
, 1);
556 tag
->name
= (char *) name
;
559 result
= (TMTag
**) bsearch(&tag
, sorted_tags_array
->pdata
, sorted_tags_array
->len
560 , sizeof(gpointer
), tm_tag_compare
);
561 /* There can be matches on both sides of result */
564 TMTag
**last
= (TMTag
**) &sorted_tags_array
->pdata
[sorted_tags_array
->len
- 1];
567 /* First look for any matches after result */
570 for (; adv
<= last
&& *adv
; ++ adv
)
572 if (0 != tm_tag_compare(&tag
, adv
))
576 /* Now look for matches from result and below */
577 for (; result
>= (TMTag
**) sorted_tags_array
->pdata
; -- result
)
579 if (0 != tm_tag_compare(&tag
, (TMTag
**) result
))
583 *tagCount
=tagMatches
;
584 ++ result
; /* Correct address for the last successful match */
587 return (TMTag
**) result
;
590 const char *tm_tag_type_name(const TMTag
*tag
)
592 g_return_val_if_fail(tag
, NULL
);
595 case tm_tag_class_t
: return "class";
596 case tm_tag_enum_t
: return "enum";
597 case tm_tag_enumerator_t
: return "enumval";
598 case tm_tag_field_t
: return "field";
599 case tm_tag_function_t
: return "function";
600 case tm_tag_interface_t
: return "interface";
601 case tm_tag_member_t
: return "member";
602 case tm_tag_method_t
: return "method";
603 case tm_tag_namespace_t
: return "namespace";
604 case tm_tag_package_t
: return "package";
605 case tm_tag_prototype_t
: return "prototype";
606 case tm_tag_struct_t
: return "struct";
607 case tm_tag_typedef_t
: return "typedef";
608 case tm_tag_union_t
: return "union";
609 case tm_tag_variable_t
: return "variable";
610 case tm_tag_externvar_t
: return "extern";
611 case tm_tag_macro_t
: return "define";
612 case tm_tag_macro_with_arg_t
: return "macro";
613 case tm_tag_file_t
: return "file";
614 default: return NULL
;
619 TMTagType
tm_tag_name_type(const char* tag_name
)
621 g_return_val_if_fail(tag_name
, tm_tag_undef_t
);
623 if (strcmp(tag_name
, "class") == 0) return tm_tag_class_t
;
624 else if (strcmp(tag_name
, "enum") == 0) return tm_tag_enum_t
;
625 else if (strcmp(tag_name
, "enumval") == 0) return tm_tag_enumerator_t
;
626 else if (strcmp(tag_name
, "field") == 0) return tm_tag_field_t
;
627 else if (strcmp(tag_name
, "function") == 0) return tm_tag_function_t
;
628 else if (strcmp(tag_name
, "interface") == 0) return tm_tag_interface_t
;
629 else if (strcmp(tag_name
, "member") == 0) return tm_tag_member_t
;
630 else if (strcmp(tag_name
, "method") == 0) return tm_tag_method_t
;
631 else if (strcmp(tag_name
, "namespace") == 0) return tm_tag_namespace_t
;
632 else if (strcmp(tag_name
, "package") == 0) return tm_tag_package_t
;
633 else if (strcmp(tag_name
, "prototype") == 0) return tm_tag_prototype_t
;
634 else if (strcmp(tag_name
, "struct") == 0) return tm_tag_struct_t
;
635 else if (strcmp(tag_name
, "typedef") == 0) return tm_tag_typedef_t
;
636 else if (strcmp(tag_name
, "union") == 0) return tm_tag_union_t
;
637 else if (strcmp(tag_name
, "variable") == 0) return tm_tag_variable_t
;
638 else if (strcmp(tag_name
, "extern") == 0) return tm_tag_externvar_t
;
639 else if (strcmp(tag_name
, "define") == 0) return tm_tag_macro_t
;
640 else if (strcmp(tag_name
, "macro") == 0) return tm_tag_macro_with_arg_t
;
641 else if (strcmp(tag_name
, "file") == 0) return tm_tag_file_t
;
642 else return tm_tag_undef_t
;
645 static const char *tm_tag_impl_name(TMTag
*tag
)
647 g_return_val_if_fail(tag
&& (tm_tag_file_t
!= tag
->type
), NULL
);
648 if (TAG_IMPL_VIRTUAL
== tag
->atts
.entry
.impl
)
654 static const char *tm_tag_access_name(TMTag
*tag
)
656 g_return_val_if_fail(tag
&& (tm_tag_file_t
!= tag
->type
), NULL
);
657 if (TAG_ACCESS_PUBLIC
== tag
->atts
.entry
.access
)
659 else if (TAG_ACCESS_PROTECTED
== tag
->atts
.entry
.access
)
661 else if (TAG_ACCESS_PRIVATE
== tag
->atts
.entry
.access
)
667 void tm_tag_print(TMTag
*tag
, FILE *fp
)
669 const char *access
, *impl
, *type
;
672 if (tm_tag_file_t
== tag
->type
)
674 fprintf(fp
, "%s\n", tag
->name
);
677 access
= tm_tag_access_name(tag
);
678 impl
= tm_tag_impl_name(tag
);
679 type
= tm_tag_type_name(tag
);
681 fprintf(fp
, "%s ", access
);
683 fprintf(fp
, "%s ", impl
);
685 fprintf(fp
, "%s ", type
);
686 if (tag
->atts
.entry
.type_ref
[1])
687 fprintf(fp
, "%s ", tag
->atts
.entry
.type_ref
[1]);
688 if (tag
->atts
.entry
.scope
)
689 fprintf(fp
, "%s::", tag
->atts
.entry
.scope
);
690 fprintf(fp
, "%s", tag
->name
);
691 if (tag
->atts
.entry
.arglist
)
692 fprintf(fp
, "%s", tag
->atts
.entry
.arglist
);
693 if (tag
->atts
.entry
.inheritance
)
694 fprintf(fp
, " : from %s", tag
->atts
.entry
.inheritance
);
695 if ((tag
->atts
.entry
.file
) && (tag
->atts
.entry
.line
> 0))
696 fprintf(fp
, "[%s:%ld]", tag
->atts
.entry
.file
->work_object
.file_name
697 , tag
->atts
.entry
.line
);
701 void tm_tags_array_print(GPtrArray
*tags
, FILE *fp
)
705 if (!(tags
&& (tags
->len
> 0) && fp
))
707 for (i
= 0; i
< tags
->len
; ++i
)
709 tag
= TM_TAG(tags
->pdata
[i
]);
710 tm_tag_print(tag
, fp
);
714 gint
tm_tag_scope_depth(const TMTag
*t
)
718 if(!(t
&& t
->atts
.entry
.scope
))
720 for (s
= t
->atts
.entry
.scope
, depth
= 0; s
; s
= strstr(s
, "::"))
728 void tm_tag_chunk_clean (void)
731 g_mem_chunk_clean (s_tag_mem_chunk
);