tagmanager: Fix handling of scopes starting with a non-ASCII character
[geany-mirror.git] / tagmanager / src / tm_tag.c
blob1773e23f22e5c2a87231052002adacb7ec4aec91
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 (0 != tag_entry->extensionFields.scope[1][0]))
248 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
249 if (tag_entry->extensionFields.inheritance != NULL)
250 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
251 if (tag_entry->extensionFields.varType != NULL)
252 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
253 if (tag_entry->extensionFields.access != NULL)
254 tag->access = get_tag_access(tag_entry->extensionFields.access);
255 if (tag_entry->extensionFields.implementation != NULL)
256 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
257 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
258 tag->type = tm_tag_macro_with_arg_t;
259 tag->file = file;
260 tag->lang = file->lang;
261 return TRUE;
265 Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
266 and returns a pointer to it.
267 @param file - Pointer to the TMSourceFile structure containing the tag
268 @param tag_entry Contains tag information generated by ctags
269 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
271 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
273 TMTag *tag;
275 TAG_NEW(tag);
276 if (FALSE == tm_tag_init(tag, file, tag_entry))
278 TAG_FREE(tag);
279 return NULL;
281 return tag;
285 Initializes an already malloc()ed TMTag structure by reading a tag entry
286 line from a file. The structure should be allocated beforehand.
287 @param tag The TMTag structure to populate
288 @param file The TMSourceFile struct (assigned to the file member)
289 @param fp FILE pointer from where the tag line is read
290 @return TRUE on success, FALSE on FAILURE
292 static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
294 guchar buf[BUFSIZ];
295 guchar *start, *end;
296 gboolean status;
297 guchar changed_char = TA_NAME;
299 tag->refcount = 1;
300 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
301 return FALSE;
302 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
304 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
305 ++ end;
306 if (('\0' == *end) || ('\n' == *end))
307 status = FALSE;
308 changed_char = *end;
309 *end = '\0';
310 if (NULL == tag->name)
312 if (!isprint(*start))
313 return FALSE;
314 else
315 tag->name = g_strdup((gchar*)start);
317 else
319 switch (*start)
321 case TA_LINE:
322 tag->line = atol((gchar*)start + 1);
323 break;
324 case TA_LOCAL:
325 tag->local = atoi((gchar*)start + 1);
326 break;
327 case TA_TYPE:
328 tag->type = (TMTagType) atoi((gchar*)start + 1);
329 break;
330 case TA_ARGLIST:
331 tag->arglist = g_strdup((gchar*)start + 1);
332 break;
333 case TA_SCOPE:
334 tag->scope = g_strdup((gchar*)start + 1);
335 break;
336 case TA_POINTER:
337 tag->pointerOrder = atoi((gchar*)start + 1);
338 break;
339 case TA_VARTYPE:
340 tag->var_type = g_strdup((gchar*)start + 1);
341 break;
342 case TA_INHERITS:
343 tag->inheritance = g_strdup((gchar*)start + 1);
344 break;
345 case TA_TIME: /* Obsolete */
346 break;
347 case TA_LANG: /* Obsolete */
348 break;
349 case TA_INACTIVE: /* Obsolete */
350 break;
351 case TA_ACCESS:
352 tag->access = (char) *(start + 1);
353 break;
354 case TA_IMPL:
355 tag->impl = (char) *(start + 1);
356 break;
357 default:
358 #ifdef GEANY_DEBUG
359 g_warning("Unknown attribute %s", start + 1);
360 #endif
361 break;
364 *end = changed_char;
366 if (NULL == tag->name)
367 return FALSE;
368 tag->file = file;
369 return TRUE;
372 /* alternative parser for Pascal and LaTeX global tags files with the following format
373 * tagname|return value|arglist|description\n */
374 static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
376 guchar buf[BUFSIZ];
377 guchar *start, *end;
378 gboolean status;
379 /*guchar changed_char = TA_NAME;*/
381 tag->refcount = 1;
382 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
383 return FALSE;
385 gchar **fields;
386 guint field_len;
387 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
389 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
390 ++ end;
391 if (('\0' == *end) || ('\n' == *end))
392 status = FALSE;
393 /*changed_char = *end;*/
394 *end = '\0';
395 if (NULL == tag->name && !isprint(*start))
396 return FALSE;
398 fields = g_strsplit((gchar*)start, "|", -1);
399 field_len = g_strv_length(fields);
401 if (field_len >= 1) tag->name = g_strdup(fields[0]);
402 else tag->name = NULL;
403 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
404 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
405 tag->type = tm_tag_prototype_t;
406 g_strfreev(fields);
410 if (NULL == tag->name)
411 return FALSE;
412 tag->file = file;
413 return TRUE;
417 Same as tm_tag_init_from_file(), but parsing CTags tag file format
418 (http://ctags.sourceforge.net/FORMAT)
420 static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
422 gchar buf[BUFSIZ];
423 gchar *p, *tab;
425 tag->refcount = 1;
426 tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
429 if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
430 return FALSE;
432 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
434 p = buf;
436 /* tag name */
437 if (! (tab = strchr(p, '\t')) || p == tab)
438 return FALSE;
439 tag->name = g_strndup(p, (gsize)(tab - p));
440 p = tab + 1;
442 /* tagfile, unused */
443 if (! (tab = strchr(p, '\t')))
445 g_free(tag->name);
446 tag->name = NULL;
447 return FALSE;
449 p = tab + 1;
450 /* Ex command, unused */
451 if (*p == '/' || *p == '?')
453 gchar c = *p;
454 for (++p; *p && *p != c; p++)
456 if (*p == '\\' && p[1])
457 p++;
460 else /* assume a line */
461 tag->line = atol(p);
462 tab = strstr(p, ";\"");
463 /* read extension fields */
464 if (tab)
466 p = tab + 2;
467 while (*p && *p != '\n' && *p != '\r')
469 gchar *end;
470 const gchar *key, *value = NULL;
472 /* skip leading tabulations */
473 while (*p && *p == '\t') p++;
474 /* find the separator (:) and end (\t) */
475 key = end = p;
476 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
478 if (*end == ':' && ! value)
480 *end = 0; /* terminate the key */
481 value = end + 1;
483 end++;
485 /* move p paste the so we won't stop parsing by setting *end=0 below */
486 p = *end ? end + 1 : end;
487 *end = 0; /* terminate the value (or key if no value) */
489 if (! value || 0 == strcmp(key, "kind")) /* tag kind */
491 const gchar *kind = value ? value : key;
493 if (kind[0] && kind[1])
494 tag->type = get_tag_type(kind);
495 else
497 switch (*kind)
499 case 'c': tag->type = tm_tag_class_t; break;
500 case 'd': tag->type = tm_tag_macro_t; break;
501 case 'e': tag->type = tm_tag_enumerator_t; break;
502 case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */
503 case 'f': tag->type = tm_tag_function_t; break;
504 case 'g': tag->type = tm_tag_enum_t; break;
505 case 'I': tag->type = tm_tag_class_t; break;
506 case 'i': tag->type = tm_tag_interface_t; break;
507 case 'l': tag->type = tm_tag_variable_t; break;
508 case 'M': tag->type = tm_tag_macro_t; break;
509 case 'm': tag->type = tm_tag_member_t; break;
510 case 'n': tag->type = tm_tag_namespace_t; break;
511 case 'P': tag->type = tm_tag_package_t; break;
512 case 'p': tag->type = tm_tag_prototype_t; break;
513 case 's': tag->type = tm_tag_struct_t; break;
514 case 't': tag->type = tm_tag_typedef_t; break;
515 case 'u': tag->type = tm_tag_union_t; break;
516 case 'v': tag->type = tm_tag_variable_t; break;
517 case 'x': tag->type = tm_tag_externvar_t; break;
518 default:
519 #ifdef TM_DEBUG
520 g_warning("Unknown tag kind %c", *kind);
521 #endif
522 tag->type = tm_tag_other_t; break;
526 else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
528 g_free(tag->inheritance);
529 tag->inheritance = g_strdup(value);
531 else if (0 == strcmp(key, "implementation")) /* implementation limit */
532 tag->impl = get_tag_impl(value);
533 else if (0 == strcmp(key, "line")) /* line */
534 tag->line = atol(value);
535 else if (0 == strcmp(key, "access")) /* access */
536 tag->access = get_tag_access(value);
537 else if (0 == strcmp(key, "class") ||
538 0 == strcmp(key, "enum") ||
539 0 == strcmp(key, "function") ||
540 0 == strcmp(key, "struct") ||
541 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
543 g_free(tag->scope);
544 tag->scope = g_strdup(value);
546 else if (0 == strcmp(key, "file")) /* static (local) tag */
547 tag->local = TRUE;
548 else if (0 == strcmp(key, "signature")) /* arglist */
550 g_free(tag->arglist);
551 tag->arglist = g_strdup(value);
556 tag->file = file;
557 return TRUE;
561 Same as tm_tag_new() except that the tag attributes are read from file.
562 @param mode langType to use for the tag.
564 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
566 TMTag *tag;
567 gboolean result = FALSE;
569 TAG_NEW(tag);
571 switch (format)
573 case TM_FILE_FORMAT_TAGMANAGER:
574 result = tm_tag_init_from_file(tag, file, fp);
575 break;
576 case TM_FILE_FORMAT_PIPE:
577 result = tm_tag_init_from_file_alt(tag, file, fp);
578 break;
579 case TM_FILE_FORMAT_CTAGS:
580 result = tm_tag_init_from_file_ctags(tag, file, fp);
581 break;
584 if (! result)
586 TAG_FREE(tag);
587 return NULL;
589 tag->lang = mode;
590 return tag;
594 Writes tag information to the given FILE *.
595 @param tag The tag information to write.
596 @param file FILE pointer to which the tag information is written.
597 @param attrs Attributes to be written (bitmask).
598 @return TRUE on success, FALSE on failure.
600 gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs)
602 fprintf(fp, "%s", tag->name);
603 if (attrs & tm_tag_attr_type_t)
604 fprintf(fp, "%c%d", TA_TYPE, tag->type);
605 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
606 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
607 if (attrs & tm_tag_attr_line_t)
608 fprintf(fp, "%c%ld", TA_LINE, tag->line);
609 if (attrs & tm_tag_attr_local_t)
610 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
611 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
612 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
613 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
614 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
615 if (attrs & tm_tag_attr_pointer_t)
616 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
617 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
618 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
619 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
620 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
621 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
622 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
624 if (fprintf(fp, "\n"))
625 return TRUE;
626 else
627 return FALSE;
631 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
632 @param tag The TMTag structure to destroy
633 @see tm_tag_free()
635 static void tm_tag_destroy(TMTag *tag)
637 g_free(tag->name);
638 g_free(tag->arglist);
639 g_free(tag->scope);
640 g_free(tag->inheritance);
641 g_free(tag->var_type);
646 Drops a reference from a TMTag. If the reference count reaches 0, this function
647 destroys all data in the tag and frees the tag structure as well.
648 @param tag Pointer to a TMTag structure
650 void tm_tag_unref(TMTag *tag)
652 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
653 * drop-in replacment of it */
654 if (NULL != tag && g_atomic_int_dec_and_test(&tag->refcount))
656 tm_tag_destroy(tag);
657 TAG_FREE(tag);
662 Adds a reference to a TMTag.
663 @param tag Pointer to a TMTag structure
664 @return the passed-in TMTag
666 TMTag *tm_tag_ref(TMTag *tag)
668 g_atomic_int_inc(&tag->refcount);
669 return tag;
673 Inbuilt tag comparison function.
675 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
677 unsigned int *sort_attr;
678 int returnval = 0;
679 TMTag *t1 = *((TMTag **) ptr1);
680 TMTag *t2 = *((TMTag **) ptr2);
681 TMSortOptions *sort_options = user_data;
683 if ((NULL == t1) || (NULL == t2))
685 g_warning("Found NULL tag");
686 return t2 - t1;
688 if (NULL == sort_options->sort_attrs)
690 if (sort_options->partial)
691 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
692 else
693 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
696 for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
698 switch (*sort_attr)
700 case tm_tag_attr_name_t:
701 if (sort_options->partial)
702 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
703 else
704 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
705 break;
706 case tm_tag_attr_file_t:
707 returnval = t1->file - t2->file;
708 break;
709 case tm_tag_attr_line_t:
710 returnval = t1->line - t2->line;
711 break;
712 case tm_tag_attr_type_t:
713 returnval = t1->type - t2->type;
714 break;
715 case tm_tag_attr_scope_t:
716 returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
717 break;
718 case tm_tag_attr_arglist_t:
719 returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
720 if (returnval != 0)
722 int line_diff = (t1->line - t2->line);
724 returnval = line_diff ? line_diff : returnval;
726 break;
727 case tm_tag_attr_vartype_t:
728 returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
729 break;
732 return returnval;
735 gboolean tm_tags_equal(const TMTag *a, const TMTag *b)
737 if (a == b)
738 return TRUE;
740 return (a->line == b->line &&
741 a->file == b->file /* ptr comparison */ &&
742 strcmp(FALLBACK(a->name, ""), FALLBACK(b->name, "")) == 0 &&
743 a->type == b->type &&
744 a->local == b->local &&
745 a->pointerOrder == b->pointerOrder &&
746 a->access == b->access &&
747 a->impl == b->impl &&
748 a->lang == b->lang &&
749 strcmp(FALLBACK(a->scope, ""), FALLBACK(b->scope, "")) == 0 &&
750 strcmp(FALLBACK(a->arglist, ""), FALLBACK(b->arglist, "")) == 0 &&
751 strcmp(FALLBACK(a->inheritance, ""), FALLBACK(b->inheritance, "")) == 0 &&
752 strcmp(FALLBACK(a->var_type, ""), FALLBACK(b->var_type, "")) == 0);
756 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
757 this function substitutes duplicate entries with NULL
758 @param tags_array Array of tags to dedup
759 @return TRUE on success, FALSE on failure
761 gboolean tm_tags_prune(GPtrArray *tags_array)
763 guint i, count;
764 for (i=0, count = 0; i < tags_array->len; ++i)
766 if (NULL != tags_array->pdata[i])
767 tags_array->pdata[count++] = tags_array->pdata[i];
769 tags_array->len = count;
770 return TRUE;
774 Deduplicates an array on tags using the inbuilt comparison function based on
775 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
776 @param tags_array Array of tags to dedup.
777 @param sort_attributes Attributes the array is sorted on. They will be deduped
778 on the same criteria.
779 @return TRUE on success, FALSE on failure
781 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
783 TMSortOptions sort_options;
784 guint i;
786 if ((!tags_array) || (!tags_array->len))
787 return TRUE;
788 sort_options.sort_attrs = sort_attributes;
789 sort_options.partial = FALSE;
790 for (i = 1; i < tags_array->len; ++i)
792 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
794 if (unref_duplicates)
795 tm_tag_unref(tags_array->pdata[i-1]);
796 tags_array->pdata[i-1] = NULL;
799 tm_tags_prune(tags_array);
800 return TRUE;
804 Sort an array of tags on the specified attribuites using the inbuilt comparison
805 function.
806 @param tags_array The array of tags to be sorted
807 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
808 @param dedup Whether to deduplicate the sorted array
809 @return TRUE on success, FALSE on failure
811 gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
812 gboolean dedup, gboolean unref_duplicates)
814 TMSortOptions sort_options;
816 if ((!tags_array) || (!tags_array->len))
817 return TRUE;
818 sort_options.sort_attrs = sort_attributes;
819 sort_options.partial = FALSE;
820 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
821 if (dedup)
822 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
823 return TRUE;
826 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
828 guint i;
830 /* Now we choose between an algorithm with complexity O(tags_array->len) and
831 * O(source_file->tags_array->len * log(tags_array->len)). The latter algorithm
832 * is better when tags_array contains many times more tags than
833 * source_file->tags_array so instead of trying to find the removed tags
834 * linearly, binary search is used. The constant 20 is more or less random
835 * but seems to work well. It's exact value isn't so critical because it's
836 * the extremes where the difference is the biggest: when
837 * source_file->tags_array->len == tags_array->len (single file open) and
838 * source_file->tags_array->len << tags_array->len (the number of tags
839 * from the file is a small fraction of all tags).
841 if (source_file->tags_array->len != 0 &&
842 tags_array->len / source_file->tags_array->len < 20)
844 for (i = 0; i < tags_array->len; i++)
846 TMTag *tag = tags_array->pdata[i];
848 if (tag->file == source_file)
849 tags_array->pdata[i] = NULL;
852 else
854 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
856 for (i = 0; i < source_file->tags_array->len; i++)
858 guint j;
859 guint tag_count;
860 TMTag **found;
861 TMTag *tag = source_file->tags_array->pdata[i];
863 found = tm_tags_find(tags_array, tag->name, FALSE, TRUE, &tag_count);
865 for (j = 0; j < tag_count; j++)
867 if (*found != NULL && (*found)->file == source_file)
869 /* we cannot set the pointer to NULL now because the search wouldn't work */
870 g_ptr_array_add(to_delete, found);
871 /* no break - if there are multiple tags of the same name, we would
872 * always find the first instance and wouldn't remove others; duplicates
873 * in the to_delete list aren't a problem */
875 found++;
879 for (i = 0; i < to_delete->len; i++)
881 TMTag **tag = to_delete->pdata[i];
882 *tag = NULL;
884 g_ptr_array_free(to_delete, TRUE);
887 tm_tags_prune(tags_array);
890 /* Optimized merge sort for merging sorted values from one array to another
891 * where one of the arrays is much smaller than the other.
892 * The merge complexity depends mostly on the size of the small array
893 * and is almost independent of the size of the big array.
894 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
895 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
896 TMSortOptions *sort_options, gboolean unref_duplicates) {
897 guint i1 = 0; /* index to big_array */
898 guint i2 = 0; /* index to small_array */
899 guint initial_step;
900 guint step;
901 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
902 #ifdef TM_DEBUG
903 guint cmpnum = 0;
904 #endif
906 /* swap the arrays if len(small) > len(big) */
907 if (small_array->len > big_array->len)
909 GPtrArray *tmp = small_array;
910 small_array = big_array;
911 big_array = tmp;
914 /* on average, we are merging a value from small_array every
915 * len(big_array) / len(small_array) values - good approximation for fast jump
916 * step size */
917 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
918 initial_step = initial_step > 4 ? initial_step : 1;
919 step = initial_step;
921 while (i1 < big_array->len && i2 < small_array->len)
923 gpointer val1;
924 gpointer val2 = small_array->pdata[i2];
926 if (step > 4) /* fast path start */
928 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
930 val1 = big_array->pdata[j1];
931 #ifdef TM_DEBUG
932 cmpnum++;
933 #endif
934 /* if the value in big_array after making the big step is still smaller
935 * than the value in small_array, we can copy all the values inbetween
936 * into the result without making expensive string comparisons */
937 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
939 while (i1 <= j1)
941 val1 = big_array->pdata[i1];
942 g_ptr_array_add(res_array, val1);
943 i1++;
946 else
948 /* lower the step and try again */
949 step /= 2;
951 } /* fast path end */
952 else
954 gint cmpval;
956 #ifdef TM_DEBUG
957 cmpnum++;
958 #endif
959 val1 = big_array->pdata[i1];
960 cmpval = tm_tag_compare(&val1, &val2, sort_options);
961 if (cmpval < 0)
963 g_ptr_array_add(res_array, val1);
964 i1++;
966 else
968 g_ptr_array_add(res_array, val2);
969 i2++;
970 /* value from small_array gets merged - reset the step size */
971 step = initial_step;
972 if (cmpval == 0)
974 i1++; /* remove the duplicate, keep just the newly merged value */
975 if (unref_duplicates)
976 tm_tag_unref(val1);
982 /* end of one of the arrays reached - copy the rest from the other array */
983 while (i1 < big_array->len)
984 g_ptr_array_add(res_array, big_array->pdata[i1++]);
985 while (i2 < small_array->len)
986 g_ptr_array_add(res_array, small_array->pdata[i2++]);
988 #ifdef TM_DEBUG
989 printf("cmpnums: %u\n", cmpnum);
990 printf("total tags: %u\n", big_array->len);
991 printf("merged tags: %u\n\n", small_array->len);
992 #endif
994 return res_array;
997 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
998 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
1000 GPtrArray *res_array;
1001 TMSortOptions sort_options;
1003 sort_options.sort_attrs = sort_attributes;
1004 sort_options.partial = FALSE;
1005 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
1006 return res_array;
1010 This function will extract the tags of the specified types from an array of tags.
1011 The returned value is a GPtrArray which should be free-d with a call to
1012 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
1013 are not duplicated.
1014 @param tags_array The original array of tags
1015 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
1016 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
1017 the original array.
1018 @return an array of tags (NULL on failure)
1020 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
1022 GPtrArray *new_tags;
1023 guint i;
1024 if (NULL == tags_array)
1025 return NULL;
1026 new_tags = g_ptr_array_new();
1027 for (i=0; i < tags_array->len; ++i)
1029 if (NULL != tags_array->pdata[i])
1031 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
1032 g_ptr_array_add(new_tags, tags_array->pdata[i]);
1035 return new_tags;
1039 Completely frees an array of tags.
1040 @param tags_array Array of tags to be freed.
1041 @param free_array Whether the GptrArray is to be freed as well.
1043 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
1045 if (tags_array)
1047 guint i;
1048 for (i = 0; i < tags_array->len; ++i)
1049 tm_tag_unref(tags_array->pdata[i]);
1050 if (free_all)
1051 g_ptr_array_free(tags_array, TRUE);
1052 else
1053 g_ptr_array_set_size(tags_array, 0);
1057 /* copy/pasted bsearch() from libc extended with user_data for comparison function
1058 * and using glib types */
1059 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
1060 GCompareDataFunc compar, gpointer user_data)
1062 gsize l, u, idx;
1063 gpointer p;
1064 gint comparison;
1066 l = 0;
1067 u = nmemb;
1068 while (l < u)
1070 idx = (l + u) / 2;
1071 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
1072 comparison = (*compar) (key, p, user_data);
1073 if (comparison < 0)
1074 u = idx;
1075 else if (comparison > 0)
1076 l = idx + 1;
1077 else
1078 return (gpointer) p;
1081 return NULL;
1084 static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag,
1085 gboolean tags_array_sorted, TMSortOptions *sort_options)
1087 if (tags_array_sorted)
1088 { /* fast binary search on sorted tags array */
1089 return (TMTag **) binary_search(&tag, tags_array->pdata, tags_array->len,
1090 tm_tag_compare, sort_options);
1092 else
1093 { /* the slow way: linear search (to make it a bit faster, search reverse assuming
1094 * that the tag to search was added recently) */
1095 guint i;
1096 TMTag **t;
1097 for (i = tags_array->len; i > 0; i--)
1099 t = (TMTag **) &tags_array->pdata[i - 1];
1100 if (0 == tm_tag_compare(&tag, t, sort_options))
1101 return t;
1104 return NULL;
1108 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
1109 The passed array of tags should be already sorted by name for optimal performance. If
1110 \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower.
1111 @param tags_array Tag array (may be sorted on name)
1112 @param name Name of the tag to locate.
1113 @param partial If TRUE, matches the first part of the name instead of doing exact match.
1114 @param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be
1115 searched with binary search. Otherwise it is searched linear which is obviously slower.
1116 @param tagCount Return location of the matched tags.
1118 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
1119 gboolean partial, gboolean tags_array_sorted, guint * tagCount)
1121 static TMTag *tag = NULL;
1122 TMTag **result;
1123 guint tagMatches=0;
1124 TMSortOptions sort_options;
1126 *tagCount = 0;
1127 if ((!tags_array) || (!tags_array->len))
1128 return NULL;
1130 if (NULL == tag)
1131 tag = g_new0(TMTag, 1);
1132 tag->name = (char *) name;
1133 sort_options.sort_attrs = NULL;
1134 sort_options.partial = partial;
1136 result = tags_search(tags_array, tag, tags_array_sorted, &sort_options);
1137 /* There can be matches on both sides of result */
1138 if (result)
1140 TMTag **last = (TMTag **) &tags_array->pdata[tags_array->len - 1];
1141 TMTag **adv;
1143 /* First look for any matches after result */
1144 adv = result;
1145 adv++;
1146 for (; adv <= last && *adv; ++ adv)
1148 if (0 != tm_tag_compare(&tag, adv, &sort_options))
1149 break;
1150 ++tagMatches;
1152 /* Now look for matches from result and below */
1153 for (; result >= (TMTag **) tags_array->pdata; -- result)
1155 if (0 != tm_tag_compare(&tag, (TMTag **) result, &sort_options))
1156 break;
1157 ++tagMatches;
1159 *tagCount=tagMatches;
1160 ++ result; /* Correct address for the last successful match */
1162 return (TMTag **) result;
1165 /* Returns TMTag which "own" given line
1166 @param line Current line in edited file.
1167 @param file_tags A GPtrArray of edited file TMTag pointers.
1168 @param tag_types the tag types to include in the match
1169 @return TMTag pointers to owner tag. */
1170 const TMTag *
1171 tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types)
1173 TMTag *matching_tag = NULL;
1174 if (file_tags && file_tags->len)
1176 guint i;
1177 gulong matching_line = 0;
1179 for (i = 0; (i < file_tags->len); ++i)
1181 TMTag *tag = TM_TAG (file_tags->pdata[i]);
1182 if (tag && tag->type & tag_types &&
1183 tag->line <= line && tag->line > matching_line)
1185 matching_tag = tag;
1186 matching_line = tag->line;
1190 return matching_tag;
1193 #if 0
1194 /* Returns TMTag to function or method which "own" given line
1195 @param line Current line in edited file.
1196 @param file_tags A GPtrArray of edited file TMTag pointers.
1197 @return TMTag pointers to owner function. */
1198 static const TMTag *
1199 tm_get_current_function (GPtrArray * file_tags, const gulong line)
1201 return tm_get_current_tag (file_tags, line, tm_tag_function_t | tm_tag_method_t);
1203 #endif
1206 #ifdef TM_DEBUG /* various debugging functions */
1209 Returns the type of tag as a string
1210 @param tag The tag whose type is required
1212 const char *tm_tag_type_name(const TMTag *tag)
1214 g_return_val_if_fail(tag, NULL);
1215 switch(tag->type)
1217 case tm_tag_class_t: return "class";
1218 case tm_tag_enum_t: return "enum";
1219 case tm_tag_enumerator_t: return "enumval";
1220 case tm_tag_field_t: return "field";
1221 case tm_tag_function_t: return "function";
1222 case tm_tag_interface_t: return "interface";
1223 case tm_tag_member_t: return "member";
1224 case tm_tag_method_t: return "method";
1225 case tm_tag_namespace_t: return "namespace";
1226 case tm_tag_package_t: return "package";
1227 case tm_tag_prototype_t: return "prototype";
1228 case tm_tag_struct_t: return "struct";
1229 case tm_tag_typedef_t: return "typedef";
1230 case tm_tag_union_t: return "union";
1231 case tm_tag_variable_t: return "variable";
1232 case tm_tag_externvar_t: return "extern";
1233 case tm_tag_macro_t: return "define";
1234 case tm_tag_macro_with_arg_t: return "macro";
1235 default: return NULL;
1237 return NULL;
1241 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
1242 @param tag_name Name of the tag type
1244 TMTagType tm_tag_name_type(const char* tag_name)
1246 g_return_val_if_fail(tag_name, tm_tag_undef_t);
1248 if (strcmp(tag_name, "class") == 0) return tm_tag_class_t;
1249 else if (strcmp(tag_name, "enum") == 0) return tm_tag_enum_t;
1250 else if (strcmp(tag_name, "enumval") == 0) return tm_tag_enumerator_t;
1251 else if (strcmp(tag_name, "field") == 0) return tm_tag_field_t;
1252 else if (strcmp(tag_name, "function") == 0) return tm_tag_function_t;
1253 else if (strcmp(tag_name, "interface") == 0) return tm_tag_interface_t;
1254 else if (strcmp(tag_name, "member") == 0) return tm_tag_member_t;
1255 else if (strcmp(tag_name, "method") == 0) return tm_tag_method_t;
1256 else if (strcmp(tag_name, "namespace") == 0) return tm_tag_namespace_t;
1257 else if (strcmp(tag_name, "package") == 0) return tm_tag_package_t;
1258 else if (strcmp(tag_name, "prototype") == 0) return tm_tag_prototype_t;
1259 else if (strcmp(tag_name, "struct") == 0) return tm_tag_struct_t;
1260 else if (strcmp(tag_name, "typedef") == 0) return tm_tag_typedef_t;
1261 else if (strcmp(tag_name, "union") == 0) return tm_tag_union_t;
1262 else if (strcmp(tag_name, "variable") == 0) return tm_tag_variable_t;
1263 else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t;
1264 else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t;
1265 else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t;
1266 else return tm_tag_undef_t;
1269 static const char *tm_tag_impl_name(TMTag *tag)
1271 g_return_val_if_fail(tag, NULL);
1272 if (TAG_IMPL_VIRTUAL == tag->impl)
1273 return "virtual";
1274 else
1275 return NULL;
1278 static const char *tm_tag_access_name(TMTag *tag)
1280 g_return_val_if_fail(tag, NULL);
1281 if (TAG_ACCESS_PUBLIC == tag->access)
1282 return "public";
1283 else if (TAG_ACCESS_PROTECTED == tag->access)
1284 return "protected";
1285 else if (TAG_ACCESS_PRIVATE == tag->access)
1286 return "private";
1287 else
1288 return NULL;
1292 Prints information about a tag to the given file pointer.
1293 @param tag The tag whose info is required.
1294 @param fp The file pointer of teh file to print the info to.
1296 void tm_tag_print(TMTag *tag, FILE *fp)
1298 const char *laccess, *impl, *type;
1299 if (!tag || !fp)
1300 return;
1301 laccess = tm_tag_access_name(tag);
1302 impl = tm_tag_impl_name(tag);
1303 type = tm_tag_type_name(tag);
1304 if (laccess)
1305 fprintf(fp, "%s ", laccess);
1306 if (impl)
1307 fprintf(fp, "%s ", impl);
1308 if (type)
1309 fprintf(fp, "%s ", type);
1310 if (tag->var_type)
1311 fprintf(fp, "%s ", tag->var_type);
1312 if (tag->scope)
1313 fprintf(fp, "%s::", tag->scope);
1314 fprintf(fp, "%s", tag->name);
1315 if (tag->arglist)
1316 fprintf(fp, "%s", tag->arglist);
1317 if (tag->inheritance)
1318 fprintf(fp, " : from %s", tag->inheritance);
1319 if ((tag->file) && (tag->line > 0))
1320 fprintf(fp, "[%s:%ld]", tag->file->file_name
1321 , tag->line);
1322 fprintf(fp, "\n");
1326 Prints info about all tags in the array to the given file pointer.
1328 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
1330 guint i;
1331 TMTag *tag;
1332 if (!(tags && (tags->len > 0) && fp))
1333 return;
1334 for (i = 0; i < tags->len; ++i)
1336 tag = TM_TAG(tags->pdata[i]);
1337 tm_tag_print(tag, fp);
1342 Returns the depth of tag scope (useful for finding tag hierarchy
1344 gint tm_tag_scope_depth(const TMTag *t)
1346 gint depth;
1347 char *s;
1348 if(!(t && t->scope))
1349 return 0;
1350 for (s = t->scope, depth = 0; s; s = strstr(s, "::"))
1352 ++ depth;
1353 ++ s;
1355 return depth;
1358 #endif /* TM_DEBUG */