From 103d2c535862ae2682022dcfd714c2ffd983c42f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 10 Mar 2016 21:37:56 +0100 Subject: [PATCH] Move code related to various tag file formats into tm_source_file.c This patch moves code related to reading/writing various tag file formats into form TMTag and TMWorkspace to TMSourceFile. The benefits of this change are: * only tm_source_file.c interfaces with ctags after this change * tagEntryInfo is removed from headers, no redefinitions needed any more * source code is more evenly distributed among tm_source_file.c, tm_tag.c and tm_workspace.c after the change (tm_tag.c got smaller) * all tag reading/writing is at a single place Despite its size, this patch mostly just moves code. Notable changes are: * tm_tag_new() now creates just an empty tag. The tag is filled by various init_* functions inside tm_source_file.c * there are new functions tm_source_file_read_tags_file() and tm_source_file_write_tags_file() - these hide tags file reading/writing details from tm_workspace.c * tm_source_file_write() debugging function got removed - tm_source_file_write_tags_file() does a similar thing and there's no need to keep around two functions doing the same. --- tagmanager/src/tm_source_file.c | 624 +++++++++++++++++++++++++++++++++++++--- tagmanager/src/tm_source_file.h | 9 +- tagmanager/src/tm_tag.c | 527 +-------------------------------- tagmanager/src/tm_tag.h | 23 +- tagmanager/src/tm_workspace.c | 69 +---- 5 files changed, 604 insertions(+), 648 deletions(-) diff --git a/tagmanager/src/tm_source_file.c b/tagmanager/src/tm_source_file.c index 0dcdf976a..712df7d23 100644 --- a/tagmanager/src/tm_source_file.c +++ b/tagmanager/src/tm_source_file.c @@ -31,7 +31,6 @@ #include "parse.h" #include "read.h" -#define LIBCTAGS_DEFINED #include "tm_source_file.h" #include "tm_tag.h" #include "tm_parser.h" @@ -42,6 +41,77 @@ typedef struct guint refcount; } TMSourceFilePriv; + +typedef enum { + TM_FILE_FORMAT_TAGMANAGER, + TM_FILE_FORMAT_PIPE, + TM_FILE_FORMAT_CTAGS +} TMFileFormat; + +/* Note: To preserve binary compatibility, it is very important + that you only *append* to this list ! */ +enum +{ + TA_NAME = 200, + TA_LINE, + TA_LOCAL, + TA_POS, /* Obsolete */ + TA_TYPE, + TA_ARGLIST, + TA_SCOPE, + TA_VARTYPE, + TA_INHERITS, + TA_TIME, + TA_ACCESS, + TA_IMPL, + TA_LANG, + TA_INACTIVE, /* Obsolete */ + TA_POINTER +}; + +static const char *s_tag_type_names[] = { + "class", /* classes */ + "enum", /* enumeration names */ + "enumerator", /* enumerators (values inside an enumeration) */ + "externvar", /* external variable declarations */ + "field", /* fields */ + "function", /* function definitions */ + "interface", /* interfaces */ + "macro", /* macro definitions */ + "member", /* class, struct, and union members */ + "method", /* methods */ + "namespace", /* namespaces */ + "package", /* packages */ + "prototype", /* function prototypes */ + "struct", /* structure names */ + "typedef", /* typedefs */ + "union", /* union names */ + "variable", /* variable definitions */ + "other" /* Other tag type (non C/C++/Java) */ +}; + +static TMTagType s_tag_types[] = { + tm_tag_class_t, + tm_tag_enum_t, + tm_tag_enumerator_t, + tm_tag_externvar_t, + tm_tag_field_t, + tm_tag_function_t, + tm_tag_interface_t, + tm_tag_macro_t, + tm_tag_member_t, + tm_tag_method_t, + tm_tag_namespace_t, + tm_tag_package_t, + tm_tag_prototype_t, + tm_tag_struct_t, + tm_tag_typedef_t, + tm_tag_union_t, + tm_tag_variable_t, + tm_tag_other_t +}; + + #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv)) #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S) @@ -107,6 +177,519 @@ gchar *tm_get_real_path(const gchar *file_name) return NULL; } +static TMTagType get_tag_type(const char *tag_name) +{ + unsigned int i; + int cmp; + g_return_val_if_fail(tag_name, 0); + for (i=0; i < sizeof(s_tag_type_names)/sizeof(char *); ++i) + { + cmp = strcmp(tag_name, s_tag_type_names[i]); + if (0 == cmp) + return s_tag_types[i]; + else if (cmp < 0) + break; + } + /* other is not checked above as it is last, not sorted alphabetically */ + if (strcmp(tag_name, "other") == 0) + return tm_tag_other_t; +#ifdef TM_DEBUG + fprintf(stderr, "Unknown tag type %s\n", tag_name); +#endif + return tm_tag_undef_t; +} + +static char get_tag_impl(const char *impl) +{ + if ((0 == strcmp("virtual", impl)) + || (0 == strcmp("pure virtual", impl))) + return TAG_IMPL_VIRTUAL; + +#ifdef TM_DEBUG + g_warning("Unknown implementation %s", impl); +#endif + return TAG_IMPL_UNKNOWN; +} + +static char get_tag_access(const char *access) +{ + if (0 == strcmp("public", access)) + return TAG_ACCESS_PUBLIC; + else if (0 == strcmp("protected", access)) + return TAG_ACCESS_PROTECTED; + else if (0 == strcmp("private", access)) + return TAG_ACCESS_PRIVATE; + else if (0 == strcmp("friend", access)) + return TAG_ACCESS_FRIEND; + else if (0 == strcmp("default", access)) + return TAG_ACCESS_DEFAULT; + +#ifdef TM_DEBUG + g_warning("Unknown access type %s", access); +#endif + return TAG_ACCESS_UNKNOWN; +} + +/* + Initializes a TMTag structure with information from a tagEntryInfo struct + used by the ctags parsers. Note that the TMTag structure must be malloc()ed + before calling this function. This function is called by tm_tag_new() - you + should not need to call this directly. + @param tag The TMTag structure to initialize + @param file Pointer to a TMSourceFile struct (it is assigned to the file member) + @param tag_entry Tag information gathered by the ctags parser + @return TRUE on success, FALSE on failure +*/ +static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry) +{ + if (NULL == tag_entry) + return FALSE; + + /* This is a normal tag entry */ + if (NULL == tag_entry->name) + return FALSE; + tag->name = g_strdup(tag_entry->name); + tag->type = get_tag_type(tag_entry->kindName); + tag->local = tag_entry->isFileScope; + tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */ + tag->line = tag_entry->lineNumber; + if (NULL != tag_entry->extensionFields.arglist) + tag->arglist = g_strdup(tag_entry->extensionFields.arglist); + if ((NULL != tag_entry->extensionFields.scope[1]) && + (0 != tag_entry->extensionFields.scope[1][0])) + tag->scope = g_strdup(tag_entry->extensionFields.scope[1]); + if (tag_entry->extensionFields.inheritance != NULL) + tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance); + if (tag_entry->extensionFields.varType != NULL) + tag->var_type = g_strdup(tag_entry->extensionFields.varType); + if (tag_entry->extensionFields.access != NULL) + tag->access = get_tag_access(tag_entry->extensionFields.access); + if (tag_entry->extensionFields.implementation != NULL) + tag->impl = get_tag_impl(tag_entry->extensionFields.implementation); + if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) + tag->type = tm_tag_macro_with_arg_t; + tag->file = file; + tag->lang = file->lang; + return TRUE; +} + +/* + Initializes an already malloc()ed TMTag structure by reading a tag entry + line from a file. The structure should be allocated beforehand. + @param tag The TMTag structure to populate + @param file The TMSourceFile struct (assigned to the file member) + @param fp FILE pointer from where the tag line is read + @return TRUE on success, FALSE on FAILURE +*/ +static gboolean init_tag_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) +{ + guchar buf[BUFSIZ]; + guchar *start, *end; + gboolean status; + guchar changed_char = TA_NAME; + + tag->refcount = 1; + if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf)) + return FALSE; + for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end) + { + while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n')) + ++ end; + if (('\0' == *end) || ('\n' == *end)) + status = FALSE; + changed_char = *end; + *end = '\0'; + if (NULL == tag->name) + { + if (!isprint(*start)) + return FALSE; + else + tag->name = g_strdup((gchar*)start); + } + else + { + switch (*start) + { + case TA_LINE: + tag->line = atol((gchar*)start + 1); + break; + case TA_LOCAL: + tag->local = atoi((gchar*)start + 1); + break; + case TA_TYPE: + tag->type = (TMTagType) atoi((gchar*)start + 1); + break; + case TA_ARGLIST: + tag->arglist = g_strdup((gchar*)start + 1); + break; + case TA_SCOPE: + tag->scope = g_strdup((gchar*)start + 1); + break; + case TA_POINTER: + tag->pointerOrder = atoi((gchar*)start + 1); + break; + case TA_VARTYPE: + tag->var_type = g_strdup((gchar*)start + 1); + break; + case TA_INHERITS: + tag->inheritance = g_strdup((gchar*)start + 1); + break; + case TA_TIME: /* Obsolete */ + break; + case TA_LANG: /* Obsolete */ + break; + case TA_INACTIVE: /* Obsolete */ + break; + case TA_ACCESS: + tag->access = (char) *(start + 1); + break; + case TA_IMPL: + tag->impl = (char) *(start + 1); + break; + default: +#ifdef GEANY_DEBUG + g_warning("Unknown attribute %s", start + 1); +#endif + break; + } + } + *end = changed_char; + } + if (NULL == tag->name) + return FALSE; + tag->file = file; + return TRUE; +} + +/* alternative parser for Pascal and LaTeX global tags files with the following format + * tagname|return value|arglist|description\n */ +static gboolean init_tag_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp) +{ + guchar buf[BUFSIZ]; + guchar *start, *end; + gboolean status; + /*guchar changed_char = TA_NAME;*/ + + tag->refcount = 1; + if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf)) + return FALSE; + { + gchar **fields; + guint field_len; + for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end) + { + while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n')) + ++ end; + if (('\0' == *end) || ('\n' == *end)) + status = FALSE; + /*changed_char = *end;*/ + *end = '\0'; + if (NULL == tag->name && !isprint(*start)) + return FALSE; + + fields = g_strsplit((gchar*)start, "|", -1); + field_len = g_strv_length(fields); + + if (field_len >= 1) tag->name = g_strdup(fields[0]); + else tag->name = NULL; + if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]); + if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]); + tag->type = tm_tag_prototype_t; + g_strfreev(fields); + } + } + + if (NULL == tag->name) + return FALSE; + tag->file = file; + return TRUE; +} + +/* + CTags tag file format (http://ctags.sourceforge.net/FORMAT) +*/ +static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) +{ + gchar buf[BUFSIZ]; + gchar *p, *tab; + + tag->refcount = 1; + tag->type = tm_tag_function_t; /* default type is function if no kind is specified */ + do + { + if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf)) + return FALSE; + } + while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */ + + p = buf; + + /* tag name */ + if (! (tab = strchr(p, '\t')) || p == tab) + return FALSE; + tag->name = g_strndup(p, (gsize)(tab - p)); + p = tab + 1; + + /* tagfile, unused */ + if (! (tab = strchr(p, '\t'))) + { + g_free(tag->name); + tag->name = NULL; + return FALSE; + } + p = tab + 1; + /* Ex command, unused */ + if (*p == '/' || *p == '?') + { + gchar c = *p; + for (++p; *p && *p != c; p++) + { + if (*p == '\\' && p[1]) + p++; + } + } + else /* assume a line */ + tag->line = atol(p); + tab = strstr(p, ";\""); + /* read extension fields */ + if (tab) + { + p = tab + 2; + while (*p && *p != '\n' && *p != '\r') + { + gchar *end; + const gchar *key, *value = NULL; + + /* skip leading tabulations */ + while (*p && *p == '\t') p++; + /* find the separator (:) and end (\t) */ + key = end = p; + while (*end && *end != '\t' && *end != '\n' && *end != '\r') + { + if (*end == ':' && ! value) + { + *end = 0; /* terminate the key */ + value = end + 1; + } + end++; + } + /* move p paste the so we won't stop parsing by setting *end=0 below */ + p = *end ? end + 1 : end; + *end = 0; /* terminate the value (or key if no value) */ + + if (! value || 0 == strcmp(key, "kind")) /* tag kind */ + { + const gchar *kind = value ? value : key; + + if (kind[0] && kind[1]) + tag->type = get_tag_type(kind); + else + { + switch (*kind) + { + case 'c': tag->type = tm_tag_class_t; break; + case 'd': tag->type = tm_tag_macro_t; break; + case 'e': tag->type = tm_tag_enumerator_t; break; + case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */ + case 'f': tag->type = tm_tag_function_t; break; + case 'g': tag->type = tm_tag_enum_t; break; + case 'I': tag->type = tm_tag_class_t; break; + case 'i': tag->type = tm_tag_interface_t; break; + case 'l': tag->type = tm_tag_variable_t; break; + case 'M': tag->type = tm_tag_macro_t; break; + case 'm': tag->type = tm_tag_member_t; break; + case 'n': tag->type = tm_tag_namespace_t; break; + case 'P': tag->type = tm_tag_package_t; break; + case 'p': tag->type = tm_tag_prototype_t; break; + case 's': tag->type = tm_tag_struct_t; break; + case 't': tag->type = tm_tag_typedef_t; break; + case 'u': tag->type = tm_tag_union_t; break; + case 'v': tag->type = tm_tag_variable_t; break; + case 'x': tag->type = tm_tag_externvar_t; break; + default: +#ifdef TM_DEBUG + g_warning("Unknown tag kind %c", *kind); +#endif + tag->type = tm_tag_other_t; break; + } + } + } + else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */ + { + g_free(tag->inheritance); + tag->inheritance = g_strdup(value); + } + else if (0 == strcmp(key, "implementation")) /* implementation limit */ + tag->impl = get_tag_impl(value); + else if (0 == strcmp(key, "line")) /* line */ + tag->line = atol(value); + else if (0 == strcmp(key, "access")) /* access */ + tag->access = get_tag_access(value); + else if (0 == strcmp(key, "class") || + 0 == strcmp(key, "enum") || + 0 == strcmp(key, "function") || + 0 == strcmp(key, "struct") || + 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */ + { + g_free(tag->scope); + tag->scope = g_strdup(value); + } + else if (0 == strcmp(key, "file")) /* static (local) tag */ + tag->local = TRUE; + else if (0 == strcmp(key, "signature")) /* arglist */ + { + g_free(tag->arglist); + tag->arglist = g_strdup(value); + } + } + } + + tag->file = file; + return TRUE; +} + +static TMTag *new_tag_from_tags_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format) +{ + TMTag *tag = tm_tag_new(); + gboolean result = FALSE; + + switch (format) + { + case TM_FILE_FORMAT_TAGMANAGER: + result = init_tag_from_file(tag, file, fp); + break; + case TM_FILE_FORMAT_PIPE: + result = init_tag_from_file_alt(tag, file, fp); + break; + case TM_FILE_FORMAT_CTAGS: + result = init_tag_from_file_ctags(tag, file, fp); + break; + } + + if (! result) + { + tm_tag_unref(tag); + return NULL; + } + tag->lang = mode; + return tag; +} + +/* + Writes tag information to the given FILE *. + @param tag The tag information to write. + @param file FILE pointer to which the tag information is written. + @param attrs Attributes to be written (bitmask). + @return TRUE on success, FALSE on failure. +*/ +static gboolean write_tag(TMTag *tag, FILE *fp, TMTagAttrType attrs) +{ + fprintf(fp, "%s", tag->name); + if (attrs & tm_tag_attr_type_t) + fprintf(fp, "%c%d", TA_TYPE, tag->type); + if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist)) + fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist); + if (attrs & tm_tag_attr_line_t) + fprintf(fp, "%c%ld", TA_LINE, tag->line); + if (attrs & tm_tag_attr_local_t) + fprintf(fp, "%c%d", TA_LOCAL, tag->local); + if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope)) + fprintf(fp, "%c%s", TA_SCOPE, tag->scope); + if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance)) + fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance); + if (attrs & tm_tag_attr_pointer_t) + fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder); + if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type)) + fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type); + if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access)) + fprintf(fp, "%c%c", TA_ACCESS, tag->access); + if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl)) + fprintf(fp, "%c%c", TA_IMPL, tag->impl); + + if (fprintf(fp, "\n")) + return TRUE; + else + return FALSE; +} + +GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode) +{ + guchar buf[BUFSIZ]; + FILE *fp; + GPtrArray *file_tags, *new_tags; + TMTag *tag; + TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER; + + if (NULL == (fp = g_fopen(tags_file, "r"))) + return NULL; + if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf)) + { + fclose(fp); + return NULL; /* early out on error */ + } + else + { /* We read the first line for the format specification. */ + if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL) + format = TM_FILE_FORMAT_PIPE; + else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL) + format = TM_FILE_FORMAT_TAGMANAGER; + else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL) + format = TM_FILE_FORMAT_CTAGS; + else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0) + format = TM_FILE_FORMAT_CTAGS; + else + { /* We didn't find a valid format specification, so we try to auto-detect the format + * by counting the pipe characters on the first line and asumme pipe format when + * we find more than one pipe on the line. */ + guint i, pipe_cnt = 0, tab_cnt = 0; + for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++) + { + if (buf[i] == '|') + pipe_cnt++; + else if (buf[i] == '\t') + tab_cnt++; + } + if (pipe_cnt > 1) + format = TM_FILE_FORMAT_PIPE; + else if (tab_cnt > 1) + format = TM_FILE_FORMAT_CTAGS; + } + rewind(fp); /* reset the file pointer, to start reading again from the beginning */ + } + + file_tags = g_ptr_array_new(); + while (NULL != (tag = new_tag_from_tags_file(NULL, fp, mode, format))) + g_ptr_array_add(file_tags, tag); + fclose(fp); + + return file_tags; +} + +gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array) +{ + guint i; + FILE *fp; + + g_return_val_if_fail(tags_array && tags_file, FALSE); + + fp = g_fopen(tags_file, "w"); + if (!fp) + return FALSE; + + fprintf(fp, "# format=tagmanager\n"); + for (i = 0; i < tags_array->len; i++) + { + TMTag *tag = TM_TAG(tags_array->pdata[i]); + + write_tag(tag, fp, tm_tag_attr_type_t + | tm_tag_attr_scope_t | tm_tag_attr_arglist_t | tm_tag_attr_vartype_t + | tm_tag_attr_pointer_t); + } + fclose(fp); + + return TRUE; +} + /* add argument list of __init__() Python methods to the class tag */ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file) { @@ -145,7 +728,9 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source static int tm_source_file_tags(const tagEntryInfo *tag, void *user_data) { TMSourceFile *current_source_file = user_data; - TMTag *tm_tag = tm_tag_new(current_source_file, tag); + TMTag *tm_tag = tm_tag_new(); + + init_tag(tm_tag, current_source_file, tag); if (tm_tag->lang == TM_PARSER_PYTHON) update_python_arglist(tm_tag, current_source_file); @@ -405,38 +990,3 @@ TMParserType tm_source_file_get_named_lang(const gchar *name) { return getNamedLanguage(name); } - -#if 0 -/* - Writes all tags of a source file (including the file tag itself) to the passed - file pointer. - @param source_file The source file to write. - @param fp The file pointer to write to. - @param attrs The attributes to write. - @return TRUE on success, FALSE on failure. -*/ -static gboolean tm_source_file_write(TMSourceFile *source_file, FILE *fp, guint attrs) -{ - TMTag *tag; - guint i; - - if (NULL != source_file) - { - if (NULL != (tag = tm_tag_new(source_file, NULL))) - { - tm_tag_write(tag, fp, tm_tag_attr_max_t); - tm_tag_unref(tag); - if (NULL != source_file->tags_array) - { - for (i=0; i < source_file->tags_array->len; ++i) - { - tag = TM_TAG(source_file->tags_array->pdata[i]); - if (TRUE != tm_tag_write(tag, fp, attrs)) - return FALSE; - } - } - } - } - return TRUE; -} -#endif diff --git a/tagmanager/src/tm_source_file.h b/tagmanager/src/tm_source_file.h index 2f816cff4..01520c5d6 100644 --- a/tagmanager/src/tm_source_file.h +++ b/tagmanager/src/tm_source_file.h @@ -16,10 +16,6 @@ #include "tm_parser.h" -#ifndef LIBCTAGS_DEFINED -typedef void tagEntryInfo; -#endif - G_BEGIN_DECLS /* Casts a pointer to a pointer to a TMSourceFile structure */ @@ -50,7 +46,6 @@ void tm_source_file_free(TMSourceFile *source_file); gchar *tm_get_real_path(const gchar *file_name); - #ifdef GEANY_PRIVATE const gchar *tm_source_file_get_lang_name(TMParserType lang); @@ -60,6 +55,10 @@ TMParserType tm_source_file_get_named_lang(const gchar *name); gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size, gboolean use_buffer); +GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode); + +gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array); + #endif /* GEANY_PRIVATE */ G_END_DECLS diff --git a/tagmanager/src/tm_tag.c b/tagmanager/src/tm_tag.c index dbab8d5a7..6db1ed56a 100644 --- a/tagmanager/src/tm_tag.c +++ b/tagmanager/src/tm_tag.c @@ -11,13 +11,7 @@ #include #include -#include "general.h" -#include "entry.h" -#include "parse.h" -#include "read.h" -#define LIBCTAGS_DEFINED #include "tm_tag.h" -#include "tm_parser.h" #define TAG_NEW(T) ((T) = g_slice_new0(TMTag)) @@ -79,32 +73,6 @@ static void log_tag_free(TMTag *tag) #endif /* DEBUG_TAG_REFS */ -const TMTagType TM_GLOBAL_TYPE_MASK = - tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t | - tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t; - - -/* Note: To preserve binary compatibility, it is very important - that you only *append* to this list ! */ -enum -{ - TA_NAME = 200, - TA_LINE, - TA_LOCAL, - TA_POS, /* Obsolete */ - TA_TYPE, - TA_ARGLIST, - TA_SCOPE, - TA_VARTYPE, - TA_INHERITS, - TA_TIME, - TA_ACCESS, - TA_IMPL, - TA_LANG, - TA_INACTIVE, /* Obsolete */ - TA_POINTER -}; - typedef struct { guint *sort_attrs; @@ -113,48 +81,6 @@ typedef struct gboolean first; } TMSortOptions; -static const char *s_tag_type_names[] = { - "class", /* classes */ - "enum", /* enumeration names */ - "enumerator", /* enumerators (values inside an enumeration) */ - "externvar", /* external variable declarations */ - "field", /* fields */ - "function", /* function definitions */ - "interface", /* interfaces */ - "macro", /* macro definitions */ - "member", /* class, struct, and union members */ - "method", /* methods */ - "namespace", /* namespaces */ - "package", /* packages */ - "prototype", /* function prototypes */ - "struct", /* structure names */ - "typedef", /* typedefs */ - "union", /* union names */ - "variable", /* variable definitions */ - "other" /* Other tag type (non C/C++/Java) */ -}; - -static TMTagType s_tag_types[] = { - tm_tag_class_t, - tm_tag_enum_t, - tm_tag_enumerator_t, - tm_tag_externvar_t, - tm_tag_field_t, - tm_tag_function_t, - tm_tag_interface_t, - tm_tag_macro_t, - tm_tag_member_t, - tm_tag_method_t, - tm_tag_namespace_t, - tm_tag_package_t, - tm_tag_prototype_t, - tm_tag_struct_t, - tm_tag_typedef_t, - tm_tag_union_t, - tm_tag_variable_t, - tm_tag_other_t -}; - /* Gets the GType for a TMTag */ GType tm_tag_get_type(void) { @@ -167,470 +93,21 @@ GType tm_tag_get_type(void) return gtype; } -static TMTagType get_tag_type(const char *tag_name) -{ - unsigned int i; - int cmp; - g_return_val_if_fail(tag_name, 0); - for (i=0; i < sizeof(s_tag_type_names)/sizeof(char *); ++i) - { - cmp = strcmp(tag_name, s_tag_type_names[i]); - if (0 == cmp) - return s_tag_types[i]; - else if (cmp < 0) - break; - } - /* other is not checked above as it is last, not sorted alphabetically */ - if (strcmp(tag_name, "other") == 0) - return tm_tag_other_t; -#ifdef TM_DEBUG - fprintf(stderr, "Unknown tag type %s\n", tag_name); -#endif - return tm_tag_undef_t; -} - -static char get_tag_impl(const char *impl) -{ - if ((0 == strcmp("virtual", impl)) - || (0 == strcmp("pure virtual", impl))) - return TAG_IMPL_VIRTUAL; - -#ifdef TM_DEBUG - g_warning("Unknown implementation %s", impl); -#endif - return TAG_IMPL_UNKNOWN; -} - -static char get_tag_access(const char *access) -{ - if (0 == strcmp("public", access)) - return TAG_ACCESS_PUBLIC; - else if (0 == strcmp("protected", access)) - return TAG_ACCESS_PROTECTED; - else if (0 == strcmp("private", access)) - return TAG_ACCESS_PRIVATE; - else if (0 == strcmp("friend", access)) - return TAG_ACCESS_FRIEND; - else if (0 == strcmp("default", access)) - return TAG_ACCESS_DEFAULT; - -#ifdef TM_DEBUG - g_warning("Unknown access type %s", access); -#endif - return TAG_ACCESS_UNKNOWN; -} - -/* - Initializes a TMTag structure with information from a tagEntryInfo struct - used by the ctags parsers. Note that the TMTag structure must be malloc()ed - before calling this function. This function is called by tm_tag_new() - you - should not need to call this directly. - @param tag The TMTag structure to initialize - @param file Pointer to a TMSourceFile struct (it is assigned to the file member) - @param tag_entry Tag information gathered by the ctags parser - @return TRUE on success, FALSE on failure -*/ -static gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry) -{ - tag->refcount = 1; - if (NULL == tag_entry) - return FALSE; - - /* This is a normal tag entry */ - if (NULL == tag_entry->name) - return FALSE; - tag->name = g_strdup(tag_entry->name); - tag->type = get_tag_type(tag_entry->kindName); - tag->local = tag_entry->isFileScope; - tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */ - tag->line = tag_entry->lineNumber; - if (NULL != tag_entry->extensionFields.arglist) - tag->arglist = g_strdup(tag_entry->extensionFields.arglist); - if ((NULL != tag_entry->extensionFields.scope[1]) && - (0 != tag_entry->extensionFields.scope[1][0])) - tag->scope = g_strdup(tag_entry->extensionFields.scope[1]); - if (tag_entry->extensionFields.inheritance != NULL) - tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance); - if (tag_entry->extensionFields.varType != NULL) - tag->var_type = g_strdup(tag_entry->extensionFields.varType); - if (tag_entry->extensionFields.access != NULL) - tag->access = get_tag_access(tag_entry->extensionFields.access); - if (tag_entry->extensionFields.implementation != NULL) - tag->impl = get_tag_impl(tag_entry->extensionFields.implementation); - if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) - tag->type = tm_tag_macro_with_arg_t; - tag->file = file; - tag->lang = file->lang; - return TRUE; -} - /* - Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer - and returns a pointer to it. - @param file - Pointer to the TMSourceFile structure containing the tag - @param tag_entry Contains tag information generated by ctags + Creates a new tag structure and returns a pointer to it. @return the new TMTag structure. This should be free()-ed using tm_tag_free() */ -TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry) +TMTag *tm_tag_new(void) { TMTag *tag; TAG_NEW(tag); - if (FALSE == tm_tag_init(tag, file, tag_entry)) - { - TAG_FREE(tag); - return NULL; - } - return tag; -} - -/* - Initializes an already malloc()ed TMTag structure by reading a tag entry - line from a file. The structure should be allocated beforehand. - @param tag The TMTag structure to populate - @param file The TMSourceFile struct (assigned to the file member) - @param fp FILE pointer from where the tag line is read - @return TRUE on success, FALSE on FAILURE -*/ -static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) -{ - guchar buf[BUFSIZ]; - guchar *start, *end; - gboolean status; - guchar changed_char = TA_NAME; - - tag->refcount = 1; - if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf)) - return FALSE; - for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end) - { - while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n')) - ++ end; - if (('\0' == *end) || ('\n' == *end)) - status = FALSE; - changed_char = *end; - *end = '\0'; - if (NULL == tag->name) - { - if (!isprint(*start)) - return FALSE; - else - tag->name = g_strdup((gchar*)start); - } - else - { - switch (*start) - { - case TA_LINE: - tag->line = atol((gchar*)start + 1); - break; - case TA_LOCAL: - tag->local = atoi((gchar*)start + 1); - break; - case TA_TYPE: - tag->type = (TMTagType) atoi((gchar*)start + 1); - break; - case TA_ARGLIST: - tag->arglist = g_strdup((gchar*)start + 1); - break; - case TA_SCOPE: - tag->scope = g_strdup((gchar*)start + 1); - break; - case TA_POINTER: - tag->pointerOrder = atoi((gchar*)start + 1); - break; - case TA_VARTYPE: - tag->var_type = g_strdup((gchar*)start + 1); - break; - case TA_INHERITS: - tag->inheritance = g_strdup((gchar*)start + 1); - break; - case TA_TIME: /* Obsolete */ - break; - case TA_LANG: /* Obsolete */ - break; - case TA_INACTIVE: /* Obsolete */ - break; - case TA_ACCESS: - tag->access = (char) *(start + 1); - break; - case TA_IMPL: - tag->impl = (char) *(start + 1); - break; - default: -#ifdef GEANY_DEBUG - g_warning("Unknown attribute %s", start + 1); -#endif - break; - } - } - *end = changed_char; - } - if (NULL == tag->name) - return FALSE; - tag->file = file; - return TRUE; -} - -/* alternative parser for Pascal and LaTeX global tags files with the following format - * tagname|return value|arglist|description\n */ -static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp) -{ - guchar buf[BUFSIZ]; - guchar *start, *end; - gboolean status; - /*guchar changed_char = TA_NAME;*/ - - tag->refcount = 1; - if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf)) - return FALSE; - { - gchar **fields; - guint field_len; - for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end) - { - while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n')) - ++ end; - if (('\0' == *end) || ('\n' == *end)) - status = FALSE; - /*changed_char = *end;*/ - *end = '\0'; - if (NULL == tag->name && !isprint(*start)) - return FALSE; - - fields = g_strsplit((gchar*)start, "|", -1); - field_len = g_strv_length(fields); - - if (field_len >= 1) tag->name = g_strdup(fields[0]); - else tag->name = NULL; - if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]); - if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]); - tag->type = tm_tag_prototype_t; - g_strfreev(fields); - } - } - - if (NULL == tag->name) - return FALSE; - tag->file = file; - return TRUE; -} - -/* - Same as tm_tag_init_from_file(), but parsing CTags tag file format - (http://ctags.sourceforge.net/FORMAT) -*/ -static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) -{ - gchar buf[BUFSIZ]; - gchar *p, *tab; - tag->refcount = 1; - tag->type = tm_tag_function_t; /* default type is function if no kind is specified */ - do - { - if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf)) - return FALSE; - } - while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */ - - p = buf; - - /* tag name */ - if (! (tab = strchr(p, '\t')) || p == tab) - return FALSE; - tag->name = g_strndup(p, (gsize)(tab - p)); - p = tab + 1; - - /* tagfile, unused */ - if (! (tab = strchr(p, '\t'))) - { - g_free(tag->name); - tag->name = NULL; - return FALSE; - } - p = tab + 1; - /* Ex command, unused */ - if (*p == '/' || *p == '?') - { - gchar c = *p; - for (++p; *p && *p != c; p++) - { - if (*p == '\\' && p[1]) - p++; - } - } - else /* assume a line */ - tag->line = atol(p); - tab = strstr(p, ";\""); - /* read extension fields */ - if (tab) - { - p = tab + 2; - while (*p && *p != '\n' && *p != '\r') - { - gchar *end; - const gchar *key, *value = NULL; - - /* skip leading tabulations */ - while (*p && *p == '\t') p++; - /* find the separator (:) and end (\t) */ - key = end = p; - while (*end && *end != '\t' && *end != '\n' && *end != '\r') - { - if (*end == ':' && ! value) - { - *end = 0; /* terminate the key */ - value = end + 1; - } - end++; - } - /* move p paste the so we won't stop parsing by setting *end=0 below */ - p = *end ? end + 1 : end; - *end = 0; /* terminate the value (or key if no value) */ - - if (! value || 0 == strcmp(key, "kind")) /* tag kind */ - { - const gchar *kind = value ? value : key; - if (kind[0] && kind[1]) - tag->type = get_tag_type(kind); - else - { - switch (*kind) - { - case 'c': tag->type = tm_tag_class_t; break; - case 'd': tag->type = tm_tag_macro_t; break; - case 'e': tag->type = tm_tag_enumerator_t; break; - case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */ - case 'f': tag->type = tm_tag_function_t; break; - case 'g': tag->type = tm_tag_enum_t; break; - case 'I': tag->type = tm_tag_class_t; break; - case 'i': tag->type = tm_tag_interface_t; break; - case 'l': tag->type = tm_tag_variable_t; break; - case 'M': tag->type = tm_tag_macro_t; break; - case 'm': tag->type = tm_tag_member_t; break; - case 'n': tag->type = tm_tag_namespace_t; break; - case 'P': tag->type = tm_tag_package_t; break; - case 'p': tag->type = tm_tag_prototype_t; break; - case 's': tag->type = tm_tag_struct_t; break; - case 't': tag->type = tm_tag_typedef_t; break; - case 'u': tag->type = tm_tag_union_t; break; - case 'v': tag->type = tm_tag_variable_t; break; - case 'x': tag->type = tm_tag_externvar_t; break; - default: -#ifdef TM_DEBUG - g_warning("Unknown tag kind %c", *kind); -#endif - tag->type = tm_tag_other_t; break; - } - } - } - else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */ - { - g_free(tag->inheritance); - tag->inheritance = g_strdup(value); - } - else if (0 == strcmp(key, "implementation")) /* implementation limit */ - tag->impl = get_tag_impl(value); - else if (0 == strcmp(key, "line")) /* line */ - tag->line = atol(value); - else if (0 == strcmp(key, "access")) /* access */ - tag->access = get_tag_access(value); - else if (0 == strcmp(key, "class") || - 0 == strcmp(key, "enum") || - 0 == strcmp(key, "function") || - 0 == strcmp(key, "struct") || - 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */ - { - g_free(tag->scope); - tag->scope = g_strdup(value); - } - else if (0 == strcmp(key, "file")) /* static (local) tag */ - tag->local = TRUE; - else if (0 == strcmp(key, "signature")) /* arglist */ - { - g_free(tag->arglist); - tag->arglist = g_strdup(value); - } - } - } - - tag->file = file; - return TRUE; -} - -/* - Same as tm_tag_new() except that the tag attributes are read from file. - @param mode TMParserType to use for the tag. -*/ -TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format) -{ - TMTag *tag; - gboolean result = FALSE; - - TAG_NEW(tag); - - switch (format) - { - case TM_FILE_FORMAT_TAGMANAGER: - result = tm_tag_init_from_file(tag, file, fp); - break; - case TM_FILE_FORMAT_PIPE: - result = tm_tag_init_from_file_alt(tag, file, fp); - break; - case TM_FILE_FORMAT_CTAGS: - result = tm_tag_init_from_file_ctags(tag, file, fp); - break; - } - - if (! result) - { - TAG_FREE(tag); - return NULL; - } - tag->lang = mode; return tag; } /* - Writes tag information to the given FILE *. - @param tag The tag information to write. - @param file FILE pointer to which the tag information is written. - @param attrs Attributes to be written (bitmask). - @return TRUE on success, FALSE on failure. -*/ -gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs) -{ - fprintf(fp, "%s", tag->name); - if (attrs & tm_tag_attr_type_t) - fprintf(fp, "%c%d", TA_TYPE, tag->type); - if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist)) - fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist); - if (attrs & tm_tag_attr_line_t) - fprintf(fp, "%c%ld", TA_LINE, tag->line); - if (attrs & tm_tag_attr_local_t) - fprintf(fp, "%c%d", TA_LOCAL, tag->local); - if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope)) - fprintf(fp, "%c%s", TA_SCOPE, tag->scope); - if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance)) - fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance); - if (attrs & tm_tag_attr_pointer_t) - fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder); - if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type)) - fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type); - if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access)) - fprintf(fp, "%c%c", TA_ACCESS, tag->access); - if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl)) - fprintf(fp, "%c%c", TA_IMPL, tag->impl); - - if (fprintf(fp, "\n")) - return TRUE; - else - return FALSE; -} - -/* Destroys a TMTag structure, i.e. frees all elements except the tag itself. @param tag The TMTag structure to destroy @see tm_tag_free() diff --git a/tagmanager/src/tm_tag.h b/tagmanager/src/tm_tag.h index 95982d5a5..a15e19a6e 100644 --- a/tagmanager/src/tm_tag.h +++ b/tagmanager/src/tm_tag.h @@ -110,13 +110,6 @@ typedef enum #define TAG_IMPL_VIRTUAL 'v' /**< Virtual implementation */ #define TAG_IMPL_UNKNOWN 'x' /**< Unknown implementation */ -/** - This structure holds all information about a tag, including the file - pseudo tag. It should always be created indirectly with one of the tag - creation functions such as tm_source_file_parse() or tm_tag_new_from_file(). - Once created, they can be sorted, deduped, etc. using functions such as - tm_tags_sort() or tm_tags_dedup() -*/ typedef struct _TMTag { char *name; /**< Name of tag */ @@ -140,26 +133,12 @@ typedef struct _TMTag #ifdef GEANY_PRIVATE -extern const TMTagType TM_GLOBAL_TYPE_MASK; - - -typedef enum { - TM_FILE_FORMAT_TAGMANAGER, - TM_FILE_FORMAT_PIPE, - TM_FILE_FORMAT_CTAGS -} TMFileFormat; - - /* The GType for a TMTag */ #define TM_TYPE_TAG (tm_tag_get_type()) GType tm_tag_get_type(void) G_GNUC_CONST; -TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry); - -TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format); - -gboolean tm_tag_write(TMTag *tag, FILE *file, guint attrs); +TMTag *tm_tag_new(void); void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array); diff --git a/tagmanager/src/tm_workspace.c b/tagmanager/src/tm_workspace.c index 7e3548eb3..14275a186 100644 --- a/tagmanager/src/tm_workspace.c +++ b/tagmanager/src/tm_workspace.c @@ -60,6 +60,10 @@ static TMTagType TM_TYPE_WITH_MEMBERS = tm_tag_class_t | tm_tag_struct_t | tm_tag_union_t | tm_tag_enum_t | tm_tag_interface_t; +static TMTagType TM_GLOBAL_TYPE_MASK = + tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t | + tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t; + static TMWorkspace *theWorkspace = NULL; @@ -342,54 +346,12 @@ void tm_workspace_remove_source_files(GPtrArray *source_files) */ gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode) { - guchar buf[BUFSIZ]; - FILE *fp; GPtrArray *file_tags, *new_tags; - TMTag *tag; - TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER; - if (NULL == (fp = g_fopen(tags_file, "r"))) + file_tags = tm_source_file_read_tags_file(tags_file, mode); + if (!file_tags) return FALSE; - if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf)) - { - fclose(fp); - return FALSE; /* early out on error */ - } - else - { /* We read the first line for the format specification. */ - if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL) - format = TM_FILE_FORMAT_PIPE; - else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL) - format = TM_FILE_FORMAT_TAGMANAGER; - else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL) - format = TM_FILE_FORMAT_CTAGS; - else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0) - format = TM_FILE_FORMAT_CTAGS; - else - { /* We didn't find a valid format specification, so we try to auto-detect the format - * by counting the pipe characters on the first line and asumme pipe format when - * we find more than one pipe on the line. */ - guint i, pipe_cnt = 0, tab_cnt = 0; - for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++) - { - if (buf[i] == '|') - pipe_cnt++; - else if (buf[i] == '\t') - tab_cnt++; - } - if (pipe_cnt > 1) - format = TM_FILE_FORMAT_PIPE; - else if (tab_cnt > 1) - format = TM_FILE_FORMAT_CTAGS; - } - rewind(fp); /* reset the file pointer, to start reading again from the beginning */ - } - - file_tags = g_ptr_array_new(); - while (NULL != (tag = tm_tag_new_from_file(NULL, fp, mode, format))) - g_ptr_array_add(file_tags, tag); - fclose(fp); - + tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE); /* reorder the whole array, because tm_tags_find expects a sorted array */ @@ -521,6 +483,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i GList *includes_files = NULL; gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp"); gchar *temp_file2 = create_temp_file("tmp_XXXXXX.cpp"); + gboolean ret; if (NULL == temp_file || NULL == temp_file2 || NULL == (fp = g_fopen(temp_file, "w"))) @@ -669,22 +632,10 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i tm_source_file_free(source_file); return FALSE; } - if (NULL == (fp = g_fopen(tags_file, "w"))) - { - tm_source_file_free(source_file); - return FALSE; - } - fprintf(fp, "# format=tagmanager\n"); - for (i = 0; i < tags_array->len; ++i) - { - tm_tag_write(TM_TAG(tags_array->pdata[i]), fp, tm_tag_attr_type_t - | tm_tag_attr_scope_t | tm_tag_attr_arglist_t | tm_tag_attr_vartype_t - | tm_tag_attr_pointer_t); - } - fclose(fp); + ret = tm_source_file_write_tags_file(tags_file, tags_array); tm_source_file_free(source_file); g_ptr_array_free(tags_array, TRUE); - return TRUE; + return ret; } -- 2.11.4.GIT