Return an unisgned tag count in tm_tags_find()
[geany-mirror.git] / tagmanager / src / tm_tag.c
blob7228fa47b9d98ca80c56c7de333d5079f844ad31
1 /*
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.
8 */
10 #include <stdlib.h>
11 #include <string.h>
12 #include <glib-object.h>
14 #include "general.h"
15 #include "entry.h"
16 #include "parse.h"
17 #include "read.h"
18 #define LIBCTAGS_DEFINED
19 #include "tm_tag.h"
22 #define TAG_NEW(T) ((T) = g_slice_new0(TMTag))
23 #define TAG_FREE(T) g_slice_free(TMTag, (T))
26 #ifdef DEBUG_TAG_REFS
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)
41 gsize ref_count = 0;
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)
49 TMTag *tag;
51 if (! alive_tags)
53 alive_tags = g_hash_table_new(g_direct_hash, g_direct_equal);
54 atexit(log_refs_at_exit);
56 TAG_NEW(tag);
57 g_hash_table_insert(alive_tags, tag, tag);
59 return 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);
68 } else {
69 TAG_FREE(tag);
73 #undef TAG_NEW
74 #undef TAG_FREE
75 #define TAG_NEW(T) ((T) = log_tag_new())
76 #define TAG_FREE(T) log_tag_free(T)
78 #endif /* DEBUG_TAG_REFS */
81 const TMTagType TM_GLOBAL_TYPE_MASK =
82 tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
83 tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
86 /* Note: To preserve binary compatibility, it is very important
87 that you only *append* to this list ! */
88 enum
90 TA_NAME = 200,
91 TA_LINE,
92 TA_LOCAL,
93 TA_POS, /* Obsolete */
94 TA_TYPE,
95 TA_ARGLIST,
96 TA_SCOPE,
97 TA_VARTYPE,
98 TA_INHERITS,
99 TA_TIME,
100 TA_ACCESS,
101 TA_IMPL,
102 TA_LANG,
103 TA_INACTIVE, /* Obsolete */
104 TA_POINTER
107 typedef struct
109 guint *sort_attrs;
110 gboolean partial;
111 } TMSortOptions;
113 static const char *s_tag_type_names[] = {
114 "class", /* classes */
115 "enum", /* enumeration names */
116 "enumerator", /* enumerators (values inside an enumeration) */
117 "externvar", /* external variable declarations */
118 "field", /* fields */
119 "function", /* function definitions */
120 "interface", /* interfaces */
121 "macro", /* macro definitions */
122 "member", /* class, struct, and union members */
123 "method", /* methods */
124 "namespace", /* namespaces */
125 "package", /* packages */
126 "prototype", /* function prototypes */
127 "struct", /* structure names */
128 "typedef", /* typedefs */
129 "union", /* union names */
130 "variable", /* variable definitions */
131 "other" /* Other tag type (non C/C++/Java) */
134 static TMTagType s_tag_types[] = {
135 tm_tag_class_t,
136 tm_tag_enum_t,
137 tm_tag_enumerator_t,
138 tm_tag_externvar_t,
139 tm_tag_field_t,
140 tm_tag_function_t,
141 tm_tag_interface_t,
142 tm_tag_macro_t,
143 tm_tag_member_t,
144 tm_tag_method_t,
145 tm_tag_namespace_t,
146 tm_tag_package_t,
147 tm_tag_prototype_t,
148 tm_tag_struct_t,
149 tm_tag_typedef_t,
150 tm_tag_union_t,
151 tm_tag_variable_t,
152 tm_tag_other_t
155 /* Gets the GType for a TMTag */
156 GType tm_tag_get_type(void)
158 static GType gtype = 0;
159 if (G_UNLIKELY (gtype == 0))
161 gtype = g_boxed_type_register_static("TMTag", (GBoxedCopyFunc)tm_tag_ref,
162 (GBoxedFreeFunc)tm_tag_unref);
164 return gtype;
167 static TMTagType get_tag_type(const char *tag_name)
169 unsigned int i;
170 int cmp;
171 g_return_val_if_fail(tag_name, 0);
172 for (i=0; i < sizeof(s_tag_type_names)/sizeof(char *); ++i)
174 cmp = strcmp(tag_name, s_tag_type_names[i]);
175 if (0 == cmp)
176 return s_tag_types[i];
177 else if (cmp < 0)
178 break;
180 /* other is not checked above as it is last, not sorted alphabetically */
181 if (strcmp(tag_name, "other") == 0)
182 return tm_tag_other_t;
183 #ifdef TM_DEBUG
184 fprintf(stderr, "Unknown tag type %s\n", tag_name);
185 #endif
186 return tm_tag_undef_t;
189 static char get_tag_impl(const char *impl)
191 if ((0 == strcmp("virtual", impl))
192 || (0 == strcmp("pure virtual", impl)))
193 return TAG_IMPL_VIRTUAL;
195 #ifdef TM_DEBUG
196 g_warning("Unknown implementation %s", impl);
197 #endif
198 return TAG_IMPL_UNKNOWN;
201 static char get_tag_access(const char *access)
203 if (0 == strcmp("public", access))
204 return TAG_ACCESS_PUBLIC;
205 else if (0 == strcmp("protected", access))
206 return TAG_ACCESS_PROTECTED;
207 else if (0 == strcmp("private", access))
208 return TAG_ACCESS_PRIVATE;
209 else if (0 == strcmp("friend", access))
210 return TAG_ACCESS_FRIEND;
211 else if (0 == strcmp("default", access))
212 return TAG_ACCESS_DEFAULT;
214 #ifdef TM_DEBUG
215 g_warning("Unknown access type %s", access);
216 #endif
217 return TAG_ACCESS_UNKNOWN;
221 Initializes a TMTag structure with information from a tagEntryInfo struct
222 used by the ctags parsers. Note that the TMTag structure must be malloc()ed
223 before calling this function. This function is called by tm_tag_new() - you
224 should not need to call this directly.
225 @param tag The TMTag structure to initialize
226 @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
227 @param tag_entry Tag information gathered by the ctags parser
228 @return TRUE on success, FALSE on failure
230 static gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
232 tag->refcount = 1;
233 if (NULL == tag_entry)
234 return FALSE;
236 /* This is a normal tag entry */
237 if (NULL == tag_entry->name)
238 return FALSE;
239 tag->name = g_strdup(tag_entry->name);
240 tag->type = get_tag_type(tag_entry->kindName);
241 tag->local = tag_entry->isFileScope;
242 tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */
243 tag->line = tag_entry->lineNumber;
244 if (NULL != tag_entry->extensionFields.arglist)
245 tag->arglist = g_strdup(tag_entry->extensionFields.arglist);
246 if ((NULL != tag_entry->extensionFields.scope[1]) &&
247 (isalpha(tag_entry->extensionFields.scope[1][0]) ||
248 tag_entry->extensionFields.scope[1][0] == '_' ||
249 tag_entry->extensionFields.scope[1][0] == '$'))
250 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
251 if (tag_entry->extensionFields.inheritance != NULL)
252 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
253 if (tag_entry->extensionFields.varType != NULL)
254 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
255 if (tag_entry->extensionFields.access != NULL)
256 tag->access = get_tag_access(tag_entry->extensionFields.access);
257 if (tag_entry->extensionFields.implementation != NULL)
258 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
259 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
260 tag->type = tm_tag_macro_with_arg_t;
261 tag->file = file;
262 tag->lang = file->lang;
263 return TRUE;
267 Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
268 and returns a pointer to it.
269 @param file - Pointer to the TMSourceFile structure containing the tag
270 @param tag_entry Contains tag information generated by ctags
271 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
273 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
275 TMTag *tag;
277 TAG_NEW(tag);
278 if (FALSE == tm_tag_init(tag, file, tag_entry))
280 TAG_FREE(tag);
281 return NULL;
283 return tag;
287 Initializes an already malloc()ed TMTag structure by reading a tag entry
288 line from a file. The structure should be allocated beforehand.
289 @param tag The TMTag structure to populate
290 @param file The TMSourceFile struct (assigned to the file member)
291 @param fp FILE pointer from where the tag line is read
292 @return TRUE on success, FALSE on FAILURE
294 static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
296 guchar buf[BUFSIZ];
297 guchar *start, *end;
298 gboolean status;
299 guchar changed_char = TA_NAME;
301 tag->refcount = 1;
302 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
303 return FALSE;
304 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
306 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
307 ++ end;
308 if (('\0' == *end) || ('\n' == *end))
309 status = FALSE;
310 changed_char = *end;
311 *end = '\0';
312 if (NULL == tag->name)
314 if (!isprint(*start))
315 return FALSE;
316 else
317 tag->name = g_strdup((gchar*)start);
319 else
321 switch (*start)
323 case TA_LINE:
324 tag->line = atol((gchar*)start + 1);
325 break;
326 case TA_LOCAL:
327 tag->local = atoi((gchar*)start + 1);
328 break;
329 case TA_TYPE:
330 tag->type = (TMTagType) atoi((gchar*)start + 1);
331 break;
332 case TA_ARGLIST:
333 tag->arglist = g_strdup((gchar*)start + 1);
334 break;
335 case TA_SCOPE:
336 tag->scope = g_strdup((gchar*)start + 1);
337 break;
338 case TA_POINTER:
339 tag->pointerOrder = atoi((gchar*)start + 1);
340 break;
341 case TA_VARTYPE:
342 tag->var_type = g_strdup((gchar*)start + 1);
343 break;
344 case TA_INHERITS:
345 tag->inheritance = g_strdup((gchar*)start + 1);
346 break;
347 case TA_TIME: /* Obsolete */
348 break;
349 case TA_LANG: /* Obsolete */
350 break;
351 case TA_INACTIVE: /* Obsolete */
352 break;
353 case TA_ACCESS:
354 tag->access = *(start + 1);
355 break;
356 case TA_IMPL:
357 tag->impl = *(start + 1);
358 break;
359 default:
360 #ifdef GEANY_DEBUG
361 g_warning("Unknown attribute %s", start + 1);
362 #endif
363 break;
366 *end = changed_char;
368 if (NULL == tag->name)
369 return FALSE;
370 tag->file = file;
371 return TRUE;
374 /* alternative parser for Pascal and LaTeX global tags files with the following format
375 * tagname|return value|arglist|description\n */
376 static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
378 guchar buf[BUFSIZ];
379 guchar *start, *end;
380 gboolean status;
381 /*guchar changed_char = TA_NAME;*/
383 tag->refcount = 1;
384 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
385 return FALSE;
387 gchar **fields;
388 guint field_len;
389 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
391 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
392 ++ end;
393 if (('\0' == *end) || ('\n' == *end))
394 status = FALSE;
395 /*changed_char = *end;*/
396 *end = '\0';
397 if (NULL == tag->name && !isprint(*start))
398 return FALSE;
400 fields = g_strsplit((gchar*)start, "|", -1);
401 field_len = g_strv_length(fields);
403 if (field_len >= 1) tag->name = g_strdup(fields[0]);
404 else tag->name = NULL;
405 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
406 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
407 tag->type = tm_tag_prototype_t;
408 g_strfreev(fields);
412 if (NULL == tag->name)
413 return FALSE;
414 tag->file = file;
415 return TRUE;
419 Same as tm_tag_init_from_file(), but parsing CTags tag file format
420 (http://ctags.sourceforge.net/FORMAT)
422 static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
424 gchar buf[BUFSIZ];
425 gchar *p, *tab;
427 tag->refcount = 1;
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))
432 return FALSE;
434 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
436 p = buf;
438 /* tag name */
439 if (! (tab = strchr(p, '\t')) || p == tab)
440 return FALSE;
441 tag->name = g_strndup(p, (gsize)(tab - p));
442 p = tab + 1;
444 /* tagfile, unused */
445 if (! (tab = strchr(p, '\t')))
447 g_free(tag->name);
448 tag->name = NULL;
449 return FALSE;
451 p = tab + 1;
452 /* Ex command, unused */
453 if (*p == '/' || *p == '?')
455 gchar c = *p;
456 for (++p; *p && *p != c; p++)
458 if (*p == '\\' && p[1])
459 p++;
462 else /* assume a line */
463 tag->line = atol(p);
464 tab = strstr(p, ";\"");
465 /* read extension fields */
466 if (tab)
468 p = tab + 2;
469 while (*p && *p != '\n' && *p != '\r')
471 gchar *end;
472 const gchar *key, *value = NULL;
474 /* skip leading tabulations */
475 while (*p && *p == '\t') p++;
476 /* find the separator (:) and end (\t) */
477 key = end = p;
478 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
480 if (*end == ':' && ! value)
482 *end = 0; /* terminate the key */
483 value = end + 1;
485 end++;
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);
497 else
499 switch (*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_other_t; break; /* Obsolete */
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;
520 default:
521 #ifdef TM_DEBUG
522 g_warning("Unknown tag kind %c", *kind);
523 #endif
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->inheritance);
531 tag->inheritance = g_strdup(value);
533 else if (0 == strcmp(key, "implementation")) /* implementation limit */
534 tag->impl = get_tag_impl(value);
535 else if (0 == strcmp(key, "line")) /* line */
536 tag->line = atol(value);
537 else if (0 == strcmp(key, "access")) /* access */
538 tag->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->scope);
546 tag->scope = g_strdup(value);
548 else if (0 == strcmp(key, "file")) /* static (local) tag */
549 tag->local = TRUE;
550 else if (0 == strcmp(key, "signature")) /* arglist */
552 g_free(tag->arglist);
553 tag->arglist = g_strdup(value);
558 tag->file = file;
559 return TRUE;
563 Same as tm_tag_new() except that the tag attributes are read from file.
564 @param mode langType to use for the tag.
566 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
568 TMTag *tag;
569 gboolean result = FALSE;
571 TAG_NEW(tag);
573 switch (format)
575 case TM_FILE_FORMAT_TAGMANAGER:
576 result = tm_tag_init_from_file(tag, file, fp);
577 break;
578 case TM_FILE_FORMAT_PIPE:
579 result = tm_tag_init_from_file_alt(tag, file, fp);
580 break;
581 case TM_FILE_FORMAT_CTAGS:
582 result = tm_tag_init_from_file_ctags(tag, file, fp);
583 break;
586 if (! result)
588 TAG_FREE(tag);
589 return NULL;
591 tag->lang = mode;
592 return tag;
596 Writes tag information to the given FILE *.
597 @param tag The tag information to write.
598 @param file FILE pointer to which the tag information is written.
599 @param attrs Attributes to be written (bitmask).
600 @return TRUE on success, FALSE on failure.
602 gboolean tm_tag_write(TMTag *tag, FILE *fp, guint attrs)
604 fprintf(fp, "%s", tag->name);
605 if (attrs & tm_tag_attr_type_t)
606 fprintf(fp, "%c%d", TA_TYPE, tag->type);
607 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
608 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
609 if (attrs & tm_tag_attr_line_t)
610 fprintf(fp, "%c%ld", TA_LINE, tag->line);
611 if (attrs & tm_tag_attr_local_t)
612 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
613 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
614 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
615 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
616 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
617 if (attrs & tm_tag_attr_pointer_t)
618 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
619 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
620 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
621 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
622 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
623 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
624 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
626 if (fprintf(fp, "\n"))
627 return TRUE;
628 else
629 return FALSE;
633 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
634 @param tag The TMTag structure to destroy
635 @see tm_tag_free()
637 static void tm_tag_destroy(TMTag *tag)
639 g_free(tag->name);
640 g_free(tag->arglist);
641 g_free(tag->scope);
642 g_free(tag->inheritance);
643 g_free(tag->var_type);
648 Drops a reference from a TMTag. If the reference count reaches 0, this function
649 destroys all data in the tag and frees the tag structure as well.
650 @param tag Pointer to a TMTag structure
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))
658 tm_tag_destroy(tag);
659 TAG_FREE(tag);
664 Adds a reference to a TMTag.
665 @param tag Pointer to a TMTag structure
666 @return the passed-in TMTag
668 TMTag *tm_tag_ref(TMTag *tag)
670 g_atomic_int_inc(&tag->refcount);
671 return tag;
675 Inbuilt tag comparison function.
677 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
679 unsigned int *sort_attr;
680 int returnval = 0;
681 TMTag *t1 = *((TMTag **) ptr1);
682 TMTag *t2 = *((TMTag **) ptr2);
683 TMSortOptions *sort_options = user_data;
685 if ((NULL == t1) || (NULL == t2))
687 g_warning("Found NULL tag");
688 return t2 - t1;
690 if (NULL == sort_options->sort_attrs)
692 if (sort_options->partial)
693 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
694 else
695 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
698 for (sort_attr = sort_options->sort_attrs; *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
700 switch (*sort_attr)
702 case tm_tag_attr_name_t:
703 if (sort_options->partial)
704 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
705 else
706 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
707 if (0 != returnval)
708 return returnval;
709 break;
710 case tm_tag_attr_type_t:
711 if (0 != (returnval = (t1->type - t2->type)))
712 return returnval;
713 break;
714 case tm_tag_attr_file_t:
715 if (0 != (returnval = (t1->file - t2->file)))
716 return returnval;
717 break;
718 case tm_tag_attr_scope_t:
719 if (0 != (returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""))))
720 return returnval;
721 break;
722 case tm_tag_attr_arglist_t:
723 if (0 != (returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""))))
725 int line_diff = (t1->line - t2->line);
727 return line_diff ? line_diff : returnval;
729 break;
730 case tm_tag_attr_vartype_t:
731 if (0 != (returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""))))
732 return returnval;
733 break;
734 case tm_tag_attr_line_t:
735 if (0 != (returnval = (t1->line - t2->line)))
736 return returnval;
737 break;
740 return returnval;
744 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
745 this function substitutes duplicate entries with NULL
746 @param tags_array Array of tags to dedup
747 @return TRUE on success, FALSE on failure
749 gboolean tm_tags_prune(GPtrArray *tags_array)
751 guint i, count;
752 for (i=0, count = 0; i < tags_array->len; ++i)
754 if (NULL != tags_array->pdata[i])
755 tags_array->pdata[count++] = tags_array->pdata[i];
757 tags_array->len = count;
758 return TRUE;
762 Deduplicates an array on tags using the inbuilt comparison function based on
763 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
764 @param tags_array Array of tags to dedup.
765 @param sort_attributes Attributes the array is sorted on. They will be deduped
766 on the same criteria.
767 @return TRUE on success, FALSE on failure
769 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
771 TMSortOptions sort_options;
772 guint i;
774 if ((!tags_array) || (!tags_array->len))
775 return TRUE;
776 sort_options.sort_attrs = sort_attributes;
777 sort_options.partial = FALSE;
778 for (i = 1; i < tags_array->len; ++i)
780 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
782 if (unref_duplicates)
783 tm_tag_unref(tags_array->pdata[i-1]);
784 tags_array->pdata[i-1] = NULL;
787 tm_tags_prune(tags_array);
788 return TRUE;
792 Sort an array of tags on the specified attribuites using the inbuilt comparison
793 function.
794 @param tags_array The array of tags to be sorted
795 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
796 @param dedup Whether to deduplicate the sorted array
797 @return TRUE on success, FALSE on failure
799 gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
800 gboolean dedup, gboolean unref_duplicates)
802 TMSortOptions sort_options;
804 if ((!tags_array) || (!tags_array->len))
805 return TRUE;
806 sort_options.sort_attrs = sort_attributes;
807 sort_options.partial = FALSE;
808 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
809 if (dedup)
810 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
811 return TRUE;
814 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
816 guint i;
817 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
819 for (i = 0; i < source_file->tags_array->len; i++)
821 guint j;
822 guint tag_count;
823 TMTag **found;
824 TMTag *tag = source_file->tags_array->pdata[i];
826 found = tm_tags_find(tags_array, tag->name, FALSE, TRUE, &tag_count);
828 for (j = 0; j < tag_count; j++)
830 if (*found != NULL && (*found)->file == source_file)
832 /* we cannot set the pointer to NULL now because the search wouldn't work */
833 g_ptr_array_add(to_delete, found);
834 /* no break - if there are multiple tags of the same name, we would
835 * always find the first instance and wouldn't remove others; duplicates
836 * in the to_delete list aren't a problem */
838 found++;
842 for (i = 0; i < to_delete->len; i++)
844 TMTag **tag = to_delete->pdata[i];
845 *tag = NULL;
847 g_ptr_array_free(to_delete, TRUE);
849 tm_tags_prune(tags_array);
852 /* Optimized merge sort for merging sorted values from one array to another
853 * where one of the arrays is much smaller than the other.
854 * The merge complexity depends mostly on the size of the small array
855 * and is almost independent of the size of the big array.
856 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
857 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
858 TMSortOptions *sort_options, gboolean unref_duplicates) {
859 guint i1 = 0; /* index to big_array */
860 guint i2 = 0; /* index to small_array */
861 guint initial_step;
862 guint step;
863 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
864 #ifdef TM_DEBUG
865 guint cmpnum = 0;
866 #endif
868 /* swap the arrays if len(small) > len(big) */
869 if (small_array->len > big_array->len)
871 GPtrArray *tmp = small_array;
872 small_array = big_array;
873 big_array = tmp;
876 /* on average, we are merging a value from small_array every
877 * len(big_array) / len(small_array) values - good approximation for fast jump
878 * step size */
879 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
880 initial_step = initial_step > 4 ? initial_step : 1;
881 step = initial_step;
883 while (i1 < big_array->len && i2 < small_array->len)
885 gpointer val1;
886 gpointer val2 = small_array->pdata[i2];
888 if (step > 4) /* fast path start */
890 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
892 val1 = big_array->pdata[j1];
893 #ifdef TM_DEBUG
894 cmpnum++;
895 #endif
896 /* if the value in big_array after making the big step is still smaller
897 * than the value in small_array, we can copy all the values inbetween
898 * into the result without making expensive string comparisons */
899 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
901 while (i1 <= j1)
903 val1 = big_array->pdata[i1];
904 /* we allocated enough space so we are sure we don't need to reallocate
905 * the array - copy and increment the size directly so it can be inlined */
906 res_array->pdata[res_array->len++] = val1;
907 i1++;
910 else
912 /* lower the step and try again */
913 step /= 2;
915 } /* fast path end */
916 else
918 gint cmpval;
920 #ifdef TM_DEBUG
921 cmpnum++;
922 #endif
923 val1 = big_array->pdata[i1];
924 cmpval = tm_tag_compare(&val1, &val2, sort_options);
925 if (cmpval < 0)
927 g_ptr_array_add(res_array, val1);
928 i1++;
930 else
932 g_ptr_array_add(res_array, val2);
933 i2++;
934 /* value from small_array gets merged - reset the step size */
935 step = initial_step;
936 if (cmpval == 0)
938 i1++; /* remove the duplicate, keep just the newly merged value */
939 if (unref_duplicates)
940 tm_tag_unref(val1);
946 /* end of one of the arrays reached - copy the rest from the other array */
947 while (i1 < big_array->len)
948 g_ptr_array_add(res_array, big_array->pdata[i1++]);
949 while (i2 < small_array->len)
950 g_ptr_array_add(res_array, small_array->pdata[i2++]);
952 #ifdef TM_DEBUG
953 printf("cmpnums: %u\n", cmpnum);
954 printf("total tags: %u\n", big_array->len);
955 printf("merged tags: %u\n\n", small_array->len);
956 #endif
958 return res_array;
961 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
962 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
964 GPtrArray *res_array;
965 TMSortOptions sort_options;
967 sort_options.sort_attrs = sort_attributes;
968 sort_options.partial = FALSE;
969 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
970 return res_array;
974 This function will extract the tags of the specified types from an array of tags.
975 The returned value is a GPtrArray which should be free-d with a call to
976 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
977 are not duplicated.
978 @param tags_array The original array of tags
979 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
980 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
981 the original array.
982 @return an array of tags (NULL on failure)
984 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
986 GPtrArray *new_tags;
987 guint i;
988 if (NULL == tags_array)
989 return NULL;
990 new_tags = g_ptr_array_new();
991 for (i=0; i < tags_array->len; ++i)
993 if (NULL != tags_array->pdata[i])
995 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
996 g_ptr_array_add(new_tags, tags_array->pdata[i]);
999 return new_tags;
1003 Completely frees an array of tags.
1004 @param tags_array Array of tags to be freed.
1005 @param free_array Whether the GptrArray is to be freed as well.
1007 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
1009 if (tags_array)
1011 guint i;
1012 for (i = 0; i < tags_array->len; ++i)
1013 tm_tag_unref(tags_array->pdata[i]);
1014 if (free_all)
1015 g_ptr_array_free(tags_array, TRUE);
1016 else
1017 g_ptr_array_set_size(tags_array, 0);
1021 /* copy/pasted bsearch() from libc extended with user_data for comparison function
1022 * and using glib types */
1023 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
1024 GCompareDataFunc compar, gpointer user_data)
1026 gsize l, u, idx;
1027 gpointer p;
1028 gint comparison;
1030 l = 0;
1031 u = nmemb;
1032 while (l < u)
1034 idx = (l + u) / 2;
1035 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
1036 comparison = (*compar) (key, p, user_data);
1037 if (comparison < 0)
1038 u = idx;
1039 else if (comparison > 0)
1040 l = idx + 1;
1041 else
1042 return (gpointer) p;
1045 return NULL;
1048 static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag, gboolean partial,
1049 gboolean tags_array_sorted, TMSortOptions *sort_options)
1051 if (tags_array_sorted)
1052 { /* fast binary search on sorted tags array */
1053 return (TMTag **) binary_search(&tag, tags_array->pdata, tags_array->len,
1054 tm_tag_compare, sort_options);
1056 else
1057 { /* the slow way: linear search (to make it a bit faster, search reverse assuming
1058 * that the tag to search was added recently) */
1059 int i;
1060 TMTag **t;
1061 for (i = tags_array->len - 1; i >= 0; i--)
1063 t = (TMTag **) &tags_array->pdata[i];
1064 if (0 == tm_tag_compare(&tag, t, sort_options))
1065 return t;
1068 return NULL;
1072 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
1073 The passed array of tags should be already sorted by name for optimal performance. If
1074 \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower.
1075 @param tags_array Tag array (may be sorted on name)
1076 @param name Name of the tag to locate.
1077 @param partial If TRUE, matches the first part of the name instead of doing exact match.
1078 @param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be
1079 searched with binary search. Otherwise it is searched linear which is obviously slower.
1080 @param tagCount Return location of the matched tags.
1082 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
1083 gboolean partial, gboolean tags_array_sorted, guint * tagCount)
1085 static TMTag *tag = NULL;
1086 TMTag **result;
1087 guint tagMatches=0;
1088 TMSortOptions sort_options;
1090 *tagCount = 0;
1091 if ((!tags_array) || (!tags_array->len))
1092 return NULL;
1094 if (NULL == tag)
1095 tag = g_new0(TMTag, 1);
1096 tag->name = (char *) name;
1097 sort_options.sort_attrs = NULL;
1098 sort_options.partial = partial;
1100 result = tags_search(tags_array, tag, partial, tags_array_sorted, &sort_options);
1101 /* There can be matches on both sides of result */
1102 if (result)
1104 TMTag **last = (TMTag **) &tags_array->pdata[tags_array->len - 1];
1105 TMTag **adv;
1107 /* First look for any matches after result */
1108 adv = result;
1109 adv++;
1110 for (; adv <= last && *adv; ++ adv)
1112 if (0 != tm_tag_compare(&tag, adv, &sort_options))
1113 break;
1114 ++tagMatches;
1116 /* Now look for matches from result and below */
1117 for (; result >= (TMTag **) tags_array->pdata; -- result)
1119 if (0 != tm_tag_compare(&tag, (TMTag **) result, &sort_options))
1120 break;
1121 ++tagMatches;
1123 *tagCount=tagMatches;
1124 ++ result; /* Correct address for the last successful match */
1126 return (TMTag **) result;
1129 #ifdef TM_DEBUG /* various debugging functions */
1132 Returns the type of tag as a string
1133 @param tag The tag whose type is required
1135 const char *tm_tag_type_name(const TMTag *tag)
1137 g_return_val_if_fail(tag, NULL);
1138 switch(tag->type)
1140 case tm_tag_class_t: return "class";
1141 case tm_tag_enum_t: return "enum";
1142 case tm_tag_enumerator_t: return "enumval";
1143 case tm_tag_field_t: return "field";
1144 case tm_tag_function_t: return "function";
1145 case tm_tag_interface_t: return "interface";
1146 case tm_tag_member_t: return "member";
1147 case tm_tag_method_t: return "method";
1148 case tm_tag_namespace_t: return "namespace";
1149 case tm_tag_package_t: return "package";
1150 case tm_tag_prototype_t: return "prototype";
1151 case tm_tag_struct_t: return "struct";
1152 case tm_tag_typedef_t: return "typedef";
1153 case tm_tag_union_t: return "union";
1154 case tm_tag_variable_t: return "variable";
1155 case tm_tag_externvar_t: return "extern";
1156 case tm_tag_macro_t: return "define";
1157 case tm_tag_macro_with_arg_t: return "macro";
1158 default: return NULL;
1160 return NULL;
1164 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
1165 @param tag_name Name of the tag type
1167 TMTagType tm_tag_name_type(const char* tag_name)
1169 g_return_val_if_fail(tag_name, tm_tag_undef_t);
1171 if (strcmp(tag_name, "class") == 0) return tm_tag_class_t;
1172 else if (strcmp(tag_name, "enum") == 0) return tm_tag_enum_t;
1173 else if (strcmp(tag_name, "enumval") == 0) return tm_tag_enumerator_t;
1174 else if (strcmp(tag_name, "field") == 0) return tm_tag_field_t;
1175 else if (strcmp(tag_name, "function") == 0) return tm_tag_function_t;
1176 else if (strcmp(tag_name, "interface") == 0) return tm_tag_interface_t;
1177 else if (strcmp(tag_name, "member") == 0) return tm_tag_member_t;
1178 else if (strcmp(tag_name, "method") == 0) return tm_tag_method_t;
1179 else if (strcmp(tag_name, "namespace") == 0) return tm_tag_namespace_t;
1180 else if (strcmp(tag_name, "package") == 0) return tm_tag_package_t;
1181 else if (strcmp(tag_name, "prototype") == 0) return tm_tag_prototype_t;
1182 else if (strcmp(tag_name, "struct") == 0) return tm_tag_struct_t;
1183 else if (strcmp(tag_name, "typedef") == 0) return tm_tag_typedef_t;
1184 else if (strcmp(tag_name, "union") == 0) return tm_tag_union_t;
1185 else if (strcmp(tag_name, "variable") == 0) return tm_tag_variable_t;
1186 else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t;
1187 else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t;
1188 else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t;
1189 else return tm_tag_undef_t;
1192 static const char *tm_tag_impl_name(TMTag *tag)
1194 g_return_val_if_fail(tag, NULL);
1195 if (TAG_IMPL_VIRTUAL == tag->impl)
1196 return "virtual";
1197 else
1198 return NULL;
1201 static const char *tm_tag_access_name(TMTag *tag)
1203 g_return_val_if_fail(tag, NULL);
1204 if (TAG_ACCESS_PUBLIC == tag->access)
1205 return "public";
1206 else if (TAG_ACCESS_PROTECTED == tag->access)
1207 return "protected";
1208 else if (TAG_ACCESS_PRIVATE == tag->access)
1209 return "private";
1210 else
1211 return NULL;
1215 Prints information about a tag to the given file pointer.
1216 @param tag The tag whose info is required.
1217 @param fp The file pointer of teh file to print the info to.
1219 void tm_tag_print(TMTag *tag, FILE *fp)
1221 const char *laccess, *impl, *type;
1222 if (!tag || !fp)
1223 return;
1224 laccess = tm_tag_access_name(tag);
1225 impl = tm_tag_impl_name(tag);
1226 type = tm_tag_type_name(tag);
1227 if (laccess)
1228 fprintf(fp, "%s ", laccess);
1229 if (impl)
1230 fprintf(fp, "%s ", impl);
1231 if (type)
1232 fprintf(fp, "%s ", type);
1233 if (tag->var_type)
1234 fprintf(fp, "%s ", tag->var_type);
1235 if (tag->scope)
1236 fprintf(fp, "%s::", tag->scope);
1237 fprintf(fp, "%s", tag->name);
1238 if (tag->arglist)
1239 fprintf(fp, "%s", tag->arglist);
1240 if (tag->inheritance)
1241 fprintf(fp, " : from %s", tag->inheritance);
1242 if ((tag->file) && (tag->line > 0))
1243 fprintf(fp, "[%s:%ld]", tag->file->file_name
1244 , tag->line);
1245 fprintf(fp, "\n");
1249 Prints info about all tags in the array to the given file pointer.
1251 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
1253 guint i;
1254 TMTag *tag;
1255 if (!(tags && (tags->len > 0) && fp))
1256 return;
1257 for (i = 0; i < tags->len; ++i)
1259 tag = TM_TAG(tags->pdata[i]);
1260 tm_tag_print(tag, fp);
1265 Returns the depth of tag scope (useful for finding tag hierarchy
1267 gint tm_tag_scope_depth(const TMTag *t)
1269 gint depth;
1270 char *s;
1271 if(!(t && t->scope))
1272 return 0;
1273 for (s = t->scope, depth = 0; s; s = strstr(s, "::"))
1275 ++ depth;
1276 ++ s;
1278 return depth;
1281 #endif /* TM_DEBUG */