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.
12 #include <glib-object.h>
18 #define LIBCTAGS_DEFINED
22 #define TAG_NEW(T) ((T) = g_slice_new0(TMTag))
23 #define TAG_FREE(T) g_slice_free(TMTag, (T))
28 static GHashTable
*alive_tags
= NULL
;
30 static void foreach_tags_log(gpointer key
, gpointer value
, gpointer data
)
32 gsize
*ref_count
= data
;
33 const TMTag
*tag
= value
;
35 *ref_count
+= (gsize
) tag
->refcount
;
36 g_debug("Leaked TMTag (%d refs): %s", tag
->refcount
, tag
->name
);
39 static void log_refs_at_exit(void)
43 g_hash_table_foreach(alive_tags
, foreach_tags_log
, &ref_count
);
44 g_debug("TMTag references left at exit: %lu", ref_count
);
47 static TMTag
*log_tag_new(void)
53 alive_tags
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
54 atexit(log_refs_at_exit
);
57 g_hash_table_insert(alive_tags
, tag
, tag
);
62 static void log_tag_free(TMTag
*tag
)
64 g_return_if_fail(alive_tags
!= NULL
);
66 if (! g_hash_table_remove(alive_tags
, tag
)) {
67 g_critical("Freeing invalid TMTag pointer %p", (void *) tag
);
75 #define TAG_NEW(T) ((T) = log_tag_new())
76 #define TAG_FREE(T) log_tag_free(T)
78 #endif /* DEBUG_TAG_REFS */
81 /* Note: To preserve binary compatibility, it is very important
82 that you only *append* to this list ! */
88 TA_POS
, /* Obsolete */
102 static guint
*s_sort_attrs
= NULL
;
103 static gboolean s_partial
= FALSE
;
105 static const char *s_tag_type_names
[] = {
106 "class", /* classes */
107 "enum", /* enumeration names */
108 "enumerator", /* enumerators (values inside an enumeration) */
109 "externvar", /* external variable declarations */
110 "field", /* fields */
111 "function", /* function definitions */
112 "interface", /* interfaces */
113 "macro", /* macro definitions */
114 "member", /* class, struct, and union members */
115 "method", /* methods */
116 "namespace", /* namespaces */
117 "package", /* packages */
118 "prototype", /* function prototypes */
119 "struct", /* structure names */
120 "typedef", /* typedefs */
121 "union", /* union names */
122 "variable", /* variable definitions */
123 "other" /* Other tag type (non C/C++/Java) */
126 static int s_tag_types
[] = {
147 GType
tm_tag_get_type(void)
149 static GType gtype
= 0;
150 if (G_UNLIKELY (gtype
== 0))
152 gtype
= g_boxed_type_register_static("TMTag", (GBoxedCopyFunc
)tm_tag_ref
,
153 (GBoxedFreeFunc
)tm_tag_unref
);
158 static int get_tag_type(const char *tag_name
)
162 g_return_val_if_fail(tag_name
, 0);
163 for (i
=0; i
< sizeof(s_tag_type_names
)/sizeof(char *); ++i
)
165 cmp
= strcmp(tag_name
, s_tag_type_names
[i
]);
167 return s_tag_types
[i
];
171 /* other is not checked above as it is last, not sorted alphabetically */
172 if (strcmp(tag_name
, "other") == 0)
173 return tm_tag_other_t
;
175 fprintf(stderr
, "Unknown tag type %s\n", tag_name
);
177 return tm_tag_undef_t
;
180 static char get_tag_impl(const char *impl
)
182 if ((0 == strcmp("virtual", impl
))
183 || (0 == strcmp("pure virtual", impl
)))
184 return TAG_IMPL_VIRTUAL
;
187 g_warning("Unknown implementation %s", impl
);
189 return TAG_IMPL_UNKNOWN
;
192 static char get_tag_access(const char *access
)
194 if (0 == strcmp("public", access
))
195 return TAG_ACCESS_PUBLIC
;
196 else if (0 == strcmp("protected", access
))
197 return TAG_ACCESS_PROTECTED
;
198 else if (0 == strcmp("private", access
))
199 return TAG_ACCESS_PRIVATE
;
200 else if (0 == strcmp("friend", access
))
201 return TAG_ACCESS_FRIEND
;
202 else if (0 == strcmp("default", access
))
203 return TAG_ACCESS_DEFAULT
;
206 g_warning("Unknown access type %s", access
);
208 return TAG_ACCESS_UNKNOWN
;
211 gboolean
tm_tag_init(TMTag
*tag
, TMSourceFile
*file
, const tagEntryInfo
*tag_entry
)
214 if (NULL
== tag_entry
)
216 /* This is a file tag */
221 tag
->name
= g_strdup(file
->work_object
.file_name
);
222 tag
->type
= tm_tag_file_t
;
223 /* tag->atts.file.timestamp = file->work_object.analyze_time; */
224 tag
->atts
.file
.lang
= file
->lang
;
225 tag
->atts
.file
.inactive
= FALSE
;
231 /* This is a normal tag entry */
232 if (NULL
== tag_entry
->name
)
234 tag
->name
= g_strdup(tag_entry
->name
);
235 tag
->type
= get_tag_type(tag_entry
->kindName
);
236 tag
->atts
.entry
.local
= tag_entry
->isFileScope
;
237 tag
->atts
.entry
.pointerOrder
= 0; /* backward compatibility (use var_type instead) */
238 tag
->atts
.entry
.line
= tag_entry
->lineNumber
;
239 if (NULL
!= tag_entry
->extensionFields
.arglist
)
240 tag
->atts
.entry
.arglist
= g_strdup(tag_entry
->extensionFields
.arglist
);
241 if ((NULL
!= tag_entry
->extensionFields
.scope
[1]) &&
242 (isalpha(tag_entry
->extensionFields
.scope
[1][0]) ||
243 tag_entry
->extensionFields
.scope
[1][0] == '_' ||
244 tag_entry
->extensionFields
.scope
[1][0] == '$'))
245 tag
->atts
.entry
.scope
= g_strdup(tag_entry
->extensionFields
.scope
[1]);
246 if (tag_entry
->extensionFields
.inheritance
!= NULL
)
247 tag
->atts
.entry
.inheritance
= g_strdup(tag_entry
->extensionFields
.inheritance
);
248 if (tag_entry
->extensionFields
.varType
!= NULL
)
249 tag
->atts
.entry
.var_type
= g_strdup(tag_entry
->extensionFields
.varType
);
250 if (tag_entry
->extensionFields
.access
!= NULL
)
251 tag
->atts
.entry
.access
= get_tag_access(tag_entry
->extensionFields
.access
);
252 if (tag_entry
->extensionFields
.implementation
!= NULL
)
253 tag
->atts
.entry
.impl
= get_tag_impl(tag_entry
->extensionFields
.implementation
);
254 if ((tm_tag_macro_t
== tag
->type
) && (NULL
!= tag
->atts
.entry
.arglist
))
255 tag
->type
= tm_tag_macro_with_arg_t
;
256 tag
->atts
.entry
.file
= file
;
261 TMTag
*tm_tag_new(TMSourceFile
*file
, const tagEntryInfo
*tag_entry
)
266 if (FALSE
== tm_tag_init(tag
, file
, tag_entry
))
274 gboolean
tm_tag_init_from_file(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
279 guchar changed_char
= TA_NAME
;
282 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
284 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
286 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
288 if (('\0' == *end
) || ('\n' == *end
))
292 if (NULL
== tag
->name
)
294 if (!isprint(*start
))
297 tag
->name
= g_strdup((gchar
*)start
);
304 tag
->atts
.entry
.line
= atol((gchar
*)start
+ 1);
307 tag
->atts
.entry
.local
= atoi((gchar
*)start
+ 1);
310 tag
->type
= (TMTagType
) atoi((gchar
*)start
+ 1);
313 tag
->atts
.entry
.arglist
= g_strdup((gchar
*)start
+ 1);
316 tag
->atts
.entry
.scope
= g_strdup((gchar
*)start
+ 1);
319 tag
->atts
.entry
.pointerOrder
= atoi((gchar
*)start
+ 1);
322 tag
->atts
.entry
.var_type
= g_strdup((gchar
*)start
+ 1);
325 tag
->atts
.entry
.inheritance
= g_strdup((gchar
*)start
+ 1);
328 if (tm_tag_file_t
!= tag
->type
)
330 g_warning("Got time attribute for non-file tag %s", tag
->name
);
334 tag
->atts
.file
.timestamp
= atol((gchar
*)start
+ 1);
337 if (tm_tag_file_t
!= tag
->type
)
339 g_warning("Got lang attribute for non-file tag %s", tag
->name
);
343 tag
->atts
.file
.lang
= atoi((gchar
*)start
+ 1);
346 if (tm_tag_file_t
!= tag
->type
)
348 g_warning("Got inactive attribute for non-file tag %s", tag
->name
);
352 tag
->atts
.file
.inactive
= (gboolean
) atoi((gchar
*)start
+ 1);
355 tag
->atts
.entry
.access
= *(start
+ 1);
358 tag
->atts
.entry
.impl
= *(start
+ 1);
362 g_warning("Unknown attribute %s", start
+ 1);
369 if (NULL
== tag
->name
)
371 if (tm_tag_file_t
!= tag
->type
)
372 tag
->atts
.entry
.file
= file
;
376 /* alternative parser for Pascal and LaTeX global tags files with the following format
377 * tagname|return value|arglist|description\n */
378 gboolean
tm_tag_init_from_file_alt(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
383 /*guchar changed_char = TA_NAME;*/
386 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
391 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
393 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
395 if (('\0' == *end
) || ('\n' == *end
))
397 /*changed_char = *end;*/
399 if (NULL
== tag
->name
&& !isprint(*start
))
402 fields
= g_strsplit((gchar
*)start
, "|", -1);
403 field_len
= g_strv_length(fields
);
405 if (field_len
>= 1) tag
->name
= g_strdup(fields
[0]);
406 else tag
->name
= NULL
;
407 if (field_len
>= 2 && fields
[1] != NULL
) tag
->atts
.entry
.var_type
= g_strdup(fields
[1]);
408 if (field_len
>= 3 && fields
[2] != NULL
) tag
->atts
.entry
.arglist
= g_strdup(fields
[2]);
409 tag
->type
= tm_tag_prototype_t
;
414 if (NULL
== tag
->name
)
416 if (tm_tag_file_t
!= tag
->type
)
417 tag
->atts
.entry
.file
= file
;
421 /* Reads ctags format (http://ctags.sourceforge.net/FORMAT) */
422 gboolean
tm_tag_init_from_file_ctags(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
428 tag
->type
= tm_tag_function_t
; /* default type is function if no kind is specified */
431 if ((NULL
== fgets(buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
434 while (strncmp(buf
, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
439 if (! (tab
= strchr(p
, '\t')) || p
== tab
)
441 tag
->name
= g_strndup(p
, (gsize
)(tab
- p
));
444 /* tagfile, unused */
445 if (! (tab
= strchr(p
, '\t')))
452 /* Ex command, unused */
453 if (*p
== '/' || *p
== '?')
456 for (++p
; *p
&& *p
!= c
; p
++)
458 if (*p
== '\\' && p
[1])
462 else /* assume a line */
463 tag
->atts
.entry
.line
= atol(p
);
464 tab
= strstr(p
, ";\"");
465 /* read extension fields */
469 while (*p
&& *p
!= '\n' && *p
!= '\r')
472 const gchar
*key
, *value
= NULL
;
474 /* skip leading tabulations */
475 while (*p
&& *p
== '\t') p
++;
476 /* find the separator (:) and end (\t) */
478 while (*end
&& *end
!= '\t' && *end
!= '\n' && *end
!= '\r')
480 if (*end
== ':' && ! value
)
482 *end
= 0; /* terminate the key */
487 /* move p paste the so we won't stop parsing by setting *end=0 below */
488 p
= *end
? end
+ 1 : end
;
489 *end
= 0; /* terminate the value (or key if no value) */
491 if (! value
|| 0 == strcmp(key
, "kind")) /* tag kind */
493 const gchar
*kind
= value
? value
: key
;
495 if (kind
[0] && kind
[1])
496 tag
->type
= get_tag_type(kind
);
501 case 'c': tag
->type
= tm_tag_class_t
; break;
502 case 'd': tag
->type
= tm_tag_macro_t
; break;
503 case 'e': tag
->type
= tm_tag_enumerator_t
; break;
504 case 'F': tag
->type
= tm_tag_file_t
; break;
505 case 'f': tag
->type
= tm_tag_function_t
; break;
506 case 'g': tag
->type
= tm_tag_enum_t
; break;
507 case 'I': tag
->type
= tm_tag_class_t
; break;
508 case 'i': tag
->type
= tm_tag_interface_t
; break;
509 case 'l': tag
->type
= tm_tag_variable_t
; break;
510 case 'M': tag
->type
= tm_tag_macro_t
; break;
511 case 'm': tag
->type
= tm_tag_member_t
; break;
512 case 'n': tag
->type
= tm_tag_namespace_t
; break;
513 case 'P': tag
->type
= tm_tag_package_t
; break;
514 case 'p': tag
->type
= tm_tag_prototype_t
; break;
515 case 's': tag
->type
= tm_tag_struct_t
; break;
516 case 't': tag
->type
= tm_tag_typedef_t
; break;
517 case 'u': tag
->type
= tm_tag_union_t
; break;
518 case 'v': tag
->type
= tm_tag_variable_t
; break;
519 case 'x': tag
->type
= tm_tag_externvar_t
; break;
522 g_warning("Unknown tag kind %c", *kind
);
524 tag
->type
= tm_tag_other_t
; break;
528 else if (0 == strcmp(key
, "inherits")) /* comma-separated list of classes this class inherits from */
530 g_free(tag
->atts
.entry
.inheritance
);
531 tag
->atts
.entry
.inheritance
= g_strdup(value
);
533 else if (0 == strcmp(key
, "implementation")) /* implementation limit */
534 tag
->atts
.entry
.impl
= get_tag_impl(value
);
535 else if (0 == strcmp(key
, "line")) /* line */
536 tag
->atts
.entry
.line
= atol(value
);
537 else if (0 == strcmp(key
, "access")) /* access */
538 tag
->atts
.entry
.access
= get_tag_access(value
);
539 else if (0 == strcmp(key
, "class") ||
540 0 == strcmp(key
, "enum") ||
541 0 == strcmp(key
, "function") ||
542 0 == strcmp(key
, "struct") ||
543 0 == strcmp(key
, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
545 g_free(tag
->atts
.entry
.scope
);
546 tag
->atts
.entry
.scope
= g_strdup(value
);
548 else if (0 == strcmp(key
, "file")) /* static (local) tag */
549 tag
->atts
.entry
.local
= TRUE
;
550 else if (0 == strcmp(key
, "signature")) /* arglist */
552 g_free(tag
->atts
.entry
.arglist
);
553 tag
->atts
.entry
.arglist
= g_strdup(value
);
558 if (tm_tag_file_t
!= tag
->type
)
559 tag
->atts
.entry
.file
= file
;
563 TMTag
*tm_tag_new_from_file(TMSourceFile
*file
, FILE *fp
, gint mode
, TMFileFormat format
)
566 gboolean result
= FALSE
;
572 case TM_FILE_FORMAT_TAGMANAGER
:
573 result
= tm_tag_init_from_file(tag
, file
, fp
);
575 case TM_FILE_FORMAT_PIPE
:
576 result
= tm_tag_init_from_file_alt(tag
, file
, fp
);
578 case TM_FILE_FORMAT_CTAGS
:
579 result
= tm_tag_init_from_file_ctags(tag
, file
, fp
);
588 tag
->atts
.file
.lang
= mode
;
592 gboolean
tm_tag_write(TMTag
*tag
, FILE *fp
, guint attrs
)
594 fprintf(fp
, "%s", tag
->name
);
595 if (attrs
& tm_tag_attr_type_t
)
596 fprintf(fp
, "%c%d", TA_TYPE
, tag
->type
);
597 if (tag
->type
== tm_tag_file_t
)
599 if (attrs
& tm_tag_attr_time_t
)
600 fprintf(fp
, "%c%ld", TA_TIME
, tag
->atts
.file
.timestamp
);
601 if (attrs
& tm_tag_attr_lang_t
)
602 fprintf(fp
, "%c%d", TA_LANG
, tag
->atts
.file
.lang
);
603 if ((attrs
& tm_tag_attr_inactive_t
) && tag
->atts
.file
.inactive
)
604 fprintf(fp
, "%c%d", TA_INACTIVE
, tag
->atts
.file
.inactive
);
608 if ((attrs
& tm_tag_attr_arglist_t
) && (NULL
!= tag
->atts
.entry
.arglist
))
609 fprintf(fp
, "%c%s", TA_ARGLIST
, tag
->atts
.entry
.arglist
);
610 if (attrs
& tm_tag_attr_line_t
)
611 fprintf(fp
, "%c%ld", TA_LINE
, tag
->atts
.entry
.line
);
612 if (attrs
& tm_tag_attr_local_t
)
613 fprintf(fp
, "%c%d", TA_LOCAL
, tag
->atts
.entry
.local
);
614 if ((attrs
& tm_tag_attr_scope_t
) && (NULL
!= tag
->atts
.entry
.scope
))
615 fprintf(fp
, "%c%s", TA_SCOPE
, tag
->atts
.entry
.scope
);
616 if ((attrs
& tm_tag_attr_inheritance_t
) && (NULL
!= tag
->atts
.entry
.inheritance
))
617 fprintf(fp
, "%c%s", TA_INHERITS
, tag
->atts
.entry
.inheritance
);
618 if (attrs
& tm_tag_attr_pointer_t
)
619 fprintf(fp
, "%c%d", TA_POINTER
, tag
->atts
.entry
.pointerOrder
);
620 if ((attrs
& tm_tag_attr_vartype_t
) && (NULL
!= tag
->atts
.entry
.var_type
))
621 fprintf(fp
, "%c%s", TA_VARTYPE
, tag
->atts
.entry
.var_type
);
622 if ((attrs
& tm_tag_attr_access_t
) && (TAG_ACCESS_UNKNOWN
!= tag
->atts
.entry
.access
))
623 fprintf(fp
, "%c%c", TA_ACCESS
, tag
->atts
.entry
.access
);
624 if ((attrs
& tm_tag_attr_impl_t
) && (TAG_IMPL_UNKNOWN
!= tag
->atts
.entry
.impl
))
625 fprintf(fp
, "%c%c", TA_IMPL
, tag
->atts
.entry
.impl
);
627 if (fprintf(fp
, "\n"))
633 static void tm_tag_destroy(TMTag
*tag
)
636 if (tm_tag_file_t
!= tag
->type
)
638 g_free(tag
->atts
.entry
.arglist
);
639 g_free(tag
->atts
.entry
.scope
);
640 g_free(tag
->atts
.entry
.inheritance
);
641 g_free(tag
->atts
.entry
.var_type
);
646 void tm_tag_free(gpointer tag
)
652 void tm_tag_unref(TMTag
*tag
)
654 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
655 * drop-in replacment of it */
656 if (NULL
!= tag
&& g_atomic_int_dec_and_test(&tag
->refcount
))
663 TMTag
*tm_tag_ref(TMTag
*tag
)
665 g_atomic_int_inc(&tag
->refcount
);
669 int tm_tag_compare(const void *ptr1
, const void *ptr2
)
671 unsigned int *sort_attr
;
673 TMTag
*t1
= *((TMTag
**) ptr1
);
674 TMTag
*t2
= *((TMTag
**) ptr2
);
676 if ((NULL
== t1
) || (NULL
== t2
))
678 g_warning("Found NULL tag");
681 if (NULL
== s_sort_attrs
)
684 return strncmp(FALLBACK(t1
->name
, ""), FALLBACK(t2
->name
, ""), strlen(FALLBACK(t1
->name
, "")));
686 return strcmp(FALLBACK(t1
->name
, ""), FALLBACK(t2
->name
, ""));
689 for (sort_attr
= s_sort_attrs
; *sort_attr
!= tm_tag_attr_none_t
; ++ sort_attr
)
693 case tm_tag_attr_name_t
:
695 returnval
= strncmp(FALLBACK(t1
->name
, ""), FALLBACK(t2
->name
, ""), strlen(FALLBACK(t1
->name
, "")));
697 returnval
= strcmp(FALLBACK(t1
->name
, ""), FALLBACK(t2
->name
, ""));
701 case tm_tag_attr_type_t
:
702 if (0 != (returnval
= (t1
->type
- t2
->type
)))
705 case tm_tag_attr_file_t
:
706 if (0 != (returnval
= (t1
->atts
.entry
.file
- t2
->atts
.entry
.file
)))
709 case tm_tag_attr_scope_t
:
710 if (0 != (returnval
= strcmp(FALLBACK(t1
->atts
.entry
.scope
, ""), FALLBACK(t2
->atts
.entry
.scope
, ""))))
713 case tm_tag_attr_arglist_t
:
714 if (0 != (returnval
= strcmp(FALLBACK(t1
->atts
.entry
.arglist
, ""), FALLBACK(t2
->atts
.entry
.arglist
, ""))))
716 int line_diff
= (t1
->atts
.entry
.line
- t2
->atts
.entry
.line
);
718 return line_diff
? line_diff
: returnval
;
721 case tm_tag_attr_vartype_t
:
722 if (0 != (returnval
= strcmp(FALLBACK(t1
->atts
.entry
.var_type
, ""), FALLBACK(t2
->atts
.entry
.var_type
, ""))))
725 case tm_tag_attr_line_t
:
726 if (0 != (returnval
= (t1
->atts
.entry
.line
- t2
->atts
.entry
.line
)))
734 gboolean
tm_tags_prune(GPtrArray
*tags_array
)
737 for (i
=0, count
= 0; i
< tags_array
->len
; ++i
)
739 if (NULL
!= tags_array
->pdata
[i
])
740 tags_array
->pdata
[count
++] = tags_array
->pdata
[i
];
742 tags_array
->len
= count
;
746 gboolean
tm_tags_dedup(GPtrArray
*tags_array
, TMTagAttrType
*sort_attributes
)
750 if ((!tags_array
) || (!tags_array
->len
))
752 s_sort_attrs
= sort_attributes
;
754 for (i
= 1; i
< tags_array
->len
; ++i
)
756 if (0 == tm_tag_compare(&(tags_array
->pdata
[i
- 1]), &(tags_array
->pdata
[i
])))
758 tags_array
->pdata
[i
-1] = NULL
;
761 tm_tags_prune(tags_array
);
765 gboolean
tm_tags_custom_dedup(GPtrArray
*tags_array
, TMTagCompareFunc compare_func
)
769 if ((!tags_array
) || (!tags_array
->len
))
771 for (i
= 1; i
< tags_array
->len
; ++i
)
773 if (0 == compare_func(&(tags_array
->pdata
[i
- 1]), &(tags_array
->pdata
[i
])))
774 tags_array
->pdata
[i
-1] = NULL
;
776 tm_tags_prune(tags_array
);
780 /* Sorts newly-added tags and merges them in order with existing tags.
781 * This is much faster than resorting the whole array.
782 * Note: Having the caller append to the existing array should be faster
783 * than creating a new array which would likely get resized more than once.
784 * tags_array: array with new (perhaps unsorted) tags appended.
785 * orig_len: number of existing tags. */
786 gboolean
tm_tags_merge(GPtrArray
*tags_array
, gsize orig_len
,
787 TMTagAttrType
*sort_attributes
, gboolean dedup
)
789 gpointer
*copy
, *a
, *b
;
792 if ((!tags_array
) || (!tags_array
->len
) || orig_len
>= tags_array
->len
)
795 return tm_tags_sort(tags_array
, sort_attributes
, dedup
);
796 copy_len
= tags_array
->len
- orig_len
;
797 copy
= g_memdup(tags_array
->pdata
+ orig_len
, copy_len
* sizeof(gpointer
));
798 s_sort_attrs
= sort_attributes
;
800 /* enforce copy sorted with same attributes for merge */
801 qsort(copy
, copy_len
, sizeof(gpointer
), tm_tag_compare
);
802 a
= tags_array
->pdata
+ orig_len
- 1;
803 b
= copy
+ copy_len
- 1;
804 for (i
= tags_array
->len
- 1;; i
--)
806 gint cmp
= tm_tag_compare(a
, b
);
808 tags_array
->pdata
[i
] = (cmp
>= 0) ? *a
-- : *b
--;
809 if (a
< tags_array
->pdata
)
811 /* include remainder of copy as well as current value of b */
812 memcpy(tags_array
->pdata
, copy
, ((b
+ 1) - copy
) * sizeof(gpointer
));
816 break; /* remaining elements of 'a' are in place already */
822 tm_tags_dedup(tags_array
, sort_attributes
);
826 gboolean
tm_tags_sort(GPtrArray
*tags_array
, TMTagAttrType
*sort_attributes
, gboolean dedup
)
828 if ((!tags_array
) || (!tags_array
->len
))
830 s_sort_attrs
= sort_attributes
;
832 qsort(tags_array
->pdata
, tags_array
->len
, sizeof(gpointer
), tm_tag_compare
);
835 tm_tags_dedup(tags_array
, sort_attributes
);
839 gboolean
tm_tags_custom_sort(GPtrArray
*tags_array
, TMTagCompareFunc compare_func
, gboolean dedup
)
841 if ((!tags_array
) || (!tags_array
->len
))
843 qsort(tags_array
->pdata
, tags_array
->len
, sizeof(gpointer
), compare_func
);
845 tm_tags_custom_dedup(tags_array
, compare_func
);
849 GPtrArray
*tm_tags_extract(GPtrArray
*tags_array
, guint tag_types
)
853 if (NULL
== tags_array
)
855 new_tags
= g_ptr_array_new();
856 for (i
=0; i
< tags_array
->len
; ++i
)
858 if (NULL
!= tags_array
->pdata
[i
])
860 if (tag_types
& (((TMTag
*) tags_array
->pdata
[i
])->type
))
861 g_ptr_array_add(new_tags
, tags_array
->pdata
[i
]);
867 void tm_tags_array_free(GPtrArray
*tags_array
, gboolean free_all
)
872 for (i
= 0; i
< tags_array
->len
; ++i
)
873 tm_tag_unref(tags_array
->pdata
[i
]);
875 g_ptr_array_free(tags_array
, TRUE
);
877 g_ptr_array_set_size(tags_array
, 0);
881 TMTag
**tm_tags_find(const GPtrArray
*sorted_tags_array
, const char *name
,
882 gboolean partial
, int * tagCount
)
884 static TMTag
*tag
= NULL
;
888 if ((!sorted_tags_array
) || (!sorted_tags_array
->len
))
892 tag
= g_new0(TMTag
, 1);
893 tag
->name
= (char *) name
;
896 result
= (TMTag
**) bsearch(&tag
, sorted_tags_array
->pdata
, sorted_tags_array
->len
897 , sizeof(gpointer
), tm_tag_compare
);
898 /* There can be matches on both sides of result */
901 TMTag
**last
= (TMTag
**) &sorted_tags_array
->pdata
[sorted_tags_array
->len
- 1];
904 /* First look for any matches after result */
907 for (; adv
<= last
&& *adv
; ++ adv
)
909 if (0 != tm_tag_compare(&tag
, adv
))
913 /* Now look for matches from result and below */
914 for (; result
>= (TMTag
**) sorted_tags_array
->pdata
; -- result
)
916 if (0 != tm_tag_compare(&tag
, (TMTag
**) result
))
920 *tagCount
=tagMatches
;
921 ++ result
; /* Correct address for the last successful match */
924 return (TMTag
**) result
;
927 const char *tm_tag_type_name(const TMTag
*tag
)
929 g_return_val_if_fail(tag
, NULL
);
932 case tm_tag_class_t
: return "class";
933 case tm_tag_enum_t
: return "enum";
934 case tm_tag_enumerator_t
: return "enumval";
935 case tm_tag_field_t
: return "field";
936 case tm_tag_function_t
: return "function";
937 case tm_tag_interface_t
: return "interface";
938 case tm_tag_member_t
: return "member";
939 case tm_tag_method_t
: return "method";
940 case tm_tag_namespace_t
: return "namespace";
941 case tm_tag_package_t
: return "package";
942 case tm_tag_prototype_t
: return "prototype";
943 case tm_tag_struct_t
: return "struct";
944 case tm_tag_typedef_t
: return "typedef";
945 case tm_tag_union_t
: return "union";
946 case tm_tag_variable_t
: return "variable";
947 case tm_tag_externvar_t
: return "extern";
948 case tm_tag_macro_t
: return "define";
949 case tm_tag_macro_with_arg_t
: return "macro";
950 case tm_tag_file_t
: return "file";
951 default: return NULL
;
956 TMTagType
tm_tag_name_type(const char* tag_name
)
958 g_return_val_if_fail(tag_name
, tm_tag_undef_t
);
960 if (strcmp(tag_name
, "class") == 0) return tm_tag_class_t
;
961 else if (strcmp(tag_name
, "enum") == 0) return tm_tag_enum_t
;
962 else if (strcmp(tag_name
, "enumval") == 0) return tm_tag_enumerator_t
;
963 else if (strcmp(tag_name
, "field") == 0) return tm_tag_field_t
;
964 else if (strcmp(tag_name
, "function") == 0) return tm_tag_function_t
;
965 else if (strcmp(tag_name
, "interface") == 0) return tm_tag_interface_t
;
966 else if (strcmp(tag_name
, "member") == 0) return tm_tag_member_t
;
967 else if (strcmp(tag_name
, "method") == 0) return tm_tag_method_t
;
968 else if (strcmp(tag_name
, "namespace") == 0) return tm_tag_namespace_t
;
969 else if (strcmp(tag_name
, "package") == 0) return tm_tag_package_t
;
970 else if (strcmp(tag_name
, "prototype") == 0) return tm_tag_prototype_t
;
971 else if (strcmp(tag_name
, "struct") == 0) return tm_tag_struct_t
;
972 else if (strcmp(tag_name
, "typedef") == 0) return tm_tag_typedef_t
;
973 else if (strcmp(tag_name
, "union") == 0) return tm_tag_union_t
;
974 else if (strcmp(tag_name
, "variable") == 0) return tm_tag_variable_t
;
975 else if (strcmp(tag_name
, "extern") == 0) return tm_tag_externvar_t
;
976 else if (strcmp(tag_name
, "define") == 0) return tm_tag_macro_t
;
977 else if (strcmp(tag_name
, "macro") == 0) return tm_tag_macro_with_arg_t
;
978 else if (strcmp(tag_name
, "file") == 0) return tm_tag_file_t
;
979 else return tm_tag_undef_t
;
982 static const char *tm_tag_impl_name(TMTag
*tag
)
984 g_return_val_if_fail(tag
&& (tm_tag_file_t
!= tag
->type
), NULL
);
985 if (TAG_IMPL_VIRTUAL
== tag
->atts
.entry
.impl
)
991 static const char *tm_tag_access_name(TMTag
*tag
)
993 g_return_val_if_fail(tag
&& (tm_tag_file_t
!= tag
->type
), NULL
);
994 if (TAG_ACCESS_PUBLIC
== tag
->atts
.entry
.access
)
996 else if (TAG_ACCESS_PROTECTED
== tag
->atts
.entry
.access
)
998 else if (TAG_ACCESS_PRIVATE
== tag
->atts
.entry
.access
)
1004 void tm_tag_print(TMTag
*tag
, FILE *fp
)
1006 const char *laccess
, *impl
, *type
;
1009 if (tm_tag_file_t
== tag
->type
)
1011 fprintf(fp
, "%s\n", tag
->name
);
1014 laccess
= tm_tag_access_name(tag
);
1015 impl
= tm_tag_impl_name(tag
);
1016 type
= tm_tag_type_name(tag
);
1018 fprintf(fp
, "%s ", laccess
);
1020 fprintf(fp
, "%s ", impl
);
1022 fprintf(fp
, "%s ", type
);
1023 if (tag
->atts
.entry
.var_type
)
1024 fprintf(fp
, "%s ", tag
->atts
.entry
.var_type
);
1025 if (tag
->atts
.entry
.scope
)
1026 fprintf(fp
, "%s::", tag
->atts
.entry
.scope
);
1027 fprintf(fp
, "%s", tag
->name
);
1028 if (tag
->atts
.entry
.arglist
)
1029 fprintf(fp
, "%s", tag
->atts
.entry
.arglist
);
1030 if (tag
->atts
.entry
.inheritance
)
1031 fprintf(fp
, " : from %s", tag
->atts
.entry
.inheritance
);
1032 if ((tag
->atts
.entry
.file
) && (tag
->atts
.entry
.line
> 0))
1033 fprintf(fp
, "[%s:%ld]", tag
->atts
.entry
.file
->work_object
.file_name
1034 , tag
->atts
.entry
.line
);
1038 void tm_tags_array_print(GPtrArray
*tags
, FILE *fp
)
1042 if (!(tags
&& (tags
->len
> 0) && fp
))
1044 for (i
= 0; i
< tags
->len
; ++i
)
1046 tag
= TM_TAG(tags
->pdata
[i
]);
1047 tm_tag_print(tag
, fp
);
1051 gint
tm_tag_scope_depth(const TMTag
*t
)
1055 if(!(t
&& t
->atts
.entry
.scope
))
1057 for (s
= t
->atts
.entry
.scope
, depth
= 0; s
; s
= strstr(s
, "::"))