1 /*
2 *
3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
4 * Copyright 2005 The Geany contributors
5 *
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
8 *
9 */
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <glib-object.h>
14
15 #include "tm_tag.h"
16 #include "ctags-api.h"
17
18
19 #define TAG_NEW(T) ((T) = g_slice_new0(TMTag))
20 #define TAG_FREE(T) g_slice_free(TMTag, (T))
21
22
23 #ifdef DEBUG_TAG_REFS
24
25 static GHashTable *alive_tags = NULL;
26
foreach_tags_log(gpointer key,gpointer value,gpointer data)27 static void foreach_tags_log(gpointer key, gpointer value, gpointer data)
28 {
29 gsize *ref_count = data;
30 const TMTag *tag = value;
31
32 *ref_count += (gsize) tag->refcount;
33 g_debug("Leaked TMTag (%d refs): %s", tag->refcount, tag->name);
34 }
35
log_refs_at_exit(void)36 static void log_refs_at_exit(void)
37 {
38 gsize ref_count = 0;
39
40 g_hash_table_foreach(alive_tags, foreach_tags_log, &ref_count);
41 g_debug("TMTag references left at exit: %lu", ref_count);
42 }
43
log_tag_new(void)44 static TMTag *log_tag_new(void)
45 {
46 TMTag *tag;
47
48 if (! alive_tags)
49 {
50 alive_tags = g_hash_table_new(g_direct_hash, g_direct_equal);
51 atexit(log_refs_at_exit);
52 }
53 TAG_NEW(tag);
54 g_hash_table_insert(alive_tags, tag, tag);
55
56 return tag;
57 }
58
log_tag_free(TMTag * tag)59 static void log_tag_free(TMTag *tag)
60 {
61 g_return_if_fail(alive_tags != NULL);
62
63 if (! g_hash_table_remove(alive_tags, tag)) {
64 g_critical("Freeing invalid TMTag pointer %p", (void *) tag);
65 } else {
66 TAG_FREE(tag);
67 }
68 }
69
70 #undef TAG_NEW
71 #undef TAG_FREE
72 #define TAG_NEW(T) ((T) = log_tag_new())
73 #define TAG_FREE(T) log_tag_free(T)
74
75 #endif /* DEBUG_TAG_REFS */
76
77
78 typedef struct
79 {
80 guint *sort_attrs;
81 gboolean partial;
82 const GPtrArray *tags_array;
83 gboolean first;
84 } TMSortOptions;
85
86 /** Gets the GType for a TMTag.
87 *
88 * @return TMTag type
89 * @since 1.32 (API 233) */
90 GEANY_API_SYMBOL
tm_tag_get_type(void)91 GType tm_tag_get_type(void)
92 {
93 static GType gtype = 0;
94 if (G_UNLIKELY (gtype == 0))
95 {
96 gtype = g_boxed_type_register_static("TMTag", (GBoxedCopyFunc)tm_tag_ref,
97 (GBoxedFreeFunc)tm_tag_unref);
98 }
99 return gtype;
100 }
101
102 /*
103 Creates a new tag structure and returns a pointer to it.
104 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
105 */
tm_tag_new(void)106 TMTag *tm_tag_new(void)
107 {
108 TMTag *tag;
109
110 TAG_NEW(tag);
111 tag->refcount = 1;
112
113 return tag;
114 }
115
116 /*
117 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
118 @param tag The TMTag structure to destroy
119 @see tm_tag_free()
120 */
tm_tag_destroy(TMTag * tag)121 static void tm_tag_destroy(TMTag *tag)
122 {
123 g_free(tag->name);
124 g_free(tag->arglist);
125 g_free(tag->scope);
126 g_free(tag->inheritance);
127 g_free(tag->var_type);
128 }
129
130
131 /*
132 Drops a reference from a TMTag. If the reference count reaches 0, this function
133 destroys all data in the tag and frees the tag structure as well.
134 @param tag Pointer to a TMTag structure
135 */
tm_tag_unref(TMTag * tag)136 void tm_tag_unref(TMTag *tag)
137 {
138 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
139 * drop-in replacment of it */
140 if (NULL != tag && g_atomic_int_dec_and_test(&tag->refcount))
141 {
142 tm_tag_destroy(tag);
143 TAG_FREE(tag);
144 }
145 }
146
147 /*
148 Adds a reference to a TMTag.
149 @param tag Pointer to a TMTag structure
150 @return the passed-in TMTag
151 */
tm_tag_ref(TMTag * tag)152 TMTag *tm_tag_ref(TMTag *tag)
153 {
154 g_atomic_int_inc(&tag->refcount);
155 return tag;
156 }
157
158 /*
159 Inbuilt tag comparison function.
160 */
tm_tag_compare(gconstpointer ptr1,gconstpointer ptr2,gpointer user_data)161 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
162 {
163 unsigned int *sort_attr;
164 int returnval = 0;
165 TMTag *t1 = *((TMTag **) ptr1);
166 TMTag *t2 = *((TMTag **) ptr2);
167 TMSortOptions *sort_options = user_data;
168
169 if ((NULL == t1) || (NULL == t2))
170 {
171 g_warning("Found NULL tag");
172 return t2 - t1;
173 }
174 if (NULL == sort_options->sort_attrs)
175 {
176 if (sort_options->partial)
177 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
178 else
179 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
180 }
181
182 for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
183 {
184 switch (*sort_attr)
185 {
186 case tm_tag_attr_name_t:
187 if (sort_options->partial)
188 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
189 else
190 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
191 break;
192 case tm_tag_attr_file_t:
193 returnval = t1->file - t2->file;
194 break;
195 case tm_tag_attr_line_t:
196 returnval = t1->line - t2->line;
197 break;
198 case tm_tag_attr_type_t:
199 returnval = t1->type - t2->type;
200 break;
201 case tm_tag_attr_scope_t:
202 returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
203 break;
204 case tm_tag_attr_arglist_t:
205 returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
206 if (returnval != 0)
207 {
208 int line_diff = (t1->line - t2->line);
209
210 returnval = line_diff ? line_diff : returnval;
211 }
212 break;
213 case tm_tag_attr_vartype_t:
214 returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
215 break;
216 }
217 }
218 return returnval;
219 }
220
tm_tags_equal(const TMTag * a,const TMTag * b)221 gboolean tm_tags_equal(const TMTag *a, const TMTag *b)
222 {
223 if (a == b)
224 return TRUE;
225
226 return (a->line == b->line &&
227 a->file == b->file /* ptr comparison */ &&
228 strcmp(FALLBACK(a->name, ""), FALLBACK(b->name, "")) == 0 &&
229 a->type == b->type &&
230 a->local == b->local &&
231 a->pointerOrder == b->pointerOrder &&
232 a->access == b->access &&
233 a->impl == b->impl &&
234 a->lang == b->lang &&
235 strcmp(FALLBACK(a->scope, ""), FALLBACK(b->scope, "")) == 0 &&
236 strcmp(FALLBACK(a->arglist, ""), FALLBACK(b->arglist, "")) == 0 &&
237 strcmp(FALLBACK(a->inheritance, ""), FALLBACK(b->inheritance, "")) == 0 &&
238 strcmp(FALLBACK(a->var_type, ""), FALLBACK(b->var_type, "")) == 0);
239 }
240
241 /*
242 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
243 this function substitutes duplicate entries with NULL
244 @param tags_array Array of tags to dedup
245 */
tm_tags_prune(GPtrArray * tags_array)246 void tm_tags_prune(GPtrArray *tags_array)
247 {
248 guint i, count;
249
250 g_return_if_fail(tags_array);
251
252 for (i=0, count = 0; i < tags_array->len; ++i)
253 {
254 if (NULL != tags_array->pdata[i])
255 tags_array->pdata[count++] = tags_array->pdata[i];
256 }
257 tags_array->len = count;
258 }
259
260 /*
261 Deduplicates an array on tags using the inbuilt comparison function based on
262 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
263 @param tags_array Array of tags to dedup.
264 @param sort_attributes Attributes the array is sorted on. They will be deduped
265 on the same criteria.
266 */
tm_tags_dedup(GPtrArray * tags_array,TMTagAttrType * sort_attributes,gboolean unref_duplicates)267 void tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
268 {
269 TMSortOptions sort_options;
270 guint i;
271
272 g_return_if_fail(tags_array);
273 if (tags_array->len < 2)
274 return;
275
276 sort_options.sort_attrs = sort_attributes;
277 sort_options.partial = FALSE;
278 for (i = 1; i < tags_array->len; ++i)
279 {
280 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
281 {
282 if (unref_duplicates)
283 tm_tag_unref(tags_array->pdata[i-1]);
284 tags_array->pdata[i-1] = NULL;
285 }
286 }
287 tm_tags_prune(tags_array);
288 }
289
290 /*
291 Sort an array of tags on the specified attribuites using the inbuilt comparison
292 function.
293 @param tags_array The array of tags to be sorted
294 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
295 @param dedup Whether to deduplicate the sorted array
296 */
tm_tags_sort(GPtrArray * tags_array,TMTagAttrType * sort_attributes,gboolean dedup,gboolean unref_duplicates)297 void tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
298 gboolean dedup, gboolean unref_duplicates)
299 {
300 TMSortOptions sort_options;
301
302 g_return_if_fail(tags_array);
303
304 sort_options.sort_attrs = sort_attributes;
305 sort_options.partial = FALSE;
306 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
307 if (dedup)
308 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
309 }
310
tm_tags_remove_file_tags(TMSourceFile * source_file,GPtrArray * tags_array)311 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
312 {
313 guint i;
314
315 /* Now we choose between an algorithm with complexity O(tags_array->len) and
316 * O(source_file->tags_array->len * log(tags_array->len)). The latter algorithm
317 * is better when tags_array contains many times more tags than
318 * source_file->tags_array so instead of trying to find the removed tags
319 * linearly, binary search is used. The constant 20 is more or less random
320 * but seems to work well. It's exact value isn't so critical because it's
321 * the extremes where the difference is the biggest: when
322 * source_file->tags_array->len == tags_array->len (single file open) and
323 * source_file->tags_array->len << tags_array->len (the number of tags
324 * from the file is a small fraction of all tags).
325 */
326 if (source_file->tags_array->len != 0 &&
327 tags_array->len / source_file->tags_array->len < 20)
328 {
329 for (i = 0; i < tags_array->len; i++)
330 {
331 TMTag *tag = tags_array->pdata[i];
332
333 if (tag->file == source_file)
334 tags_array->pdata[i] = NULL;
335 }
336 }
337 else
338 {
339 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
340
341 for (i = 0; i < source_file->tags_array->len; i++)
342 {
343 guint j;
344 guint tag_count;
345 TMTag **found;
346 TMTag *tag = source_file->tags_array->pdata[i];
347
348 found = tm_tags_find(tags_array, tag->name, FALSE, &tag_count);
349
350 for (j = 0; j < tag_count; j++)
351 {
352 if (*found != NULL && (*found)->file == source_file)
353 {
354 /* we cannot set the pointer to NULL now because the search wouldn't work */
355 g_ptr_array_add(to_delete, found);
356 /* no break - if there are multiple tags of the same name, we would
357 * always find the first instance and wouldn't remove others; duplicates
358 * in the to_delete list aren't a problem */
359 }
360 found++;
361 }
362 }
363
364 for (i = 0; i < to_delete->len; i++)
365 {
366 TMTag **tag = to_delete->pdata[i];
367 *tag = NULL;
368 }
369 g_ptr_array_free(to_delete, TRUE);
370 }
371
372 tm_tags_prune(tags_array);
373 }
374
375 /* Optimized merge sort for merging sorted values from one array to another
376 * where one of the arrays is much smaller than the other.
377 * The merge complexity depends mostly on the size of the small array
378 * and is almost independent of the size of the big array.
379 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
merge(GPtrArray * big_array,GPtrArray * small_array,TMSortOptions * sort_options,gboolean unref_duplicates)380 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
381 TMSortOptions *sort_options, gboolean unref_duplicates) {
382 guint i1 = 0; /* index to big_array */
383 guint i2 = 0; /* index to small_array */
384 guint initial_step;
385 guint step;
386 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
387 #ifdef TM_DEBUG
388 guint cmpnum = 0;
389 #endif
390
391 /* swap the arrays if len(small) > len(big) */
392 if (small_array->len > big_array->len)
393 {
394 GPtrArray *tmp = small_array;
395 small_array = big_array;
396 big_array = tmp;
397 }
398
399 /* on average, we are merging a value from small_array every
400 * len(big_array) / len(small_array) values - good approximation for fast jump
401 * step size */
402 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
403 initial_step = initial_step > 4 ? initial_step : 1;
404 step = initial_step;
405
406 while (i1 < big_array->len && i2 < small_array->len)
407 {
408 gpointer val1;
409 gpointer val2 = small_array->pdata[i2];
410
411 if (step > 4) /* fast path start */
412 {
413 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
414
415 val1 = big_array->pdata[j1];
416 #ifdef TM_DEBUG
417 cmpnum++;
418 #endif
419 /* if the value in big_array after making the big step is still smaller
420 * than the value in small_array, we can copy all the values inbetween
421 * into the result without making expensive string comparisons */
422 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
423 {
424 while (i1 <= j1)
425 {
426 val1 = big_array->pdata[i1];
427 g_ptr_array_add(res_array, val1);
428 i1++;
429 }
430 }
431 else
432 {
433 /* lower the step and try again */
434 step /= 2;
435 }
436 } /* fast path end */
437 else
438 {
439 gint cmpval;
440
441 #ifdef TM_DEBUG
442 cmpnum++;
443 #endif
444 val1 = big_array->pdata[i1];
445 cmpval = tm_tag_compare(&val1, &val2, sort_options);
446 if (cmpval < 0)
447 {
448 g_ptr_array_add(res_array, val1);
449 i1++;
450 }
451 else
452 {
453 g_ptr_array_add(res_array, val2);
454 i2++;
455 /* value from small_array gets merged - reset the step size */
456 step = initial_step;
457 if (cmpval == 0)
458 {
459 i1++; /* remove the duplicate, keep just the newly merged value */
460 if (unref_duplicates)
461 tm_tag_unref(val1);
462 }
463 }
464 }
465 }
466
467 /* end of one of the arrays reached - copy the rest from the other array */
468 while (i1 < big_array->len)
469 g_ptr_array_add(res_array, big_array->pdata[i1++]);
470 while (i2 < small_array->len)
471 g_ptr_array_add(res_array, small_array->pdata[i2++]);
472
473 #ifdef TM_DEBUG
474 printf("cmpnums: %u\n", cmpnum);
475 printf("total tags: %u\n", big_array->len);
476 printf("merged tags: %u\n\n", small_array->len);
477 #endif
478
479 return res_array;
480 }
481
tm_tags_merge(GPtrArray * big_array,GPtrArray * small_array,TMTagAttrType * sort_attributes,gboolean unref_duplicates)482 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
483 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
484 {
485 GPtrArray *res_array;
486 TMSortOptions sort_options;
487
488 sort_options.sort_attrs = sort_attributes;
489 sort_options.partial = FALSE;
490 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
491 return res_array;
492 }
493
494 /*
495 This function will extract the tags of the specified types from an array of tags.
496 The returned value is a GPtrArray which should be free-d with a call to
497 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
498 are not duplicated.
499 @param tags_array The original array of tags
500 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
501 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
502 the original array.
503 @return an array of tags (NULL on failure)
504 */
tm_tags_extract(GPtrArray * tags_array,TMTagType tag_types)505 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
506 {
507 GPtrArray *new_tags;
508 guint i;
509
510 g_return_val_if_fail(tags_array, NULL);
511
512 new_tags = g_ptr_array_new();
513 for (i=0; i < tags_array->len; ++i)
514 {
515 if (NULL != tags_array->pdata[i])
516 {
517 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
518 g_ptr_array_add(new_tags, tags_array->pdata[i]);
519 }
520 }
521 return new_tags;
522 }
523
524 /*
525 Completely frees an array of tags.
526 @param tags_array Array of tags to be freed.
527 @param free_array Whether the GptrArray is to be freed as well.
528 */
tm_tags_array_free(GPtrArray * tags_array,gboolean free_all)529 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
530 {
531 if (tags_array)
532 {
533 guint i;
534 for (i = 0; i < tags_array->len; ++i)
535 tm_tag_unref(tags_array->pdata[i]);
536 if (free_all)
537 g_ptr_array_free(tags_array, TRUE);
538 else
539 g_ptr_array_set_size(tags_array, 0);
540 }
541 }
542
543 /* copy/pasted bsearch() from libc extended with user_data for comparison function
544 * and using glib types */
binary_search(gpointer key,gpointer base,size_t nmemb,GCompareDataFunc compar,gpointer user_data)545 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
546 GCompareDataFunc compar, gpointer user_data)
547 {
548 gsize l, u, idx;
549 gpointer p;
550 gint comparison;
551
552 l = 0;
553 u = nmemb;
554 while (l < u)
555 {
556 idx = (l + u) / 2;
557 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
558 comparison = (*compar) (key, p, user_data);
559 if (comparison < 0)
560 u = idx;
561 else if (comparison > 0)
562 l = idx + 1;
563 else
564 return (gpointer) p;
565 }
566
567 return NULL;
568 }
569
tag_search_cmp(gconstpointer ptr1,gconstpointer ptr2,gpointer user_data)570 static gint tag_search_cmp(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
571 {
572 gint res = tm_tag_compare(ptr1, ptr2, user_data);
573
574 if (res == 0)
575 {
576 TMSortOptions *sort_options = user_data;
577 const GPtrArray *tags_array = sort_options->tags_array;
578 TMTag **tag = (TMTag **) ptr2;
579
580 /* if previous/next (depending on sort options) tag equal, we haven't
581 * found the first/last tag in a sequence of equal tags yet */
582 if (sort_options->first && ptr2 != &tags_array->pdata[0]) {
583 if (tm_tag_compare(ptr1, tag - 1, user_data) == 0)
584 return -1;
585 }
586 else if (!sort_options->first && ptr2 != &tags_array->pdata[tags_array->len-1])
587 {
588 if (tm_tag_compare(ptr1, tag + 1, user_data) == 0)
589 return 1;
590 }
591 }
592 return res;
593 }
594
595 /*
596 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
597 The passed array of tags must be already sorted by name (searched with binary search).
598 @param tags_array Tag array (sorted on name)
599 @param name Name of the tag to locate.
600 @param partial If TRUE, matches the first part of the name instead of doing exact match.
601 @param tagCount Return location of the matched tags.
602 */
tm_tags_find(const GPtrArray * tags_array,const char * name,gboolean partial,guint * tagCount)603 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
604 gboolean partial, guint *tagCount)
605 {
606 TMTag *tag, **first;
607 TMSortOptions sort_options;
608
609 *tagCount = 0;
610 if (!tags_array || !tags_array->len)
611 return NULL;
612
613 tag = g_new0(TMTag, 1);
614 tag->name = (char *) name;
615
616 sort_options.sort_attrs = NULL;
617 sort_options.partial = partial;
618 sort_options.tags_array = tags_array;
619 sort_options.first = TRUE;
620 first = (TMTag **)binary_search(&tag, tags_array->pdata, tags_array->len,
621 tag_search_cmp, &sort_options);
622
623 if (first)
624 {
625 TMTag **last;
626 unsigned first_pos;
627
628 sort_options.first = FALSE;
629 first_pos = first - (TMTag **)tags_array->pdata;
630 /* search between the first element and end */
631 last = (TMTag **)binary_search(&tag, first, tags_array->len - first_pos,
632 tag_search_cmp, &sort_options);
633 *tagCount = last - first + 1;
634 }
635
636 g_free(tag);
637 return (TMTag **) first;
638 }
639
640 /* Returns TMTag which "own" given line
641 @param line Current line in edited file.
642 @param file_tags A GPtrArray of edited file TMTag pointers.
643 @param tag_types the tag types to include in the match
644 @return TMTag pointers to owner tag. */
645 const TMTag *
tm_get_current_tag(GPtrArray * file_tags,const gulong line,const TMTagType tag_types)646 tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types)
647 {
648 TMTag *matching_tag = NULL;
649 if (file_tags && file_tags->len)
650 {
651 guint i;
652 gulong matching_line = 0;
653
654 for (i = 0; (i < file_tags->len); ++i)
655 {
656 TMTag *tag = TM_TAG (file_tags->pdata[i]);
657 if (tag && tag->type & tag_types &&
658 tag->line <= line && tag->line > matching_line)
659 {
660 matching_tag = tag;
661 matching_line = tag->line;
662 }
663 }
664 }
665 return matching_tag;
666 }
667
tm_tag_is_anon(const TMTag * tag)668 gboolean tm_tag_is_anon(const TMTag *tag)
669 {
670 guint i;
671 char dummy;
672
673 if (tag->lang == TM_PARSER_C || tag->lang == TM_PARSER_CPP)
674 return sscanf(tag->name, "anon_%*[a-z]_%u%c", &i, &dummy) == 1;
675 else if (tag->lang == TM_PARSER_FORTRAN || tag->lang == TM_PARSER_F77)
676 return sscanf(tag->name, "Structure#%u%c", &i, &dummy) == 1 ||
677 sscanf(tag->name, "Interface#%u%c", &i, &dummy) == 1 ||
678 sscanf(tag->name, "Enum#%u%c", &i, &dummy) == 1;
679 return FALSE;
680 }
681
682
683 #ifdef TM_DEBUG /* various debugging functions */
684
685 /*
686 Returns the type of tag as a string
687 @param tag The tag whose type is required
688 */
tm_tag_type_name(const TMTag * tag)689 const char *tm_tag_type_name(const TMTag *tag)
690 {
691 g_return_val_if_fail(tag, NULL);
692
693 return tm_ctags_get_kind_name(tm_parser_get_tag_kind(tag->type, tag->lang), tag->lang);
694 }
695
696 /*
697 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
698 @param tag_name Name of the tag type
699 */
tm_tag_name_type(const char * tag_name,TMParserType lang)700 TMTagType tm_tag_name_type(const char* tag_name, TMParserType lang)
701 {
702 g_return_val_if_fail(tag_name, tm_tag_undef_t);
703
704 return tm_parser_get_tag_type(tm_ctags_get_kind_from_name(tag_name, lang), lang);
705 }
706
tm_tag_impl_name(TMTag * tag)707 static const char *tm_tag_impl_name(TMTag *tag)
708 {
709 g_return_val_if_fail(tag, NULL);
710 if (TAG_IMPL_VIRTUAL == tag->impl)
711 return "virtual";
712 else
713 return NULL;
714 }
715
tm_tag_access_name(TMTag * tag)716 static const char *tm_tag_access_name(TMTag *tag)
717 {
718 g_return_val_if_fail(tag, NULL);
719 if (TAG_ACCESS_PUBLIC == tag->access)
720 return "public";
721 else if (TAG_ACCESS_PROTECTED == tag->access)
722 return "protected";
723 else if (TAG_ACCESS_PRIVATE == tag->access)
724 return "private";
725 else
726 return NULL;
727 }
728
729 /*
730 Prints information about a tag to the given file pointer.
731 @param tag The tag whose info is required.
732 @param fp The file pointer of the file to print the info to.
733 */
tm_tag_print(TMTag * tag,FILE * fp)734 void tm_tag_print(TMTag *tag, FILE *fp)
735 {
736 const char *laccess, *impl, *type;
737 if (!tag || !fp)
738 return;
739 laccess = tm_tag_access_name(tag);
740 impl = tm_tag_impl_name(tag);
741 type = tm_tag_type_name(tag);
742 if (laccess)
743 fprintf(fp, "%s ", laccess);
744 if (impl)
745 fprintf(fp, "%s ", impl);
746 if (type)
747 fprintf(fp, "%s ", type);
748 if (tag->var_type)
749 fprintf(fp, "%s ", tag->var_type);
750 if (tag->scope)
751 fprintf(fp, "%s::", tag->scope);
752 fprintf(fp, "%s", tag->name);
753 if (tag->arglist)
754 fprintf(fp, "%s", tag->arglist);
755 if (tag->inheritance)
756 fprintf(fp, " : from %s", tag->inheritance);
757 if ((tag->file) && (tag->line > 0))
758 fprintf(fp, "[%s:%ld]", tag->file->file_name
759 , tag->line);
760 fprintf(fp, "\n");
761 }
762
763 /*
764 Prints info about all tags in the array to the given file pointer.
765 */
tm_tags_array_print(GPtrArray * tags,FILE * fp)766 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
767 {
768 guint i;
769 TMTag *tag;
770 if (!(tags && (tags->len > 0) && fp))
771 return;
772 for (i = 0; i < tags->len; ++i)
773 {
774 tag = TM_TAG(tags->pdata[i]);
775 tm_tag_print(tag, fp);
776 }
777 }
778
779 /*
780 Returns the depth of tag scope (useful for finding tag hierarchy
781 */
tm_tag_scope_depth(const TMTag * t)782 gint tm_tag_scope_depth(const TMTag *t)
783 {
784 const gchar *context_sep = tm_parser_context_separator(t->lang);
785 gint depth;
786 char *s;
787 if(!(t && t->scope))
788 return 0;
789 for (s = t->scope, depth = 0; s; s = strstr(s, context_sep))
790 {
791 ++ depth;
792 ++ s;
793 }
794 return depth;
795 }
796
797 #endif /* TM_DEBUG */
798