Make code more readable by renaming poorly named macros NZV and NVL
[geany-mirror.git] / tagmanager / src / tm_tag.c
blobb2ba00f700ccb9cbf8972ea1e60d77f0450fed11
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 /* Note: To preserve binary compatibility, it is very important
82 that you only *append* to this list ! */
83 enum
85 TA_NAME = 200,
86 TA_LINE,
87 TA_LOCAL,
88 TA_POS, /* Obsolete */
89 TA_TYPE,
90 TA_ARGLIST,
91 TA_SCOPE,
92 TA_VARTYPE,
93 TA_INHERITS,
94 TA_TIME,
95 TA_ACCESS,
96 TA_IMPL,
97 TA_LANG,
98 TA_INACTIVE,
99 TA_POINTER
102 static guint *s_sort_attrs = NULL;
103 static gboolean s_partial = FALSE;
105 static const char *s_tag_type_names[] = {
106 "class", /* classes */
107 "enum", /* enumeration names */
108 "enumerator", /* enumerators (values inside an enumeration) */
109 "externvar", /* external variable declarations */
110 "field", /* fields */
111 "function", /* function definitions */
112 "interface", /* interfaces */
113 "macro", /* macro definitions */
114 "member", /* class, struct, and union members */
115 "method", /* methods */
116 "namespace", /* namespaces */
117 "package", /* packages */
118 "prototype", /* function prototypes */
119 "struct", /* structure names */
120 "typedef", /* typedefs */
121 "union", /* union names */
122 "variable", /* variable definitions */
123 "other" /* Other tag type (non C/C++/Java) */
126 static int s_tag_types[] = {
127 tm_tag_class_t,
128 tm_tag_enum_t,
129 tm_tag_enumerator_t,
130 tm_tag_externvar_t,
131 tm_tag_field_t,
132 tm_tag_function_t,
133 tm_tag_interface_t,
134 tm_tag_macro_t,
135 tm_tag_member_t,
136 tm_tag_method_t,
137 tm_tag_namespace_t,
138 tm_tag_package_t,
139 tm_tag_prototype_t,
140 tm_tag_struct_t,
141 tm_tag_typedef_t,
142 tm_tag_union_t,
143 tm_tag_variable_t,
144 tm_tag_other_t
147 GType tm_tag_get_type(void)
149 static GType gtype = 0;
150 if (G_UNLIKELY (gtype == 0))
152 gtype = g_boxed_type_register_static("TMTag", (GBoxedCopyFunc)tm_tag_ref,
153 (GBoxedFreeFunc)tm_tag_unref);
155 return gtype;
158 static int get_tag_type(const char *tag_name)
160 unsigned int i;
161 int cmp;
162 g_return_val_if_fail(tag_name, 0);
163 for (i=0; i < sizeof(s_tag_type_names)/sizeof(char *); ++i)
165 cmp = strcmp(tag_name, s_tag_type_names[i]);
166 if (0 == cmp)
167 return s_tag_types[i];
168 else if (cmp < 0)
169 break;
171 /* other is not checked above as it is last, not sorted alphabetically */
172 if (strcmp(tag_name, "other") == 0)
173 return tm_tag_other_t;
174 #ifdef TM_DEBUG
175 fprintf(stderr, "Unknown tag type %s\n", tag_name);
176 #endif
177 return tm_tag_undef_t;
180 static char get_tag_impl(const char *impl)
182 if ((0 == strcmp("virtual", impl))
183 || (0 == strcmp("pure virtual", impl)))
184 return TAG_IMPL_VIRTUAL;
186 #ifdef TM_DEBUG
187 g_warning("Unknown implementation %s", impl);
188 #endif
189 return TAG_IMPL_UNKNOWN;
192 static char get_tag_access(const char *access)
194 if (0 == strcmp("public", access))
195 return TAG_ACCESS_PUBLIC;
196 else if (0 == strcmp("protected", access))
197 return TAG_ACCESS_PROTECTED;
198 else if (0 == strcmp("private", access))
199 return TAG_ACCESS_PRIVATE;
200 else if (0 == strcmp("friend", access))
201 return TAG_ACCESS_FRIEND;
202 else if (0 == strcmp("default", access))
203 return TAG_ACCESS_DEFAULT;
205 #ifdef TM_DEBUG
206 g_warning("Unknown access type %s", access);
207 #endif
208 return TAG_ACCESS_UNKNOWN;
211 gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
213 tag->refcount = 1;
214 if (NULL == tag_entry)
216 /* This is a file tag */
217 if (NULL == file)
218 return FALSE;
219 else
221 tag->name = g_strdup(file->work_object.file_name);
222 tag->type = tm_tag_file_t;
223 /* tag->atts.file.timestamp = file->work_object.analyze_time; */
224 tag->atts.file.lang = file->lang;
225 tag->atts.file.inactive = FALSE;
226 return TRUE;
229 else
231 /* This is a normal tag entry */
232 if (NULL == tag_entry->name)
233 return FALSE;
234 tag->name = g_strdup(tag_entry->name);
235 tag->type = get_tag_type(tag_entry->kindName);
236 tag->atts.entry.local = tag_entry->isFileScope;
237 tag->atts.entry.pointerOrder = 0; /* backward compatibility (use var_type instead) */
238 tag->atts.entry.line = tag_entry->lineNumber;
239 if (NULL != tag_entry->extensionFields.arglist)
240 tag->atts.entry.arglist = g_strdup(tag_entry->extensionFields.arglist);
241 if ((NULL != tag_entry->extensionFields.scope[1]) &&
242 (isalpha(tag_entry->extensionFields.scope[1][0]) ||
243 tag_entry->extensionFields.scope[1][0] == '_' ||
244 tag_entry->extensionFields.scope[1][0] == '$'))
245 tag->atts.entry.scope = g_strdup(tag_entry->extensionFields.scope[1]);
246 if (tag_entry->extensionFields.inheritance != NULL)
247 tag->atts.entry.inheritance = g_strdup(tag_entry->extensionFields.inheritance);
248 if (tag_entry->extensionFields.varType != NULL)
249 tag->atts.entry.var_type = g_strdup(tag_entry->extensionFields.varType);
250 if (tag_entry->extensionFields.access != NULL)
251 tag->atts.entry.access = get_tag_access(tag_entry->extensionFields.access);
252 if (tag_entry->extensionFields.implementation != NULL)
253 tag->atts.entry.impl = get_tag_impl(tag_entry->extensionFields.implementation);
254 if ((tm_tag_macro_t == tag->type) && (NULL != tag->atts.entry.arglist))
255 tag->type = tm_tag_macro_with_arg_t;
256 tag->atts.entry.file = file;
257 return TRUE;
261 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
263 TMTag *tag;
265 TAG_NEW(tag);
266 if (FALSE == tm_tag_init(tag, file, tag_entry))
268 TAG_FREE(tag);
269 return NULL;
271 return tag;
274 gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
276 guchar buf[BUFSIZ];
277 guchar *start, *end;
278 gboolean status;
279 guchar changed_char = TA_NAME;
281 tag->refcount = 1;
282 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
283 return FALSE;
284 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
286 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
287 ++ end;
288 if (('\0' == *end) || ('\n' == *end))
289 status = FALSE;
290 changed_char = *end;
291 *end = '\0';
292 if (NULL == tag->name)
294 if (!isprint(*start))
295 return FALSE;
296 else
297 tag->name = g_strdup((gchar*)start);
299 else
301 switch (*start)
303 case TA_LINE:
304 tag->atts.entry.line = atol((gchar*)start + 1);
305 break;
306 case TA_LOCAL:
307 tag->atts.entry.local = atoi((gchar*)start + 1);
308 break;
309 case TA_TYPE:
310 tag->type = (TMTagType) atoi((gchar*)start + 1);
311 break;
312 case TA_ARGLIST:
313 tag->atts.entry.arglist = g_strdup((gchar*)start + 1);
314 break;
315 case TA_SCOPE:
316 tag->atts.entry.scope = g_strdup((gchar*)start + 1);
317 break;
318 case TA_POINTER:
319 tag->atts.entry.pointerOrder = atoi((gchar*)start + 1);
320 break;
321 case TA_VARTYPE:
322 tag->atts.entry.var_type = g_strdup((gchar*)start + 1);
323 break;
324 case TA_INHERITS:
325 tag->atts.entry.inheritance = g_strdup((gchar*)start + 1);
326 break;
327 case TA_TIME:
328 if (tm_tag_file_t != tag->type)
330 g_warning("Got time attribute for non-file tag %s", tag->name);
331 return FALSE;
333 else
334 tag->atts.file.timestamp = atol((gchar*)start + 1);
335 break;
336 case TA_LANG:
337 if (tm_tag_file_t != tag->type)
339 g_warning("Got lang attribute for non-file tag %s", tag->name);
340 return FALSE;
342 else
343 tag->atts.file.lang = atoi((gchar*)start + 1);
344 break;
345 case TA_INACTIVE:
346 if (tm_tag_file_t != tag->type)
348 g_warning("Got inactive attribute for non-file tag %s", tag->name);
349 return FALSE;
351 else
352 tag->atts.file.inactive = (gboolean) atoi((gchar*)start + 1);
353 break;
354 case TA_ACCESS:
355 tag->atts.entry.access = *(start + 1);
356 break;
357 case TA_IMPL:
358 tag->atts.entry.impl = *(start + 1);
359 break;
360 default:
361 #ifdef GEANY_DEBUG
362 g_warning("Unknown attribute %s", start + 1);
363 #endif
364 break;
367 *end = changed_char;
369 if (NULL == tag->name)
370 return FALSE;
371 if (tm_tag_file_t != tag->type)
372 tag->atts.entry.file = file;
373 return TRUE;
376 /* alternative parser for Pascal and LaTeX global tags files with the following format
377 * tagname|return value|arglist|description\n */
378 gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
380 guchar buf[BUFSIZ];
381 guchar *start, *end;
382 gboolean status;
383 /*guchar changed_char = TA_NAME;*/
385 tag->refcount = 1;
386 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
387 return FALSE;
389 gchar **fields;
390 guint field_len;
391 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
393 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
394 ++ end;
395 if (('\0' == *end) || ('\n' == *end))
396 status = FALSE;
397 /*changed_char = *end;*/
398 *end = '\0';
399 if (NULL == tag->name && !isprint(*start))
400 return FALSE;
402 fields = g_strsplit((gchar*)start, "|", -1);
403 field_len = g_strv_length(fields);
405 if (field_len >= 1) tag->name = g_strdup(fields[0]);
406 else tag->name = NULL;
407 if (field_len >= 2 && fields[1] != NULL) tag->atts.entry.var_type = g_strdup(fields[1]);
408 if (field_len >= 3 && fields[2] != NULL) tag->atts.entry.arglist = g_strdup(fields[2]);
409 tag->type = tm_tag_prototype_t;
410 g_strfreev(fields);
414 if (NULL == tag->name)
415 return FALSE;
416 if (tm_tag_file_t != tag->type)
417 tag->atts.entry.file = file;
418 return TRUE;
421 /* Reads ctags format (http://ctags.sourceforge.net/FORMAT) */
422 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->atts.entry.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_file_t; break;
505 case 'f': tag->type = tm_tag_function_t; break;
506 case 'g': tag->type = tm_tag_enum_t; break;
507 case 'I': tag->type = tm_tag_class_t; break;
508 case 'i': tag->type = tm_tag_interface_t; break;
509 case 'l': tag->type = tm_tag_variable_t; break;
510 case 'M': tag->type = tm_tag_macro_t; break;
511 case 'm': tag->type = tm_tag_member_t; break;
512 case 'n': tag->type = tm_tag_namespace_t; break;
513 case 'P': tag->type = tm_tag_package_t; break;
514 case 'p': tag->type = tm_tag_prototype_t; break;
515 case 's': tag->type = tm_tag_struct_t; break;
516 case 't': tag->type = tm_tag_typedef_t; break;
517 case 'u': tag->type = tm_tag_union_t; break;
518 case 'v': tag->type = tm_tag_variable_t; break;
519 case 'x': tag->type = tm_tag_externvar_t; break;
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->atts.entry.inheritance);
531 tag->atts.entry.inheritance = g_strdup(value);
533 else if (0 == strcmp(key, "implementation")) /* implementation limit */
534 tag->atts.entry.impl = get_tag_impl(value);
535 else if (0 == strcmp(key, "line")) /* line */
536 tag->atts.entry.line = atol(value);
537 else if (0 == strcmp(key, "access")) /* access */
538 tag->atts.entry.access = get_tag_access(value);
539 else if (0 == strcmp(key, "class") ||
540 0 == strcmp(key, "enum") ||
541 0 == strcmp(key, "function") ||
542 0 == strcmp(key, "struct") ||
543 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
545 g_free(tag->atts.entry.scope);
546 tag->atts.entry.scope = g_strdup(value);
548 else if (0 == strcmp(key, "file")) /* static (local) tag */
549 tag->atts.entry.local = TRUE;
550 else if (0 == strcmp(key, "signature")) /* arglist */
552 g_free(tag->atts.entry.arglist);
553 tag->atts.entry.arglist = g_strdup(value);
558 if (tm_tag_file_t != tag->type)
559 tag->atts.entry.file = file;
560 return TRUE;
563 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
565 TMTag *tag;
566 gboolean result = FALSE;
568 TAG_NEW(tag);
570 switch (format)
572 case TM_FILE_FORMAT_TAGMANAGER:
573 result = tm_tag_init_from_file(tag, file, fp);
574 break;
575 case TM_FILE_FORMAT_PIPE:
576 result = tm_tag_init_from_file_alt(tag, file, fp);
577 break;
578 case TM_FILE_FORMAT_CTAGS:
579 result = tm_tag_init_from_file_ctags(tag, file, fp);
580 break;
583 if (! result)
585 TAG_FREE(tag);
586 return NULL;
588 tag->atts.file.lang = mode;
589 return tag;
592 gboolean tm_tag_write(TMTag *tag, FILE *fp, guint attrs)
594 fprintf(fp, "%s", tag->name);
595 if (attrs & tm_tag_attr_type_t)
596 fprintf(fp, "%c%d", TA_TYPE, tag->type);
597 if (tag->type == tm_tag_file_t)
599 if (attrs & tm_tag_attr_time_t)
600 fprintf(fp, "%c%ld", TA_TIME, tag->atts.file.timestamp);
601 if (attrs & tm_tag_attr_lang_t)
602 fprintf(fp, "%c%d", TA_LANG, tag->atts.file.lang);
603 if ((attrs & tm_tag_attr_inactive_t) && tag->atts.file.inactive)
604 fprintf(fp, "%c%d", TA_INACTIVE, tag->atts.file.inactive);
606 else
608 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->atts.entry.arglist))
609 fprintf(fp, "%c%s", TA_ARGLIST, tag->atts.entry.arglist);
610 if (attrs & tm_tag_attr_line_t)
611 fprintf(fp, "%c%ld", TA_LINE, tag->atts.entry.line);
612 if (attrs & tm_tag_attr_local_t)
613 fprintf(fp, "%c%d", TA_LOCAL, tag->atts.entry.local);
614 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->atts.entry.scope))
615 fprintf(fp, "%c%s", TA_SCOPE, tag->atts.entry.scope);
616 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->atts.entry.inheritance))
617 fprintf(fp, "%c%s", TA_INHERITS, tag->atts.entry.inheritance);
618 if (attrs & tm_tag_attr_pointer_t)
619 fprintf(fp, "%c%d", TA_POINTER, tag->atts.entry.pointerOrder);
620 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->atts.entry.var_type))
621 fprintf(fp, "%c%s", TA_VARTYPE, tag->atts.entry.var_type);
622 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->atts.entry.access))
623 fprintf(fp, "%c%c", TA_ACCESS, tag->atts.entry.access);
624 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->atts.entry.impl))
625 fprintf(fp, "%c%c", TA_IMPL, tag->atts.entry.impl);
627 if (fprintf(fp, "\n"))
628 return TRUE;
629 else
630 return FALSE;
633 static void tm_tag_destroy(TMTag *tag)
635 g_free(tag->name);
636 if (tm_tag_file_t != tag->type)
638 g_free(tag->atts.entry.arglist);
639 g_free(tag->atts.entry.scope);
640 g_free(tag->atts.entry.inheritance);
641 g_free(tag->atts.entry.var_type);
645 #if 0
646 void tm_tag_free(gpointer tag)
648 tm_tag_unref(tag);
650 #endif
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);
663 TMTag *tm_tag_ref(TMTag *tag)
665 g_atomic_int_inc(&tag->refcount);
666 return tag;
669 int tm_tag_compare(const void *ptr1, const void *ptr2)
671 unsigned int *sort_attr;
672 int returnval = 0;
673 TMTag *t1 = *((TMTag **) ptr1);
674 TMTag *t2 = *((TMTag **) ptr2);
676 if ((NULL == t1) || (NULL == t2))
678 g_warning("Found NULL tag");
679 return t2 - t1;
681 if (NULL == s_sort_attrs)
683 if (s_partial)
684 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
685 else
686 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
689 for (sort_attr = s_sort_attrs; *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
691 switch (*sort_attr)
693 case tm_tag_attr_name_t:
694 if (s_partial)
695 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
696 else
697 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
698 if (0 != returnval)
699 return returnval;
700 break;
701 case tm_tag_attr_type_t:
702 if (0 != (returnval = (t1->type - t2->type)))
703 return returnval;
704 break;
705 case tm_tag_attr_file_t:
706 if (0 != (returnval = (t1->atts.entry.file - t2->atts.entry.file)))
707 return returnval;
708 break;
709 case tm_tag_attr_scope_t:
710 if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.scope, ""), FALLBACK(t2->atts.entry.scope, ""))))
711 return returnval;
712 break;
713 case tm_tag_attr_arglist_t:
714 if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.arglist, ""), FALLBACK(t2->atts.entry.arglist, ""))))
716 int line_diff = (t1->atts.entry.line - t2->atts.entry.line);
718 return line_diff ? line_diff : returnval;
720 break;
721 case tm_tag_attr_vartype_t:
722 if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.var_type, ""), FALLBACK(t2->atts.entry.var_type, ""))))
723 return returnval;
724 break;
725 case tm_tag_attr_line_t:
726 if (0 != (returnval = (t1->atts.entry.line - t2->atts.entry.line)))
727 return returnval;
728 break;
731 return returnval;
734 gboolean tm_tags_prune(GPtrArray *tags_array)
736 guint i, count;
737 for (i=0, count = 0; i < tags_array->len; ++i)
739 if (NULL != tags_array->pdata[i])
740 tags_array->pdata[count++] = tags_array->pdata[i];
742 tags_array->len = count;
743 return TRUE;
746 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes)
748 guint i;
750 if ((!tags_array) || (!tags_array->len))
751 return TRUE;
752 s_sort_attrs = sort_attributes;
753 s_partial = FALSE;
754 for (i = 1; i < tags_array->len; ++i)
756 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i])))
758 tags_array->pdata[i-1] = NULL;
761 tm_tags_prune(tags_array);
762 return TRUE;
765 gboolean tm_tags_custom_dedup(GPtrArray *tags_array, TMTagCompareFunc compare_func)
767 guint i;
769 if ((!tags_array) || (!tags_array->len))
770 return TRUE;
771 for (i = 1; i < tags_array->len; ++i)
773 if (0 == compare_func(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i])))
774 tags_array->pdata[i-1] = NULL;
776 tm_tags_prune(tags_array);
777 return TRUE;
780 /* Sorts newly-added tags and merges them in order with existing tags.
781 * This is much faster than resorting the whole array.
782 * Note: Having the caller append to the existing array should be faster
783 * than creating a new array which would likely get resized more than once.
784 * tags_array: array with new (perhaps unsorted) tags appended.
785 * orig_len: number of existing tags. */
786 gboolean tm_tags_merge(GPtrArray *tags_array, gsize orig_len,
787 TMTagAttrType *sort_attributes, gboolean dedup)
789 gpointer *copy, *a, *b;
790 gsize copy_len, i;
792 if ((!tags_array) || (!tags_array->len) || orig_len >= tags_array->len)
793 return TRUE;
794 if (!orig_len)
795 return tm_tags_sort(tags_array, sort_attributes, dedup);
796 copy_len = tags_array->len - orig_len;
797 copy = g_memdup(tags_array->pdata + orig_len, copy_len * sizeof(gpointer));
798 s_sort_attrs = sort_attributes;
799 s_partial = FALSE;
800 /* enforce copy sorted with same attributes for merge */
801 qsort(copy, copy_len, sizeof(gpointer), tm_tag_compare);
802 a = tags_array->pdata + orig_len - 1;
803 b = copy + copy_len - 1;
804 for (i = tags_array->len - 1;; i--)
806 gint cmp = tm_tag_compare(a, b);
808 tags_array->pdata[i] = (cmp >= 0) ? *a-- : *b--;
809 if (a < tags_array->pdata)
811 /* include remainder of copy as well as current value of b */
812 memcpy(tags_array->pdata, copy, ((b + 1) - copy) * sizeof(gpointer));
813 break;
815 if (b < copy)
816 break; /* remaining elements of 'a' are in place already */
817 g_assert(i != 0);
819 s_sort_attrs = NULL;
820 g_free(copy);
821 if (dedup)
822 tm_tags_dedup(tags_array, sort_attributes);
823 return TRUE;
826 gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean dedup)
828 if ((!tags_array) || (!tags_array->len))
829 return TRUE;
830 s_sort_attrs = sort_attributes;
831 s_partial = FALSE;
832 qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), tm_tag_compare);
833 s_sort_attrs = NULL;
834 if (dedup)
835 tm_tags_dedup(tags_array, sort_attributes);
836 return TRUE;
839 gboolean tm_tags_custom_sort(GPtrArray *tags_array, TMTagCompareFunc compare_func, gboolean dedup)
841 if ((!tags_array) || (!tags_array->len))
842 return TRUE;
843 qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), compare_func);
844 if (dedup)
845 tm_tags_custom_dedup(tags_array, compare_func);
846 return TRUE;
849 GPtrArray *tm_tags_extract(GPtrArray *tags_array, guint tag_types)
851 GPtrArray *new_tags;
852 guint i;
853 if (NULL == tags_array)
854 return NULL;
855 new_tags = g_ptr_array_new();
856 for (i=0; i < tags_array->len; ++i)
858 if (NULL != tags_array->pdata[i])
860 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
861 g_ptr_array_add(new_tags, tags_array->pdata[i]);
864 return new_tags;
867 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
869 if (tags_array)
871 guint i;
872 for (i = 0; i < tags_array->len; ++i)
873 tm_tag_unref(tags_array->pdata[i]);
874 if (free_all)
875 g_ptr_array_free(tags_array, TRUE);
876 else
877 g_ptr_array_set_size(tags_array, 0);
881 TMTag **tm_tags_find(const GPtrArray *sorted_tags_array, const char *name,
882 gboolean partial, int * tagCount)
884 static TMTag *tag = NULL;
885 TMTag **result;
886 int tagMatches=0;
888 if ((!sorted_tags_array) || (!sorted_tags_array->len))
889 return NULL;
891 if (NULL == tag)
892 tag = g_new0(TMTag, 1);
893 tag->name = (char *) name;
894 s_sort_attrs = NULL;
895 s_partial = partial;
896 result = (TMTag **) bsearch(&tag, sorted_tags_array->pdata, sorted_tags_array->len
897 , sizeof(gpointer), tm_tag_compare);
898 /* There can be matches on both sides of result */
899 if (result)
901 TMTag **last = (TMTag **) &sorted_tags_array->pdata[sorted_tags_array->len - 1];
902 TMTag **adv;
904 /* First look for any matches after result */
905 adv = result;
906 adv++;
907 for (; adv <= last && *adv; ++ adv)
909 if (0 != tm_tag_compare(&tag, adv))
910 break;
911 ++tagMatches;
913 /* Now look for matches from result and below */
914 for (; result >= (TMTag **) sorted_tags_array->pdata; -- result)
916 if (0 != tm_tag_compare(&tag, (TMTag **) result))
917 break;
918 ++tagMatches;
920 *tagCount=tagMatches;
921 ++ result; /* Correct address for the last successful match */
923 s_partial = FALSE;
924 return (TMTag **) result;
927 const char *tm_tag_type_name(const TMTag *tag)
929 g_return_val_if_fail(tag, NULL);
930 switch(tag->type)
932 case tm_tag_class_t: return "class";
933 case tm_tag_enum_t: return "enum";
934 case tm_tag_enumerator_t: return "enumval";
935 case tm_tag_field_t: return "field";
936 case tm_tag_function_t: return "function";
937 case tm_tag_interface_t: return "interface";
938 case tm_tag_member_t: return "member";
939 case tm_tag_method_t: return "method";
940 case tm_tag_namespace_t: return "namespace";
941 case tm_tag_package_t: return "package";
942 case tm_tag_prototype_t: return "prototype";
943 case tm_tag_struct_t: return "struct";
944 case tm_tag_typedef_t: return "typedef";
945 case tm_tag_union_t: return "union";
946 case tm_tag_variable_t: return "variable";
947 case tm_tag_externvar_t: return "extern";
948 case tm_tag_macro_t: return "define";
949 case tm_tag_macro_with_arg_t: return "macro";
950 case tm_tag_file_t: return "file";
951 default: return NULL;
953 return NULL;
956 TMTagType tm_tag_name_type(const char* tag_name)
958 g_return_val_if_fail(tag_name, tm_tag_undef_t);
960 if (strcmp(tag_name, "class") == 0) return tm_tag_class_t;
961 else if (strcmp(tag_name, "enum") == 0) return tm_tag_enum_t;
962 else if (strcmp(tag_name, "enumval") == 0) return tm_tag_enumerator_t;
963 else if (strcmp(tag_name, "field") == 0) return tm_tag_field_t;
964 else if (strcmp(tag_name, "function") == 0) return tm_tag_function_t;
965 else if (strcmp(tag_name, "interface") == 0) return tm_tag_interface_t;
966 else if (strcmp(tag_name, "member") == 0) return tm_tag_member_t;
967 else if (strcmp(tag_name, "method") == 0) return tm_tag_method_t;
968 else if (strcmp(tag_name, "namespace") == 0) return tm_tag_namespace_t;
969 else if (strcmp(tag_name, "package") == 0) return tm_tag_package_t;
970 else if (strcmp(tag_name, "prototype") == 0) return tm_tag_prototype_t;
971 else if (strcmp(tag_name, "struct") == 0) return tm_tag_struct_t;
972 else if (strcmp(tag_name, "typedef") == 0) return tm_tag_typedef_t;
973 else if (strcmp(tag_name, "union") == 0) return tm_tag_union_t;
974 else if (strcmp(tag_name, "variable") == 0) return tm_tag_variable_t;
975 else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t;
976 else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t;
977 else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t;
978 else if (strcmp(tag_name, "file") == 0) return tm_tag_file_t;
979 else return tm_tag_undef_t;
982 static const char *tm_tag_impl_name(TMTag *tag)
984 g_return_val_if_fail(tag && (tm_tag_file_t != tag->type), NULL);
985 if (TAG_IMPL_VIRTUAL == tag->atts.entry.impl)
986 return "virtual";
987 else
988 return NULL;
991 static const char *tm_tag_access_name(TMTag *tag)
993 g_return_val_if_fail(tag && (tm_tag_file_t != tag->type), NULL);
994 if (TAG_ACCESS_PUBLIC == tag->atts.entry.access)
995 return "public";
996 else if (TAG_ACCESS_PROTECTED == tag->atts.entry.access)
997 return "protected";
998 else if (TAG_ACCESS_PRIVATE == tag->atts.entry.access)
999 return "private";
1000 else
1001 return NULL;
1004 void tm_tag_print(TMTag *tag, FILE *fp)
1006 const char *laccess, *impl, *type;
1007 if (!tag || !fp)
1008 return;
1009 if (tm_tag_file_t == tag->type)
1011 fprintf(fp, "%s\n", tag->name);
1012 return;
1014 laccess = tm_tag_access_name(tag);
1015 impl = tm_tag_impl_name(tag);
1016 type = tm_tag_type_name(tag);
1017 if (laccess)
1018 fprintf(fp, "%s ", laccess);
1019 if (impl)
1020 fprintf(fp, "%s ", impl);
1021 if (type)
1022 fprintf(fp, "%s ", type);
1023 if (tag->atts.entry.var_type)
1024 fprintf(fp, "%s ", tag->atts.entry.var_type);
1025 if (tag->atts.entry.scope)
1026 fprintf(fp, "%s::", tag->atts.entry.scope);
1027 fprintf(fp, "%s", tag->name);
1028 if (tag->atts.entry.arglist)
1029 fprintf(fp, "%s", tag->atts.entry.arglist);
1030 if (tag->atts.entry.inheritance)
1031 fprintf(fp, " : from %s", tag->atts.entry.inheritance);
1032 if ((tag->atts.entry.file) && (tag->atts.entry.line > 0))
1033 fprintf(fp, "[%s:%ld]", tag->atts.entry.file->work_object.file_name
1034 , tag->atts.entry.line);
1035 fprintf(fp, "\n");
1038 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
1040 guint i;
1041 TMTag *tag;
1042 if (!(tags && (tags->len > 0) && fp))
1043 return;
1044 for (i = 0; i < tags->len; ++i)
1046 tag = TM_TAG(tags->pdata[i]);
1047 tm_tag_print(tag, fp);
1051 gint tm_tag_scope_depth(const TMTag *t)
1053 gint depth;
1054 char *s;
1055 if(!(t && t->atts.entry.scope))
1056 return 0;
1057 for (s = t->atts.entry.scope, depth = 0; s; s = strstr(s, "::"))
1059 ++ depth;
1060 ++ s;
1062 return depth;