ctags: Rename Geany-specific tagEntryInfo::arglist to upstream's ::signature
[geany-mirror.git] / src / tagmanager / tm_source_file.c
blob3f27e765cf29980f1d702987b222371a80557c61
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.
170 @param tag The TMTag structure to initialize
171 @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
172 @param tag_entry Tag information gathered by the ctags parser
173 @return TRUE on success, FALSE on failure
175 static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
177 TMTagType type;
179 if (!tag_entry)
180 return FALSE;
182 type = tm_parser_get_tag_type(tag_entry->kind, file->lang);
183 if (!tag_entry->name || type == tm_tag_undef_t)
184 return FALSE;
186 tag->name = g_strdup(tag_entry->name);
187 tag->type = type;
188 tag->local = tag_entry->isFileScope;
189 tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */
190 tag->line = tag_entry->lineNumber;
191 if (NULL != tag_entry->extensionFields.signature)
192 tag->arglist = g_strdup(tag_entry->extensionFields.signature);
193 if ((NULL != tag_entry->extensionFields.scope[1]) &&
194 (0 != tag_entry->extensionFields.scope[1][0]))
195 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
196 if (tag_entry->extensionFields.inheritance != NULL)
197 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
198 if (tag_entry->extensionFields.varType != NULL)
199 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
200 if (tag_entry->extensionFields.access != NULL)
201 tag->access = get_tag_access(tag_entry->extensionFields.access);
202 if (tag_entry->extensionFields.implementation != NULL)
203 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
204 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
205 tag->type = tm_tag_macro_with_arg_t;
206 tag->file = file;
207 tag->lang = file->lang;
208 return TRUE;
212 Initializes an already malloc()ed TMTag structure by reading a tag entry
213 line from a file. The structure should be allocated beforehand.
214 @param tag The TMTag structure to populate
215 @param file The TMSourceFile struct (assigned to the file member)
216 @param fp FILE pointer from where the tag line is read
217 @return TRUE on success, FALSE on FAILURE
219 static gboolean init_tag_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
221 guchar buf[BUFSIZ];
222 guchar *start, *end;
223 gboolean status;
224 guchar changed_char = TA_NAME;
226 tag->refcount = 1;
227 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
228 return FALSE;
229 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
231 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
232 ++ end;
233 if (('\0' == *end) || ('\n' == *end))
234 status = FALSE;
235 changed_char = *end;
236 *end = '\0';
237 if (NULL == tag->name)
239 if (!isprint(*start))
240 return FALSE;
241 else
242 tag->name = g_strdup((gchar*)start);
244 else
246 switch (*start)
248 case TA_LINE:
249 tag->line = atol((gchar*)start + 1);
250 break;
251 case TA_LOCAL:
252 tag->local = atoi((gchar*)start + 1);
253 break;
254 case TA_TYPE:
255 tag->type = (TMTagType) atoi((gchar*)start + 1);
256 break;
257 case TA_ARGLIST:
258 tag->arglist = g_strdup((gchar*)start + 1);
259 break;
260 case TA_SCOPE:
261 tag->scope = g_strdup((gchar*)start + 1);
262 break;
263 case TA_POINTER:
264 tag->pointerOrder = atoi((gchar*)start + 1);
265 break;
266 case TA_VARTYPE:
267 tag->var_type = g_strdup((gchar*)start + 1);
268 break;
269 case TA_INHERITS:
270 tag->inheritance = g_strdup((gchar*)start + 1);
271 break;
272 case TA_TIME: /* Obsolete */
273 break;
274 case TA_LANG: /* Obsolete */
275 break;
276 case TA_INACTIVE: /* Obsolete */
277 break;
278 case TA_ACCESS:
279 tag->access = (char) *(start + 1);
280 break;
281 case TA_IMPL:
282 tag->impl = (char) *(start + 1);
283 break;
284 default:
285 #ifdef GEANY_DEBUG
286 g_warning("Unknown attribute %s", start + 1);
287 #endif
288 break;
291 *end = changed_char;
293 if (NULL == tag->name)
294 return FALSE;
295 tag->file = file;
296 return TRUE;
299 /* alternative parser for Pascal and LaTeX global tags files with the following format
300 * tagname|return value|arglist|description\n */
301 static gboolean init_tag_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
303 guchar buf[BUFSIZ];
304 guchar *start, *end;
305 gboolean status;
306 /*guchar changed_char = TA_NAME;*/
308 tag->refcount = 1;
309 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
310 return FALSE;
312 gchar **fields;
313 guint field_len;
314 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
316 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
317 ++ end;
318 if (('\0' == *end) || ('\n' == *end))
319 status = FALSE;
320 /*changed_char = *end;*/
321 *end = '\0';
322 if (NULL == tag->name && !isprint(*start))
323 return FALSE;
325 fields = g_strsplit((gchar*)start, "|", -1);
326 field_len = g_strv_length(fields);
328 if (field_len >= 1) tag->name = g_strdup(fields[0]);
329 else tag->name = NULL;
330 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
331 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
332 tag->type = tm_tag_prototype_t;
333 g_strfreev(fields);
337 if (NULL == tag->name)
338 return FALSE;
339 tag->file = file;
340 return TRUE;
344 CTags tag file format (http://ctags.sourceforge.net/FORMAT)
346 static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp, TMParserType lang)
348 gchar buf[BUFSIZ];
349 gchar *p, *tab;
351 tag->refcount = 1;
352 tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
355 if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
356 return FALSE;
358 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
360 p = buf;
362 /* tag name */
363 if (! (tab = strchr(p, '\t')) || p == tab)
364 return FALSE;
365 tag->name = g_strndup(p, (gsize)(tab - p));
366 p = tab + 1;
368 /* tagfile, unused */
369 if (! (tab = strchr(p, '\t')))
371 g_free(tag->name);
372 tag->name = NULL;
373 return FALSE;
375 p = tab + 1;
376 /* Ex command, unused */
377 if (*p == '/' || *p == '?')
379 gchar c = *p;
380 for (++p; *p && *p != c; p++)
382 if (*p == '\\' && p[1])
383 p++;
386 else /* assume a line */
387 tag->line = atol(p);
388 tab = strstr(p, ";\"");
389 /* read extension fields */
390 if (tab)
392 p = tab + 2;
393 while (*p && *p != '\n' && *p != '\r')
395 gchar *end;
396 const gchar *key, *value = NULL;
398 /* skip leading tabulations */
399 while (*p && *p == '\t') p++;
400 /* find the separator (:) and end (\t) */
401 key = end = p;
402 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
404 if (*end == ':' && ! value)
406 *end = 0; /* terminate the key */
407 value = end + 1;
409 end++;
411 /* move p paste the so we won't stop parsing by setting *end=0 below */
412 p = *end ? end + 1 : end;
413 *end = 0; /* terminate the value (or key if no value) */
415 if (! value || 0 == strcmp(key, "kind")) /* tag kind */
417 const gchar *kind = value ? value : key;
419 if (kind[0] && kind[1])
420 tag->type = tm_parser_get_tag_type(tm_ctags_get_kind_from_name(kind, lang), lang);
421 else
422 tag->type = tm_parser_get_tag_type(*kind, lang);
424 else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
426 g_free(tag->inheritance);
427 tag->inheritance = g_strdup(value);
429 else if (0 == strcmp(key, "implementation")) /* implementation limit */
430 tag->impl = get_tag_impl(value);
431 else if (0 == strcmp(key, "line")) /* line */
432 tag->line = atol(value);
433 else if (0 == strcmp(key, "access")) /* access */
434 tag->access = get_tag_access(value);
435 else if (0 == strcmp(key, "class") ||
436 0 == strcmp(key, "enum") ||
437 0 == strcmp(key, "function") ||
438 0 == strcmp(key, "struct") ||
439 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
441 g_free(tag->scope);
442 tag->scope = g_strdup(value);
444 else if (0 == strcmp(key, "file")) /* static (local) tag */
445 tag->local = TRUE;
446 else if (0 == strcmp(key, "signature")) /* arglist */
448 g_free(tag->arglist);
449 tag->arglist = g_strdup(value);
454 tag->file = file;
455 return TRUE;
458 static TMTag *new_tag_from_tags_file(TMSourceFile *file, FILE *fp, TMParserType mode, TMFileFormat format)
460 TMTag *tag = tm_tag_new();
461 gboolean result = FALSE;
463 switch (format)
465 case TM_FILE_FORMAT_TAGMANAGER:
466 result = init_tag_from_file(tag, file, fp);
467 break;
468 case TM_FILE_FORMAT_PIPE:
469 result = init_tag_from_file_alt(tag, file, fp);
470 break;
471 case TM_FILE_FORMAT_CTAGS:
472 result = init_tag_from_file_ctags(tag, file, fp, mode);
473 break;
476 if (! result)
478 tm_tag_unref(tag);
479 return NULL;
481 tag->lang = mode;
482 return tag;
486 Writes tag information to the given FILE *.
487 @param tag The tag information to write.
488 @param file FILE pointer to which the tag information is written.
489 @param attrs Attributes to be written (bitmask).
490 @return TRUE on success, FALSE on failure.
492 static gboolean write_tag(TMTag *tag, FILE *fp, TMTagAttrType attrs)
494 fprintf(fp, "%s", tag->name);
495 if (attrs & tm_tag_attr_type_t)
496 fprintf(fp, "%c%d", TA_TYPE, tag->type);
497 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
498 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
499 if (attrs & tm_tag_attr_line_t)
500 fprintf(fp, "%c%ld", TA_LINE, tag->line);
501 if (attrs & tm_tag_attr_local_t)
502 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
503 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
504 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
505 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
506 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
507 if (attrs & tm_tag_attr_pointer_t)
508 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
509 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
510 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
511 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
512 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
513 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
514 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
516 if (fprintf(fp, "\n"))
517 return TRUE;
518 else
519 return FALSE;
522 GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode)
524 guchar buf[BUFSIZ];
525 FILE *fp;
526 GPtrArray *file_tags;
527 TMTag *tag;
528 TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
530 if (NULL == (fp = g_fopen(tags_file, "r")))
531 return NULL;
532 if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf))
534 fclose(fp);
535 return NULL; /* early out on error */
537 else
538 { /* We read the first line for the format specification. */
539 if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL)
540 format = TM_FILE_FORMAT_PIPE;
541 else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL)
542 format = TM_FILE_FORMAT_TAGMANAGER;
543 else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL)
544 format = TM_FILE_FORMAT_CTAGS;
545 else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0)
546 format = TM_FILE_FORMAT_CTAGS;
547 else
548 { /* We didn't find a valid format specification, so we try to auto-detect the format
549 * by counting the pipe characters on the first line and asumme pipe format when
550 * we find more than one pipe on the line. */
551 guint i, pipe_cnt = 0, tab_cnt = 0;
552 for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++)
554 if (buf[i] == '|')
555 pipe_cnt++;
556 else if (buf[i] == '\t')
557 tab_cnt++;
559 if (pipe_cnt > 1)
560 format = TM_FILE_FORMAT_PIPE;
561 else if (tab_cnt > 1)
562 format = TM_FILE_FORMAT_CTAGS;
564 rewind(fp); /* reset the file pointer, to start reading again from the beginning */
567 file_tags = g_ptr_array_new();
568 while (NULL != (tag = new_tag_from_tags_file(NULL, fp, mode, format)))
569 g_ptr_array_add(file_tags, tag);
570 fclose(fp);
572 return file_tags;
575 gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array)
577 guint i;
578 FILE *fp;
579 gboolean ret = TRUE;
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 ret = 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 if (!ret)
597 break;
599 fclose(fp);
601 return ret;
604 /* add argument list of __init__() Python methods to the class tag */
605 static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file)
607 guint i;
608 const char *parent_tag_name;
610 if (tag->type != tm_tag_method_t || tag->scope == NULL ||
611 g_strcmp0(tag->name, "__init__") != 0)
612 return;
614 parent_tag_name = strrchr(tag->scope, '.');
615 if (parent_tag_name)
616 parent_tag_name++;
617 else
618 parent_tag_name = tag->scope;
620 /* going in reverse order because the tag was added recently */
621 for (i = current_source_file->tags_array->len; i > 0; i--)
623 TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
624 if (g_strcmp0(prev_tag->name, parent_tag_name) == 0)
626 g_free(prev_tag->arglist);
627 prev_tag->arglist = g_strdup(tag->arglist);
628 break;
633 /* new parsing pass ctags callback function */
634 static gboolean ctags_pass_start(void *user_data)
636 TMSourceFile *current_source_file = user_data;
638 tm_tags_array_free(current_source_file->tags_array, FALSE);
639 return TRUE;
642 /* new tag ctags callback function */
643 static gboolean ctags_new_tag(const tagEntryInfo *const tag,
644 void *user_data)
646 TMSourceFile *current_source_file = user_data;
647 TMTag *tm_tag = tm_tag_new();
649 if (!init_tag(tm_tag, current_source_file, tag))
651 tm_tag_unref(tm_tag);
652 return TRUE;
655 if (tm_tag->lang == TM_PARSER_PYTHON)
656 update_python_arglist(tm_tag, current_source_file);
658 g_ptr_array_add(current_source_file->tags_array, tm_tag);
660 return TRUE;
663 /* Initializes a TMSourceFile structure from a file name. */
664 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
665 const char* name)
667 GStatBuf s;
668 int status;
670 #ifdef TM_DEBUG
671 g_message("Source File init: %s", file_name);
672 #endif
674 if (file_name != NULL)
676 status = g_stat(file_name, &s);
677 if (0 != status)
679 /* g_warning("Unable to stat %s", file_name);*/
680 return FALSE;
682 if (!S_ISREG(s.st_mode))
684 g_warning("%s: Not a regular file", file_name);
685 return FALSE;
687 source_file->file_name = tm_get_real_path(file_name);
688 source_file->short_name = strrchr(source_file->file_name, '/');
689 if (source_file->short_name)
690 ++ source_file->short_name;
691 else
692 source_file->short_name = source_file->file_name;
695 source_file->tags_array = g_ptr_array_new();
697 if (name == NULL)
698 source_file->lang = TM_PARSER_NONE;
699 else
700 source_file->lang = tm_ctags_get_named_lang(name);
702 return TRUE;
705 /** Initializes a TMSourceFile structure and returns a pointer to it. The
706 * TMSourceFile has to be added to TMWorkspace to start its parsing.
707 * @param file_name The file name.
708 * @param name Name of the used programming language, NULL to disable parsing.
709 * @return The created unparsed TMSourceFile object.
710 * */
711 GEANY_API_SYMBOL
712 TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
714 TMSourceFilePriv *priv;
716 SOURCE_FILE_NEW(priv);
717 if (TRUE != tm_source_file_init(&priv->public, file_name, name))
719 SOURCE_FILE_FREE(priv);
720 return NULL;
722 priv->refcount = 1;
723 return &priv->public;
727 static TMSourceFile *tm_source_file_dup(TMSourceFile *source_file)
729 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
731 g_return_val_if_fail(NULL != source_file, NULL);
733 g_atomic_int_inc(&priv->refcount);
734 return source_file;
737 /* Destroys the contents of the source file. Note that the tags are owned by the
738 source file and are also destroyed when the source file is destroyed. If pointers
739 to these tags are used elsewhere, then those tag arrays should be rebuilt.
741 static void tm_source_file_destroy(TMSourceFile *source_file)
743 #ifdef TM_DEBUG
744 g_message("Destroying source file: %s", source_file->file_name);
745 #endif
747 g_free(source_file->file_name);
748 tm_tags_array_free(source_file->tags_array, TRUE);
749 source_file->tags_array = NULL;
752 /** Decrements the reference count of @a source_file
754 * If the reference count drops to 0, then @a source_file is freed, including all contents.
755 * Make sure the @a source_file is already removed from any TMWorkSpace before the
756 * this happens.
757 * @param source_file The source file to free.
758 * @see tm_workspace_remove_source_file()
760 GEANY_API_SYMBOL
761 void tm_source_file_free(TMSourceFile *source_file)
763 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
765 if (NULL != priv && g_atomic_int_dec_and_test(&priv->refcount))
767 tm_source_file_destroy(source_file);
768 SOURCE_FILE_FREE(priv);
772 /** Gets the GBoxed-derived GType for TMSourceFile
774 * @return TMSourceFile type . */
775 GEANY_API_SYMBOL
776 GType tm_source_file_get_type(void);
778 G_DEFINE_BOXED_TYPE(TMSourceFile, tm_source_file, tm_source_file_dup, tm_source_file_free);
780 /* Parses the text-buffer or source file and regenarates the tags.
781 @param source_file The source file to parse
782 @param text_buf The text buffer to parse
783 @param buf_size The size of text_buf.
784 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
785 TRUE to parse the buffer and ignore the file content.
786 @return TRUE on success, FALSE on failure
788 gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
789 gboolean use_buffer)
791 const char *file_name;
792 gboolean retry = TRUE;
793 gboolean parse_file = FALSE;
794 gboolean free_buf = FALSE;
796 if ((NULL == source_file) || (NULL == source_file->file_name))
798 g_warning("Attempt to parse NULL file");
799 return FALSE;
802 if (source_file->lang == TM_PARSER_NONE)
804 tm_tags_array_free(source_file->tags_array, FALSE);
805 return FALSE;
808 file_name = source_file->file_name;
810 if (!use_buffer)
812 GStatBuf s;
814 /* load file to memory and parse it from memory unless the file is too big */
815 if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024)
816 parse_file = TRUE;
817 else
819 if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL))
821 g_warning("Unable to open %s", file_name);
822 return FALSE;
824 free_buf = TRUE;
828 if (!parse_file && (NULL == text_buf || 0 == buf_size))
830 /* Empty buffer, "parse" by setting empty tag array */
831 tm_tags_array_free(source_file->tags_array, FALSE);
832 if (free_buf)
833 g_free(text_buf);
834 return TRUE;
837 tm_tags_array_free(source_file->tags_array, FALSE);
839 tm_ctags_parse(parse_file ? NULL : text_buf, buf_size, file_name,
840 source_file->lang, ctags_new_tag, ctags_pass_start, source_file);
842 if (free_buf)
843 g_free(text_buf);
844 return !retry;
847 /* Gets the name associated with the language index.
848 @param lang The language index.
849 @return The language name, or NULL.
851 const gchar *tm_source_file_get_lang_name(TMParserType lang)
853 return tm_ctags_get_lang_name(lang);
856 /* Gets the language index for \a name.
857 @param name The language name.
858 @return The language index, or TM_PARSER_NONE.
860 TMParserType tm_source_file_get_named_lang(const gchar *name)
862 return tm_ctags_get_named_lang(name);