"Fossies" - the Fresh Open Source Software Archive

Member "geany-1.36/src/tagmanager/tm_source_file.c" (28 Sep 2019, 22624 Bytes) of package /linux/misc/geany-1.36.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tm_source_file.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.35_vs_1.36.

    1 /*
    2 *
    3 *   Copyright (c) 2001-2002, Biswapesh Chattopadhyay
    4 *   Copyright 2005 The Geany contributors
    5 *
    6 *   This source code is released for free distribution under the terms of the
    7 *   GNU General Public License.
    8 *
    9 */
   10 
   11 /**
   12  * @file tm_source_file.h
   13  The TMSourceFile structure and associated functions are used to maintain
   14  tags for individual files.
   15 */
   16 
   17 
   18 #include <stdio.h>
   19 #include <limits.h>
   20 #include <stdlib.h>
   21 #include <string.h>
   22 #include <ctype.h>
   23 #include <sys/stat.h>
   24 #include <glib/gstdio.h>
   25 #ifdef G_OS_WIN32
   26 # define VC_EXTRALEAN
   27 # define WIN32_LEAN_AND_MEAN
   28 # include <windows.h> /* for GetFullPathName */
   29 #endif
   30 
   31 #include "tm_source_file.h"
   32 #include "tm_tag.h"
   33 #include "tm_parser.h"
   34 #include "ctags-api.h"
   35 
   36 typedef struct
   37 {
   38     TMSourceFile public;
   39     guint refcount;
   40 } TMSourceFilePriv;
   41 
   42 
   43 typedef enum {
   44     TM_FILE_FORMAT_TAGMANAGER,
   45     TM_FILE_FORMAT_PIPE,
   46     TM_FILE_FORMAT_CTAGS
   47 } TMFileFormat;
   48 
   49 /* Note: To preserve binary compatibility, it is very important
   50     that you only *append* to this list ! */
   51 enum
   52 {
   53     TA_NAME = 200,
   54     TA_LINE,
   55     TA_LOCAL,
   56     TA_POS, /* Obsolete */
   57     TA_TYPE,
   58     TA_ARGLIST,
   59     TA_SCOPE,
   60     TA_VARTYPE,
   61     TA_INHERITS,
   62     TA_TIME,
   63     TA_ACCESS,
   64     TA_IMPL,
   65     TA_LANG,
   66     TA_INACTIVE, /* Obsolete */
   67     TA_POINTER
   68 };
   69 
   70 
   71 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
   72 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
   73 
   74 static int get_path_max(const char *path)
   75 {
   76 #ifdef PATH_MAX
   77     return PATH_MAX;
   78 #else
   79     int path_max = pathconf(path, _PC_PATH_MAX);
   80     if (path_max <= 0)
   81         path_max = 4096;
   82     return path_max;
   83 #endif
   84 }
   85 
   86 
   87 #if defined(G_OS_WIN32) && !defined(HAVE_REALPATH)
   88 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
   89  * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
   90  * with special chars within the filename */
   91 static char *realpath (const char *pathname, char *resolved_path)
   92 {
   93     int size;
   94 
   95     if (resolved_path != NULL)
   96     {
   97         int path_max = get_path_max(pathname);
   98         size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
   99         if (size > path_max)
  100             return NULL;
  101         else
  102             return resolved_path;
  103     }
  104     else
  105     {
  106         size = GetFullPathNameA (pathname, 0, NULL, NULL);
  107         resolved_path = g_new0 (char, size);
  108         GetFullPathNameA (pathname, size, resolved_path, NULL);
  109         return resolved_path;
  110     }
  111 }
  112 #endif
  113 
  114 /**
  115  Given a file name, returns a newly allocated string containing the realpath()
  116  of the file.
  117  @param file_name The original file_name
  118  @return A newly allocated string containing the real path to the file. NULL if none is available.
  119  @deprecated since 1.32 (ABI 235)
  120  @see utils_get_real_path()
  121 */
  122 GEANY_API_SYMBOL
  123 gchar *tm_get_real_path(const gchar *file_name)
  124 {
  125     if (file_name)
  126     {
  127         gsize len = get_path_max(file_name) + 1;
  128         gchar *path = g_malloc0(len);
  129 
  130         if (realpath(file_name, path))
  131             return path;
  132         else
  133             g_free(path);
  134     }
  135     return NULL;
  136 }
  137 
  138 static char get_tag_impl(const char *impl)
  139 {
  140     if ((0 == strcmp("virtual", impl))
  141      || (0 == strcmp("pure virtual", impl)))
  142         return TAG_IMPL_VIRTUAL;
  143 
  144 #ifdef TM_DEBUG
  145         g_warning("Unknown implementation %s", impl);
  146 #endif
  147     return TAG_IMPL_UNKNOWN;
  148 }
  149 
  150 static char get_tag_access(const char *access)
  151 {
  152     if (0 == strcmp("public", access))
  153         return TAG_ACCESS_PUBLIC;
  154     else if (0 == strcmp("protected", access))
  155         return TAG_ACCESS_PROTECTED;
  156     else if (0 == strcmp("private", access))
  157         return TAG_ACCESS_PRIVATE;
  158     else if (0 == strcmp("friend", access))
  159         return TAG_ACCESS_FRIEND;
  160     else if (0 == strcmp("default", access))
  161         return TAG_ACCESS_DEFAULT;
  162 
  163 #ifdef TM_DEBUG
  164     g_warning("Unknown access type %s", access);
  165 #endif
  166     return TAG_ACCESS_UNKNOWN;
  167 }
  168 
  169 /*
  170  Initializes a TMTag structure with information from a ctagsTag struct
  171  used by the ctags parsers. Note that the TMTag structure must be malloc()ed
  172  before calling this function.
  173  @param tag The TMTag structure to initialize
  174  @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
  175  @param tag_entry Tag information gathered by the ctags parser
  176  @return TRUE on success, FALSE on failure
  177 */
  178 static gboolean init_tag(TMTag *tag, TMSourceFile *file, const ctagsTag *tag_entry)
  179 {
  180     TMTagType type;
  181 
  182     if (!tag_entry)
  183         return FALSE;
  184 
  185     type = tm_parser_get_tag_type(tag_entry->kindLetter, tag_entry->lang);
  186     if (file->lang != tag_entry->lang)  /* this is a tag from a subparser */
  187     {
  188         /* check for possible re-definition of subparser type */
  189         type = tm_parser_get_subparser_type(file->lang, tag_entry->lang, type);
  190     }
  191 
  192     if (!tag_entry->name || type == tm_tag_undef_t)
  193         return FALSE;
  194 
  195     tag->name = g_strdup(tag_entry->name);
  196     tag->type = type;
  197     tag->local = tag_entry->isFileScope;
  198     tag->pointerOrder = 0;  /* backward compatibility (use var_type instead) */
  199     tag->line = tag_entry->lineNumber;
  200     if (NULL != tag_entry->signature)
  201         tag->arglist = g_strdup(tag_entry->signature);
  202     if ((NULL != tag_entry->scopeName) &&
  203         (0 != tag_entry->scopeName[0]))
  204         tag->scope = g_strdup(tag_entry->scopeName);
  205     if (tag_entry->inheritance != NULL)
  206         tag->inheritance = g_strdup(tag_entry->inheritance);
  207     if (tag_entry->varType != NULL)
  208         tag->var_type = g_strdup(tag_entry->varType);
  209     if (tag_entry->access != NULL)
  210         tag->access = get_tag_access(tag_entry->access);
  211     if (tag_entry->implementation != NULL)
  212         tag->impl = get_tag_impl(tag_entry->implementation);
  213     if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
  214         tag->type = tm_tag_macro_with_arg_t;
  215     tag->file = file;
  216     /* redefine lang also for subparsers because the rest of Geany assumes that
  217      * tags from a single file are from a single language */
  218     tag->lang = file->lang;
  219     return TRUE;
  220 }
  221 
  222 /*
  223  Initializes an already malloc()ed TMTag structure by reading a tag entry
  224  line from a file. The structure should be allocated beforehand.
  225  @param tag The TMTag structure to populate
  226  @param file The TMSourceFile struct (assigned to the file member)
  227  @param fp FILE pointer from where the tag line is read
  228  @return TRUE on success, FALSE on FAILURE
  229 */
  230 static gboolean init_tag_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
  231 {
  232     guchar buf[BUFSIZ];
  233     guchar *start, *end;
  234     gboolean status;
  235     guchar changed_char = TA_NAME;
  236 
  237     tag->refcount = 1;
  238     if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
  239         return FALSE;
  240     for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
  241     {
  242         while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
  243             ++ end;
  244         if (('\0' == *end) || ('\n' == *end))
  245             status = FALSE;
  246         changed_char = *end;
  247         *end = '\0';
  248         if (NULL == tag->name)
  249         {
  250             if (!isprint(*start))
  251                 return FALSE;
  252             else
  253                 tag->name = g_strdup((gchar*)start);
  254         }
  255         else
  256         {
  257             switch (*start)
  258             {
  259                 case TA_LINE:
  260                     tag->line = atol((gchar*)start + 1);
  261                     break;
  262                 case TA_LOCAL:
  263                     tag->local = atoi((gchar*)start + 1);
  264                     break;
  265                 case TA_TYPE:
  266                     tag->type = (TMTagType) atoi((gchar*)start + 1);
  267                     break;
  268                 case TA_ARGLIST:
  269                     tag->arglist = g_strdup((gchar*)start + 1);
  270                     break;
  271                 case TA_SCOPE:
  272                     tag->scope = g_strdup((gchar*)start + 1);
  273                     break;
  274                 case TA_POINTER:
  275                     tag->pointerOrder = atoi((gchar*)start + 1);
  276                     break;
  277                 case TA_VARTYPE:
  278                     tag->var_type = g_strdup((gchar*)start + 1);
  279                     break;
  280                 case TA_INHERITS:
  281                     tag->inheritance = g_strdup((gchar*)start + 1);
  282                     break;
  283                 case TA_TIME:  /* Obsolete */
  284                     break;
  285                 case TA_LANG:  /* Obsolete */
  286                     break;
  287                 case TA_INACTIVE:  /* Obsolete */
  288                     break;
  289                 case TA_ACCESS:
  290                     tag->access = (char) *(start + 1);
  291                     break;
  292                 case TA_IMPL:
  293                     tag->impl = (char) *(start + 1);
  294                     break;
  295                 default:
  296 #ifdef GEANY_DEBUG
  297                     g_warning("Unknown attribute %s", start + 1);
  298 #endif
  299                     break;
  300             }
  301         }
  302         *end = changed_char;
  303     }
  304     if (NULL == tag->name)
  305         return FALSE;
  306     tag->file = file;
  307     return TRUE;
  308 }
  309 
  310 /* alternative parser for Pascal and LaTeX global tags files with the following format
  311  * tagname|return value|arglist|description\n */
  312 static gboolean init_tag_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
  313 {
  314     guchar buf[BUFSIZ];
  315     guchar *start, *end;
  316     gboolean status;
  317     /*guchar changed_char = TA_NAME;*/
  318 
  319     tag->refcount = 1;
  320     if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
  321         return FALSE;
  322     {
  323         gchar **fields;
  324         guint field_len;
  325         for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
  326         {
  327             while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
  328                 ++ end;
  329             if (('\0' == *end) || ('\n' == *end))
  330                 status = FALSE;
  331             /*changed_char = *end;*/
  332             *end = '\0';
  333             if (NULL == tag->name && !isprint(*start))
  334                     return FALSE;
  335 
  336             fields = g_strsplit((gchar*)start, "|", -1);
  337             field_len = g_strv_length(fields);
  338 
  339             if (field_len >= 1) tag->name = g_strdup(fields[0]);
  340             else tag->name = NULL;
  341             if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
  342             if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
  343             tag->type = tm_tag_prototype_t;
  344             g_strfreev(fields);
  345         }
  346     }
  347 
  348     if (NULL == tag->name)
  349         return FALSE;
  350     tag->file = file;
  351     return TRUE;
  352 }
  353 
  354 /*
  355  CTags tag file format (http://ctags.sourceforge.net/FORMAT)
  356 */
  357 static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp, TMParserType lang)
  358 {
  359     gchar buf[BUFSIZ];
  360     gchar *p, *tab;
  361 
  362     tag->refcount = 1;
  363     tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
  364     do
  365     {
  366         if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
  367             return FALSE;
  368     }
  369     while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
  370 
  371     p = buf;
  372 
  373     /* tag name */
  374     if (! (tab = strchr(p, '\t')) || p == tab)
  375         return FALSE;
  376     tag->name = g_strndup(p, (gsize)(tab - p));
  377     p = tab + 1;
  378 
  379     /* tagfile, unused */
  380     if (! (tab = strchr(p, '\t')))
  381     {
  382         g_free(tag->name);
  383         tag->name = NULL;
  384         return FALSE;
  385     }
  386     p = tab + 1;
  387     /* Ex command, unused */
  388     if (*p == '/' || *p == '?')
  389     {
  390         gchar c = *p;
  391         for (++p; *p && *p != c; p++)
  392         {
  393             if (*p == '\\' && p[1])
  394                 p++;
  395         }
  396     }
  397     else /* assume a line */
  398         tag->line = atol(p);
  399     tab = strstr(p, ";\"");
  400     /* read extension fields */
  401     if (tab)
  402     {
  403         p = tab + 2;
  404         while (*p && *p != '\n' && *p != '\r')
  405         {
  406             gchar *end;
  407             const gchar *key, *value = NULL;
  408 
  409             /* skip leading tabulations */
  410             while (*p && *p == '\t') p++;
  411             /* find the separator (:) and end (\t) */
  412             key = end = p;
  413             while (*end && *end != '\t' && *end != '\n' && *end != '\r')
  414             {
  415                 if (*end == ':' && ! value)
  416                 {
  417                     *end = 0; /* terminate the key */
  418                     value = end + 1;
  419                 }
  420                 end++;
  421             }
  422             /* move p paste the so we won't stop parsing by setting *end=0 below */
  423             p = *end ? end + 1 : end;
  424             *end = 0; /* terminate the value (or key if no value) */
  425 
  426             if (! value || 0 == strcmp(key, "kind")) /* tag kind */
  427             {
  428                 const gchar *kind = value ? value : key;
  429 
  430                 if (kind[0] && kind[1])
  431                     tag->type = tm_parser_get_tag_type(ctagsGetKindFromName(kind, lang), lang);
  432                 else
  433                     tag->type = tm_parser_get_tag_type(*kind, lang);
  434             }
  435             else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
  436             {
  437                 g_free(tag->inheritance);
  438                 tag->inheritance = g_strdup(value);
  439             }
  440             else if (0 == strcmp(key, "implementation")) /* implementation limit */
  441                 tag->impl = get_tag_impl(value);
  442             else if (0 == strcmp(key, "line")) /* line */
  443                 tag->line = atol(value);
  444             else if (0 == strcmp(key, "access")) /* access */
  445                 tag->access = get_tag_access(value);
  446             else if (0 == strcmp(key, "class") ||
  447                      0 == strcmp(key, "enum") ||
  448                      0 == strcmp(key, "function") ||
  449                      0 == strcmp(key, "struct") ||
  450                      0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
  451             {
  452                 g_free(tag->scope);
  453                 tag->scope = g_strdup(value);
  454             }
  455             else if (0 == strcmp(key, "file")) /* static (local) tag */
  456                 tag->local = TRUE;
  457             else if (0 == strcmp(key, "signature")) /* arglist */
  458             {
  459                 g_free(tag->arglist);
  460                 tag->arglist = g_strdup(value);
  461             }
  462         }
  463     }
  464 
  465     tag->file = file;
  466     return TRUE;
  467 }
  468 
  469 static TMTag *new_tag_from_tags_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format)
  470 {
  471     TMTag *tag = tm_tag_new();
  472     gboolean result = FALSE;
  473 
  474     switch (format)
  475     {
  476         case TM_FILE_FORMAT_TAGMANAGER:
  477             result = init_tag_from_file(tag, file, fp);
  478             break;
  479         case TM_FILE_FORMAT_PIPE:
  480             result = init_tag_from_file_alt(tag, file, fp);
  481             break;
  482         case TM_FILE_FORMAT_CTAGS:
  483             result = init_tag_from_file_ctags(tag, file, fp, mode);
  484             break;
  485     }
  486 
  487     if (! result)
  488     {
  489         tm_tag_unref(tag);
  490         return NULL;
  491     }
  492     tag->lang = mode;
  493     return tag;
  494 }
  495 
  496 /*
  497  Writes tag information to the given FILE *.
  498  @param tag The tag information to write.
  499  @param file FILE pointer to which the tag information is written.
  500  @param attrs Attributes to be written (bitmask).
  501  @return TRUE on success, FALSE on failure.
  502 */
  503 static gboolean write_tag(TMTag *tag, FILE *fp, TMTagAttrType attrs)
  504 {
  505     fprintf(fp, "%s", tag->name);
  506     if (attrs & tm_tag_attr_type_t)
  507         fprintf(fp, "%c%d", TA_TYPE, tag->type);
  508     if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
  509         fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
  510     if (attrs & tm_tag_attr_line_t)
  511         fprintf(fp, "%c%ld", TA_LINE, tag->line);
  512     if (attrs & tm_tag_attr_local_t)
  513         fprintf(fp, "%c%d", TA_LOCAL, tag->local);
  514     if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
  515         fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
  516     if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
  517         fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
  518     if (attrs & tm_tag_attr_pointer_t)
  519         fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
  520     if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
  521         fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
  522     if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
  523         fprintf(fp, "%c%c", TA_ACCESS, tag->access);
  524     if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
  525         fprintf(fp, "%c%c", TA_IMPL, tag->impl);
  526 
  527     if (fprintf(fp, "\n"))
  528         return TRUE;
  529     else
  530         return FALSE;
  531 }
  532 
  533 GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode)
  534 {
  535     guchar buf[BUFSIZ];
  536     FILE *fp;
  537     GPtrArray *file_tags;
  538     TMTag *tag;
  539     TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
  540 
  541     if (NULL == (fp = g_fopen(tags_file, "r")))
  542         return NULL;
  543     if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf))
  544     {
  545         fclose(fp);
  546         return NULL; /* early out on error */
  547     }
  548     else
  549     {   /* We read (and discard) the first line for the format specification. */
  550         if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL)
  551             format = TM_FILE_FORMAT_PIPE;
  552         else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL)
  553             format = TM_FILE_FORMAT_TAGMANAGER;
  554         else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL)
  555             format = TM_FILE_FORMAT_CTAGS;
  556         else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0)
  557             format = TM_FILE_FORMAT_CTAGS;
  558         else
  559         {   /* We didn't find a valid format specification, so we try to auto-detect the format
  560              * by counting the pipe characters on the first line and asumme pipe format when
  561              * we find more than one pipe on the line. */
  562             guint i, pipe_cnt = 0, tab_cnt = 0;
  563             for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++)
  564             {
  565                 if (buf[i] == '|')
  566                     pipe_cnt++;
  567                 else if (buf[i] == '\t')
  568                     tab_cnt++;
  569             }
  570             if (pipe_cnt > 1)
  571                 format = TM_FILE_FORMAT_PIPE;
  572             else if (tab_cnt > 1)
  573                 format = TM_FILE_FORMAT_CTAGS;
  574             /* reset the file pointer, to start reading again from the beginning */
  575             rewind(fp);
  576         }
  577     }
  578 
  579     file_tags = g_ptr_array_new();
  580     while (NULL != (tag = new_tag_from_tags_file(NULL, fp, mode, format)))
  581         g_ptr_array_add(file_tags, tag);
  582     fclose(fp);
  583 
  584     return file_tags;
  585 }
  586 
  587 gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array)
  588 {
  589     guint i;
  590     FILE *fp;
  591     gboolean ret = TRUE;
  592 
  593     g_return_val_if_fail(tags_array && tags_file, FALSE);
  594 
  595     fp = g_fopen(tags_file, "w");
  596     if (!fp)
  597         return FALSE;
  598 
  599     fprintf(fp, "# format=tagmanager\n");
  600     for (i = 0; i < tags_array->len; i++)
  601     {
  602         TMTag *tag = TM_TAG(tags_array->pdata[i]);
  603 
  604         ret = write_tag(tag, fp, tm_tag_attr_type_t
  605           | tm_tag_attr_scope_t | tm_tag_attr_arglist_t | tm_tag_attr_vartype_t
  606           | tm_tag_attr_pointer_t);
  607 
  608         if (!ret)
  609             break;
  610     }
  611     fclose(fp);
  612 
  613     return ret;
  614 }
  615 
  616 /* add argument list of __init__() Python methods to the class tag */
  617 static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file)
  618 {
  619     guint i;
  620     const char *parent_tag_name;
  621 
  622     if (tag->type != tm_tag_method_t || tag->scope == NULL ||
  623         g_strcmp0(tag->name, "__init__") != 0)
  624         return;
  625 
  626     parent_tag_name = strrchr(tag->scope, '.');
  627     if (parent_tag_name)
  628         parent_tag_name++;
  629     else
  630         parent_tag_name = tag->scope;
  631 
  632     /* going in reverse order because the tag was added recently */
  633     for (i = current_source_file->tags_array->len; i > 0; i--)
  634     {
  635         TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
  636         if (g_strcmp0(prev_tag->name, parent_tag_name) == 0)
  637         {
  638             g_free(prev_tag->arglist);
  639             prev_tag->arglist = g_strdup(tag->arglist);
  640             break;
  641         }
  642     }
  643 }
  644 
  645 /* new parsing pass ctags callback function */
  646 static bool ctags_pass_start(void *user_data)
  647 {
  648     TMSourceFile *current_source_file = user_data;
  649 
  650     tm_tags_array_free(current_source_file->tags_array, FALSE);
  651     return TRUE;
  652 }
  653 
  654 /* new tag ctags callback function */
  655 static bool ctags_new_tag(const ctagsTag *const tag,
  656     void *user_data)
  657 {
  658     TMSourceFile *current_source_file = user_data;
  659     TMTag *tm_tag = tm_tag_new();
  660 
  661     if (!init_tag(tm_tag, current_source_file, tag))
  662     {
  663         tm_tag_unref(tm_tag);
  664         return TRUE;
  665     }
  666 
  667     if (tm_tag->lang == TM_PARSER_PYTHON)
  668         update_python_arglist(tm_tag, current_source_file);
  669 
  670     g_ptr_array_add(current_source_file->tags_array, tm_tag);
  671 
  672     return TRUE;
  673 }
  674 
  675 /* Initializes a TMSourceFile structure from a file name. */
  676 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
  677     const char* name)
  678 {
  679     GStatBuf s;
  680     int status;
  681 
  682 #ifdef TM_DEBUG
  683     g_message("Source File init: %s", file_name);
  684 #endif
  685 
  686     if (file_name != NULL)
  687     {
  688         status = g_stat(file_name, &s);
  689         if (0 != status)
  690         {
  691             /* g_warning("Unable to stat %s", file_name);*/
  692             return FALSE;
  693         }
  694         if (!S_ISREG(s.st_mode))
  695         {
  696             g_warning("%s: Not a regular file", file_name);
  697             return FALSE;
  698         }
  699         source_file->file_name = tm_get_real_path(file_name);
  700         source_file->short_name = strrchr(source_file->file_name, '/');
  701         if (source_file->short_name)
  702             ++ source_file->short_name;
  703         else
  704             source_file->short_name = source_file->file_name;
  705     }
  706 
  707     source_file->tags_array = g_ptr_array_new();
  708 
  709     if (name == NULL)
  710         source_file->lang = TM_PARSER_NONE;
  711     else
  712         source_file->lang = ctagsGetNamedLang(name);
  713 
  714     return TRUE;
  715 }
  716 
  717 /** Initializes a TMSourceFile structure and returns a pointer to it. The
  718  * TMSourceFile has to be added to TMWorkspace to start its parsing.
  719  * @param file_name The file name.
  720  * @param name Name of the used programming language, NULL to disable parsing.
  721  * @return The created unparsed TMSourceFile object.
  722  * */
  723 GEANY_API_SYMBOL
  724 TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
  725 {
  726     TMSourceFilePriv *priv;
  727 
  728     SOURCE_FILE_NEW(priv);
  729     if (TRUE != tm_source_file_init(&priv->public, file_name, name))
  730     {
  731         SOURCE_FILE_FREE(priv);
  732         return NULL;
  733     }
  734     priv->refcount = 1;
  735     return &priv->public;
  736 }
  737 
  738 
  739 static TMSourceFile *tm_source_file_dup(TMSourceFile *source_file)
  740 {
  741     TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
  742 
  743     g_return_val_if_fail(NULL != source_file, NULL);
  744 
  745     g_atomic_int_inc(&priv->refcount);
  746     return source_file;
  747 }
  748 
  749 /* Destroys the contents of the source file. Note that the tags are owned by the
  750  source file and are also destroyed when the source file is destroyed. If pointers
  751  to these tags are used elsewhere, then those tag arrays should be rebuilt.
  752 */
  753 static void tm_source_file_destroy(TMSourceFile *source_file)
  754 {
  755 #ifdef TM_DEBUG
  756     g_message("Destroying source file: %s", source_file->file_name);
  757 #endif
  758 
  759     g_free(source_file->file_name);
  760     tm_tags_array_free(source_file->tags_array, TRUE);
  761     source_file->tags_array = NULL;
  762 }
  763 
  764 /** Decrements the reference count of @a source_file
  765  *
  766  * If the reference count drops to 0, then @a source_file is freed, including all contents.
  767  * Make sure the @a source_file is already removed from any TMWorkSpace before the
  768  * this happens.
  769  * @param source_file The source file to free.
  770  * @see tm_workspace_remove_source_file()
  771 */
  772 GEANY_API_SYMBOL
  773 void tm_source_file_free(TMSourceFile *source_file)
  774 {
  775     TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
  776 
  777     if (NULL != priv && g_atomic_int_dec_and_test(&priv->refcount))
  778     {
  779         tm_source_file_destroy(source_file);
  780         SOURCE_FILE_FREE(priv);
  781     }
  782 }
  783 
  784 /** Gets the GBoxed-derived GType for TMSourceFile
  785  *
  786  * @return TMSourceFile type . */
  787 GEANY_API_SYMBOL
  788 GType tm_source_file_get_type(void);
  789 
  790 G_DEFINE_BOXED_TYPE(TMSourceFile, tm_source_file, tm_source_file_dup, tm_source_file_free);
  791 
  792 /* Parses the text-buffer or source file and regenarates the tags.
  793  @param source_file The source file to parse
  794  @param text_buf The text buffer to parse
  795  @param buf_size The size of text_buf.
  796  @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
  797  TRUE to parse the buffer and ignore the file content.
  798  @return TRUE on success, FALSE on failure
  799 */
  800 gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
  801     gboolean use_buffer)
  802 {
  803     const char *file_name;
  804     gboolean retry = TRUE;
  805 
  806     if ((NULL == source_file) || (NULL == source_file->file_name))
  807     {
  808         g_warning("Attempt to parse NULL file");
  809         return FALSE;
  810     }
  811 
  812     if (source_file->lang == TM_PARSER_NONE)
  813     {
  814         tm_tags_array_free(source_file->tags_array, FALSE);
  815         return FALSE;
  816     }
  817 
  818     file_name = source_file->file_name;
  819 
  820     if (use_buffer && (NULL == text_buf || 0 == buf_size))
  821     {
  822         /* Empty buffer, "parse" by setting empty tag array */
  823         tm_tags_array_free(source_file->tags_array, FALSE);
  824         return TRUE;
  825     }
  826 
  827     tm_tags_array_free(source_file->tags_array, FALSE);
  828 
  829     ctagsParse(use_buffer ? text_buf : NULL, buf_size, file_name,
  830         source_file->lang, ctags_new_tag, ctags_pass_start, source_file);
  831 
  832     return !retry;
  833 }
  834 
  835 /* Gets the name associated with the language index.
  836  @param lang The language index.
  837  @return The language name, or NULL.
  838 */
  839 const gchar *tm_source_file_get_lang_name(TMParserType lang)
  840 {
  841     return ctagsGetLangName(lang);
  842 }
  843 
  844 /* Gets the language index for \a name.
  845  @param name The language name.
  846  @return The language index, or TM_PARSER_NONE.
  847 */
  848 TMParserType tm_source_file_get_named_lang(const gchar *name)
  849 {
  850     return ctagsGetNamedLang(name);
  851 }