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