Remove not useless tm_source_file_ctags_init() indirection
[geany-mirror.git] / tagmanager / src / tm_source_file.c
blobc8efa18a5afb09b7983dfade6b1ba1fbe0de5517
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 /**
11 * @file tm_source_file.h
12 The TMSourceFile structure and associated functions are used to maintain
13 tags for individual files.
17 #include <stdio.h>
18 #include <limits.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <sys/stat.h>
23 #include <glib/gstdio.h>
24 #ifdef G_OS_WIN32
25 # define VC_EXTRALEAN
26 # define WIN32_LEAN_AND_MEAN
27 # include <windows.h> /* for GetFullPathName */
28 #endif
30 #include "tm_source_file.h"
31 #include "tm_tag.h"
32 #include "tm_parser.h"
33 #include "tm_ctags_wrappers.h"
35 typedef struct
37 TMSourceFile public;
38 guint refcount;
39 } TMSourceFilePriv;
42 typedef enum {
43 TM_FILE_FORMAT_TAGMANAGER,
44 TM_FILE_FORMAT_PIPE,
45 TM_FILE_FORMAT_CTAGS
46 } TMFileFormat;
48 /* Note: To preserve binary compatibility, it is very important
49 that you only *append* to this list ! */
50 enum
52 TA_NAME = 200,
53 TA_LINE,
54 TA_LOCAL,
55 TA_POS, /* Obsolete */
56 TA_TYPE,
57 TA_ARGLIST,
58 TA_SCOPE,
59 TA_VARTYPE,
60 TA_INHERITS,
61 TA_TIME,
62 TA_ACCESS,
63 TA_IMPL,
64 TA_LANG,
65 TA_INACTIVE, /* Obsolete */
66 TA_POINTER
70 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
71 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
73 static int get_path_max(const char *path)
75 #ifdef PATH_MAX
76 return PATH_MAX;
77 #else
78 int path_max = pathconf(path, _PC_PATH_MAX);
79 if (path_max <= 0)
80 path_max = 4096;
81 return path_max;
82 #endif
86 #ifdef G_OS_WIN32
87 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
88 * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
89 * with special chars within the filename */
90 static char *realpath (const char *pathname, char *resolved_path)
92 int size;
94 if (resolved_path != NULL)
96 int path_max = get_path_max(pathname);
97 size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
98 if (size > path_max)
99 return NULL;
100 else
101 return resolved_path;
103 else
105 size = GetFullPathNameA (pathname, 0, NULL, NULL);
106 resolved_path = g_new0 (char, size);
107 GetFullPathNameA (pathname, size, resolved_path, NULL);
108 return resolved_path;
111 #endif
114 Given a file name, returns a newly allocated string containing the realpath()
115 of the file.
116 @param file_name The original file_name
117 @return A newly allocated string containing the real path to the file. NULL if none is available.
119 GEANY_API_SYMBOL
120 gchar *tm_get_real_path(const gchar *file_name)
122 if (file_name)
124 gsize len = get_path_max(file_name) + 1;
125 gchar *path = g_malloc0(len);
127 if (realpath(file_name, path))
128 return path;
129 else
130 g_free(path);
132 return NULL;
135 static char get_tag_impl(const char *impl)
137 if ((0 == strcmp("virtual", impl))
138 || (0 == strcmp("pure virtual", impl)))
139 return TAG_IMPL_VIRTUAL;
141 #ifdef TM_DEBUG
142 g_warning("Unknown implementation %s", impl);
143 #endif
144 return TAG_IMPL_UNKNOWN;
147 static char get_tag_access(const char *access)
149 if (0 == strcmp("public", access))
150 return TAG_ACCESS_PUBLIC;
151 else if (0 == strcmp("protected", access))
152 return TAG_ACCESS_PROTECTED;
153 else if (0 == strcmp("private", access))
154 return TAG_ACCESS_PRIVATE;
155 else if (0 == strcmp("friend", access))
156 return TAG_ACCESS_FRIEND;
157 else if (0 == strcmp("default", access))
158 return TAG_ACCESS_DEFAULT;
160 #ifdef TM_DEBUG
161 g_warning("Unknown access type %s", access);
162 #endif
163 return TAG_ACCESS_UNKNOWN;
167 Initializes a TMTag structure with information from a tagEntryInfo struct
168 used by the ctags parsers. Note that the TMTag structure must be malloc()ed
169 before calling this function. This function is called by tm_tag_new() - you
170 should not need to call this directly.
171 @param tag The TMTag structure to initialize
172 @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
173 @param tag_entry Tag information gathered by the ctags parser
174 @return TRUE on success, FALSE on failure
176 static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
178 TMTagType type;
180 if (!tag_entry)
181 return FALSE;
183 type = tm_parser_get_tag_type(tag_entry->kind, file->lang);
184 if (!tag_entry->name || type == tm_tag_undef_t)
185 return FALSE;
187 tag->name = g_strdup(tag_entry->name);
188 tag->type = type;
189 tag->local = tag_entry->isFileScope;
190 tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */
191 tag->line = tag_entry->lineNumber;
192 if (NULL != tag_entry->extensionFields.arglist)
193 tag->arglist = g_strdup(tag_entry->extensionFields.arglist);
194 if ((NULL != tag_entry->extensionFields.scope[1]) &&
195 (0 != tag_entry->extensionFields.scope[1][0]))
196 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
197 if (tag_entry->extensionFields.inheritance != NULL)
198 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
199 if (tag_entry->extensionFields.varType != NULL)
200 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
201 if (tag_entry->extensionFields.access != NULL)
202 tag->access = get_tag_access(tag_entry->extensionFields.access);
203 if (tag_entry->extensionFields.implementation != NULL)
204 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
205 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
206 tag->type = tm_tag_macro_with_arg_t;
207 tag->file = file;
208 tag->lang = file->lang;
209 return TRUE;
213 Initializes an already malloc()ed TMTag structure by reading a tag entry
214 line from a file. The structure should be allocated beforehand.
215 @param tag The TMTag structure to populate
216 @param file The TMSourceFile struct (assigned to the file member)
217 @param fp FILE pointer from where the tag line is read
218 @return TRUE on success, FALSE on FAILURE
220 static gboolean init_tag_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
222 guchar buf[BUFSIZ];
223 guchar *start, *end;
224 gboolean status;
225 guchar changed_char = TA_NAME;
227 tag->refcount = 1;
228 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
229 return FALSE;
230 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
232 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
233 ++ end;
234 if (('\0' == *end) || ('\n' == *end))
235 status = FALSE;
236 changed_char = *end;
237 *end = '\0';
238 if (NULL == tag->name)
240 if (!isprint(*start))
241 return FALSE;
242 else
243 tag->name = g_strdup((gchar*)start);
245 else
247 switch (*start)
249 case TA_LINE:
250 tag->line = atol((gchar*)start + 1);
251 break;
252 case TA_LOCAL:
253 tag->local = atoi((gchar*)start + 1);
254 break;
255 case TA_TYPE:
256 tag->type = (TMTagType) atoi((gchar*)start + 1);
257 break;
258 case TA_ARGLIST:
259 tag->arglist = g_strdup((gchar*)start + 1);
260 break;
261 case TA_SCOPE:
262 tag->scope = g_strdup((gchar*)start + 1);
263 break;
264 case TA_POINTER:
265 tag->pointerOrder = atoi((gchar*)start + 1);
266 break;
267 case TA_VARTYPE:
268 tag->var_type = g_strdup((gchar*)start + 1);
269 break;
270 case TA_INHERITS:
271 tag->inheritance = g_strdup((gchar*)start + 1);
272 break;
273 case TA_TIME: /* Obsolete */
274 break;
275 case TA_LANG: /* Obsolete */
276 break;
277 case TA_INACTIVE: /* Obsolete */
278 break;
279 case TA_ACCESS:
280 tag->access = (char) *(start + 1);
281 break;
282 case TA_IMPL:
283 tag->impl = (char) *(start + 1);
284 break;
285 default:
286 #ifdef GEANY_DEBUG
287 g_warning("Unknown attribute %s", start + 1);
288 #endif
289 break;
292 *end = changed_char;
294 if (NULL == tag->name)
295 return FALSE;
296 tag->file = file;
297 return TRUE;
300 /* alternative parser for Pascal and LaTeX global tags files with the following format
301 * tagname|return value|arglist|description\n */
302 static gboolean init_tag_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
304 guchar buf[BUFSIZ];
305 guchar *start, *end;
306 gboolean status;
307 /*guchar changed_char = TA_NAME;*/
309 tag->refcount = 1;
310 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
311 return FALSE;
313 gchar **fields;
314 guint field_len;
315 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
317 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
318 ++ end;
319 if (('\0' == *end) || ('\n' == *end))
320 status = FALSE;
321 /*changed_char = *end;*/
322 *end = '\0';
323 if (NULL == tag->name && !isprint(*start))
324 return FALSE;
326 fields = g_strsplit((gchar*)start, "|", -1);
327 field_len = g_strv_length(fields);
329 if (field_len >= 1) tag->name = g_strdup(fields[0]);
330 else tag->name = NULL;
331 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
332 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
333 tag->type = tm_tag_prototype_t;
334 g_strfreev(fields);
338 if (NULL == tag->name)
339 return FALSE;
340 tag->file = file;
341 return TRUE;
345 CTags tag file format (http://ctags.sourceforge.net/FORMAT)
347 static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp, TMParserType lang)
349 gchar buf[BUFSIZ];
350 gchar *p, *tab;
352 tag->refcount = 1;
353 tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
356 if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
357 return FALSE;
359 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
361 p = buf;
363 /* tag name */
364 if (! (tab = strchr(p, '\t')) || p == tab)
365 return FALSE;
366 tag->name = g_strndup(p, (gsize)(tab - p));
367 p = tab + 1;
369 /* tagfile, unused */
370 if (! (tab = strchr(p, '\t')))
372 g_free(tag->name);
373 tag->name = NULL;
374 return FALSE;
376 p = tab + 1;
377 /* Ex command, unused */
378 if (*p == '/' || *p == '?')
380 gchar c = *p;
381 for (++p; *p && *p != c; p++)
383 if (*p == '\\' && p[1])
384 p++;
387 else /* assume a line */
388 tag->line = atol(p);
389 tab = strstr(p, ";\"");
390 /* read extension fields */
391 if (tab)
393 p = tab + 2;
394 while (*p && *p != '\n' && *p != '\r')
396 gchar *end;
397 const gchar *key, *value = NULL;
399 /* skip leading tabulations */
400 while (*p && *p == '\t') p++;
401 /* find the separator (:) and end (\t) */
402 key = end = p;
403 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
405 if (*end == ':' && ! value)
407 *end = 0; /* terminate the key */
408 value = end + 1;
410 end++;
412 /* move p paste the so we won't stop parsing by setting *end=0 below */
413 p = *end ? end + 1 : end;
414 *end = 0; /* terminate the value (or key if no value) */
416 if (! value || 0 == strcmp(key, "kind")) /* tag kind */
418 const gchar *kind = value ? value : key;
420 if (kind[0] && kind[1])
421 tag->type = tm_parser_get_tag_type(tm_ctags_get_kind_from_name(kind, lang), lang);
422 else
423 tag->type = tm_parser_get_tag_type(*kind, lang);
425 else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
427 g_free(tag->inheritance);
428 tag->inheritance = g_strdup(value);
430 else if (0 == strcmp(key, "implementation")) /* implementation limit */
431 tag->impl = get_tag_impl(value);
432 else if (0 == strcmp(key, "line")) /* line */
433 tag->line = atol(value);
434 else if (0 == strcmp(key, "access")) /* access */
435 tag->access = get_tag_access(value);
436 else if (0 == strcmp(key, "class") ||
437 0 == strcmp(key, "enum") ||
438 0 == strcmp(key, "function") ||
439 0 == strcmp(key, "struct") ||
440 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
442 g_free(tag->scope);
443 tag->scope = g_strdup(value);
445 else if (0 == strcmp(key, "file")) /* static (local) tag */
446 tag->local = TRUE;
447 else if (0 == strcmp(key, "signature")) /* arglist */
449 g_free(tag->arglist);
450 tag->arglist = g_strdup(value);
455 tag->file = file;
456 return TRUE;
459 static TMTag *new_tag_from_tags_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format)
461 TMTag *tag = tm_tag_new();
462 gboolean result = FALSE;
464 switch (format)
466 case TM_FILE_FORMAT_TAGMANAGER:
467 result = init_tag_from_file(tag, file, fp);
468 break;
469 case TM_FILE_FORMAT_PIPE:
470 result = init_tag_from_file_alt(tag, file, fp);
471 break;
472 case TM_FILE_FORMAT_CTAGS:
473 result = init_tag_from_file_ctags(tag, file, fp, mode);
474 break;
477 if (! result)
479 tm_tag_unref(tag);
480 return NULL;
482 tag->lang = mode;
483 return tag;
487 Writes tag information to the given FILE *.
488 @param tag The tag information to write.
489 @param file FILE pointer to which the tag information is written.
490 @param attrs Attributes to be written (bitmask).
491 @return TRUE on success, FALSE on failure.
493 static gboolean write_tag(TMTag *tag, FILE *fp, TMTagAttrType attrs)
495 fprintf(fp, "%s", tag->name);
496 if (attrs & tm_tag_attr_type_t)
497 fprintf(fp, "%c%d", TA_TYPE, tag->type);
498 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
499 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
500 if (attrs & tm_tag_attr_line_t)
501 fprintf(fp, "%c%ld", TA_LINE, tag->line);
502 if (attrs & tm_tag_attr_local_t)
503 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
504 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
505 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
506 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
507 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
508 if (attrs & tm_tag_attr_pointer_t)
509 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
510 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
511 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
512 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
513 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
514 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
515 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
517 if (fprintf(fp, "\n"))
518 return TRUE;
519 else
520 return FALSE;
523 GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode)
525 guchar buf[BUFSIZ];
526 FILE *fp;
527 GPtrArray *file_tags;
528 TMTag *tag;
529 TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
531 if (NULL == (fp = g_fopen(tags_file, "r")))
532 return NULL;
533 if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf))
535 fclose(fp);
536 return NULL; /* early out on error */
538 else
539 { /* We read the first line for the format specification. */
540 if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL)
541 format = TM_FILE_FORMAT_PIPE;
542 else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL)
543 format = TM_FILE_FORMAT_TAGMANAGER;
544 else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL)
545 format = TM_FILE_FORMAT_CTAGS;
546 else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0)
547 format = TM_FILE_FORMAT_CTAGS;
548 else
549 { /* We didn't find a valid format specification, so we try to auto-detect the format
550 * by counting the pipe characters on the first line and asumme pipe format when
551 * we find more than one pipe on the line. */
552 guint i, pipe_cnt = 0, tab_cnt = 0;
553 for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++)
555 if (buf[i] == '|')
556 pipe_cnt++;
557 else if (buf[i] == '\t')
558 tab_cnt++;
560 if (pipe_cnt > 1)
561 format = TM_FILE_FORMAT_PIPE;
562 else if (tab_cnt > 1)
563 format = TM_FILE_FORMAT_CTAGS;
565 rewind(fp); /* reset the file pointer, to start reading again from the beginning */
568 file_tags = g_ptr_array_new();
569 while (NULL != (tag = new_tag_from_tags_file(NULL, fp, mode, format)))
570 g_ptr_array_add(file_tags, tag);
571 fclose(fp);
573 return file_tags;
576 gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array)
578 guint i;
579 FILE *fp;
581 g_return_val_if_fail(tags_array && tags_file, FALSE);
583 fp = g_fopen(tags_file, "w");
584 if (!fp)
585 return FALSE;
587 fprintf(fp, "# format=tagmanager\n");
588 for (i = 0; i < tags_array->len; i++)
590 TMTag *tag = TM_TAG(tags_array->pdata[i]);
592 write_tag(tag, fp, tm_tag_attr_type_t
593 | tm_tag_attr_scope_t | tm_tag_attr_arglist_t | tm_tag_attr_vartype_t
594 | tm_tag_attr_pointer_t);
596 fclose(fp);
598 return TRUE;
601 /* add argument list of __init__() Python methods to the class tag */
602 static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file)
604 guint i;
605 const char *parent_tag_name;
607 if (tag->type != tm_tag_method_t || tag->scope == NULL ||
608 g_strcmp0(tag->name, "__init__") != 0)
609 return;
611 parent_tag_name = strrchr(tag->scope, '.');
612 if (parent_tag_name)
613 parent_tag_name++;
614 else
615 parent_tag_name = tag->scope;
617 /* going in reverse order because the tag was added recently */
618 for (i = current_source_file->tags_array->len; i > 0; i--)
620 TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
621 if (g_strcmp0(prev_tag->name, parent_tag_name) == 0)
623 g_free(prev_tag->arglist);
624 prev_tag->arglist = g_strdup(tag->arglist);
625 break;
631 This function is registered into the ctags parser when a file is parsed for
632 the first time. The function is then called by the ctags parser each time
633 it finds a new tag. You should not have to use this function.
634 @see tm_source_file_parse()
636 static gboolean tm_source_file_tags(const tagEntryInfo *const tag,
637 gboolean invalidate, void *user_data)
639 TMSourceFile *current_source_file = user_data;
640 TMTag *tm_tag;
642 if (invalidate)
644 tm_tags_array_free(current_source_file->tags_array, FALSE);
645 return TRUE;
648 tm_tag = tm_tag_new();
650 if (!init_tag(tm_tag, current_source_file, tag))
652 tm_tag_unref(tm_tag);
653 return TRUE;
656 if (tm_tag->lang == TM_PARSER_PYTHON)
657 update_python_arglist(tm_tag, current_source_file);
659 g_ptr_array_add(current_source_file->tags_array, tm_tag);
661 return TRUE;
664 /* Initializes a TMSourceFile structure from a file name. */
665 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
666 const char* name)
668 GStatBuf s;
669 int status;
671 #ifdef TM_DEBUG
672 g_message("Source File init: %s", file_name);
673 #endif
675 if (file_name != NULL)
677 status = g_stat(file_name, &s);
678 if (0 != status)
680 /* g_warning("Unable to stat %s", file_name);*/
681 return FALSE;
683 if (!S_ISREG(s.st_mode))
685 g_warning("%s: Not a regular file", file_name);
686 return FALSE;
688 source_file->file_name = tm_get_real_path(file_name);
689 source_file->short_name = strrchr(source_file->file_name, '/');
690 if (source_file->short_name)
691 ++ source_file->short_name;
692 else
693 source_file->short_name = source_file->file_name;
696 source_file->tags_array = g_ptr_array_new();
698 if (name == NULL)
699 source_file->lang = TM_PARSER_NONE;
700 else
701 source_file->lang = tm_ctags_get_named_lang(name);
703 return TRUE;
706 /** Initializes a TMSourceFile structure and returns a pointer to it. The
707 * TMSourceFile has to be added to TMWorkspace to start its parsing.
708 * @param file_name The file name.
709 * @param name Name of the used programming language, NULL to disable parsing.
710 * @return The created unparsed TMSourceFile object.
711 * */
712 GEANY_API_SYMBOL
713 TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
715 TMSourceFilePriv *priv;
717 SOURCE_FILE_NEW(priv);
718 if (TRUE != tm_source_file_init(&priv->public, file_name, name))
720 SOURCE_FILE_FREE(priv);
721 return NULL;
723 priv->refcount = 1;
724 return &priv->public;
728 static TMSourceFile *tm_source_file_dup(TMSourceFile *source_file)
730 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
732 g_return_val_if_fail(NULL != source_file, NULL);
734 g_atomic_int_inc(&priv->refcount);
735 return source_file;
738 /* Destroys the contents of the source file. Note that the tags are owned by the
739 source file and are also destroyed when the source file is destroyed. If pointers
740 to these tags are used elsewhere, then those tag arrays should be rebuilt.
742 static void tm_source_file_destroy(TMSourceFile *source_file)
744 #ifdef TM_DEBUG
745 g_message("Destroying source file: %s", source_file->file_name);
746 #endif
748 g_free(source_file->file_name);
749 tm_tags_array_free(source_file->tags_array, TRUE);
750 source_file->tags_array = NULL;
753 /** Decrements the reference count of @a source_file
755 * If the reference count drops to 0, then @a source_file is freed, including all contents.
756 * Make sure the @a source_file is already removed from any TMWorkSpace before the
757 * this happens.
758 * @param source_file The source file to free.
759 * @see tm_workspace_remove_source_file()
761 GEANY_API_SYMBOL
762 void tm_source_file_free(TMSourceFile *source_file)
764 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
766 if (NULL != priv && g_atomic_int_dec_and_test(&priv->refcount))
768 tm_source_file_destroy(source_file);
769 SOURCE_FILE_FREE(priv);
773 /** Gets the GBoxed-derived GType for TMSourceFile
775 * @return TMSourceFile type . */
776 GEANY_API_SYMBOL
777 GType tm_source_file_get_type(void);
779 G_DEFINE_BOXED_TYPE(TMSourceFile, tm_source_file, tm_source_file_dup, tm_source_file_free);
781 /* Parses the text-buffer or source file and regenarates the tags.
782 @param source_file The source file to parse
783 @param text_buf The text buffer to parse
784 @param buf_size The size of text_buf.
785 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
786 TRUE to parse the buffer and ignore the file content.
787 @return TRUE on success, FALSE on failure
789 gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
790 gboolean use_buffer)
792 const char *file_name;
793 gboolean retry = TRUE;
794 gboolean parse_file = FALSE;
795 gboolean free_buf = FALSE;
797 if ((NULL == source_file) || (NULL == source_file->file_name))
799 g_warning("Attempt to parse NULL file");
800 return FALSE;
803 if (source_file->lang == TM_PARSER_NONE)
805 tm_tags_array_free(source_file->tags_array, FALSE);
806 return FALSE;
809 file_name = source_file->file_name;
811 if (!use_buffer)
813 GStatBuf s;
815 /* load file to memory and parse it from memory unless the file is too big */
816 if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024)
817 parse_file = TRUE;
818 else
820 if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL))
822 g_warning("Unable to open %s", file_name);
823 return FALSE;
825 free_buf = TRUE;
829 if (!parse_file && (NULL == text_buf || 0 == buf_size))
831 /* Empty buffer, "parse" by setting empty tag array */
832 tm_tags_array_free(source_file->tags_array, FALSE);
833 if (free_buf)
834 g_free(text_buf);
835 return TRUE;
838 tm_tags_array_free(source_file->tags_array, FALSE);
840 tm_ctags_parse(parse_file ? NULL : text_buf, buf_size, file_name,
841 source_file->lang, tm_source_file_tags, source_file);
843 if (free_buf)
844 g_free(text_buf);
845 return !retry;
848 /* Gets the name associated with the language index.
849 @param lang The language index.
850 @return The language name, or NULL.
852 const gchar *tm_source_file_get_lang_name(TMParserType lang)
854 return tm_ctags_get_lang_name(lang);
857 /* Gets the language index for \a name.
858 @param name The language name.
859 @return The language index, or TM_PARSER_NONE.
861 TMParserType tm_source_file_get_named_lang(const gchar *name)
863 return tm_ctags_get_named_lang(name);