Initialize ctags at a single place instead of four
[geany-mirror.git] / tagmanager / src / tm_source_file.c
blob3fa80f54f1e29f21bc3be4408f50f04bb845ada1
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 <sys/stat.h>
22 #include <glib/gstdio.h>
23 #ifdef G_OS_WIN32
24 # define VC_EXTRALEAN
25 # define WIN32_LEAN_AND_MEAN
26 # include <windows.h> /* for GetFullPathName */
27 #endif
29 #include "general.h"
30 #include "entry.h"
31 #include "parse.h"
32 #include "read.h"
34 #define LIBCTAGS_DEFINED
35 #include "tm_source_file.h"
36 #include "tm_tag.h"
37 #include "tm_parser.h"
39 typedef struct
41 TMSourceFile public;
42 guint refcount;
43 } TMSourceFilePriv;
45 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
46 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
48 static TMSourceFile *current_source_file = NULL;
50 static int get_path_max(const char *path)
52 #ifdef PATH_MAX
53 return PATH_MAX;
54 #else
55 int path_max = pathconf(path, _PC_PATH_MAX);
56 if (path_max <= 0)
57 path_max = 4096;
58 return path_max;
59 #endif
63 #ifdef G_OS_WIN32
64 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
65 * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
66 * with special chars within the filename */
67 static char *realpath (const char *pathname, char *resolved_path)
69 int size;
71 if (resolved_path != NULL)
73 int path_max = get_path_max(pathname);
74 size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
75 if (size > path_max)
76 return NULL;
77 else
78 return resolved_path;
80 else
82 size = GetFullPathNameA (pathname, 0, NULL, NULL);
83 resolved_path = g_new0 (char, size);
84 GetFullPathNameA (pathname, size, resolved_path, NULL);
85 return resolved_path;
88 #endif
90 /**
91 Given a file name, returns a newly allocated string containing the realpath()
92 of the file.
93 @param file_name The original file_name
94 @return A newly allocated string containing the real path to the file. NULL if none is available.
96 GEANY_API_SYMBOL
97 gchar *tm_get_real_path(const gchar *file_name)
99 if (file_name)
101 gsize len = get_path_max(file_name) + 1;
102 gchar *path = g_malloc0(len);
104 if (realpath(file_name, path))
105 return path;
106 else
107 g_free(path);
109 return NULL;
112 /* add argument list of __init__() Python methods to the class tag */
113 static void update_python_arglist(const TMTag *tag)
115 guint i;
116 const char *parent_tag_name;
118 if (tag->type != tm_tag_method_t || tag->scope == NULL ||
119 g_strcmp0(tag->name, "__init__") != 0)
120 return;
122 parent_tag_name = strrchr(tag->scope, '.');
123 if (parent_tag_name)
124 parent_tag_name++;
125 else
126 parent_tag_name = tag->scope;
128 /* going in reverse order because the tag was added recently */
129 for (i = current_source_file->tags_array->len; i > 0; i--)
131 TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
132 if (g_strcmp0(prev_tag->name, parent_tag_name) == 0)
134 g_free(prev_tag->arglist);
135 prev_tag->arglist = g_strdup(tag->arglist);
136 break;
142 This function is registered into the ctags parser when a file is parsed for
143 the first time. The function is then called by the ctags parser each time
144 it finds a new tag. You should not have to use this function.
145 @see tm_source_file_parse()
147 static int tm_source_file_tags(const tagEntryInfo *tag)
149 if (NULL == current_source_file)
150 return 0;
152 TMTag *tm_tag = tm_tag_new(current_source_file, tag);
154 if (tm_tag->lang == TM_PARSER_PYTHON)
155 update_python_arglist(tm_tag);
157 g_ptr_array_add(current_source_file->tags_array, tm_tag);
159 return TRUE;
162 void tm_source_file_ctags_init()
164 initializeParsing();
165 installLanguageMapDefaults();
166 TagEntryFunction = tm_source_file_tags;
169 /* Initializes a TMSourceFile structure from a file name. */
170 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
171 const char* name)
173 GStatBuf s;
174 int status;
176 #ifdef TM_DEBUG
177 g_message("Source File init: %s", file_name);
178 #endif
180 if (file_name != NULL)
182 status = g_stat(file_name, &s);
183 if (0 != status)
185 /* g_warning("Unable to stat %s", file_name);*/
186 return FALSE;
188 if (!S_ISREG(s.st_mode))
190 g_warning("%s: Not a regular file", file_name);
191 return FALSE;
193 source_file->file_name = tm_get_real_path(file_name);
194 source_file->short_name = strrchr(source_file->file_name, '/');
195 if (source_file->short_name)
196 ++ source_file->short_name;
197 else
198 source_file->short_name = source_file->file_name;
201 source_file->tags_array = g_ptr_array_new();
203 if (name == NULL)
204 source_file->lang = TM_PARSER_NONE;
205 else
206 source_file->lang = getNamedLanguage(name);
208 return TRUE;
211 /** Initializes a TMSourceFile structure and returns a pointer to it. The
212 * TMSourceFile has to be added to TMWorkspace to start its parsing.
213 * @param file_name The file name.
214 * @param name Name of the used programming language, NULL to disable parsing.
215 * @return The created unparsed TMSourceFile object.
216 * */
217 GEANY_API_SYMBOL
218 TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
220 TMSourceFilePriv *priv;
222 SOURCE_FILE_NEW(priv);
223 if (TRUE != tm_source_file_init(&priv->public, file_name, name))
225 SOURCE_FILE_FREE(priv);
226 return NULL;
228 priv->refcount = 1;
229 return &priv->public;
233 static TMSourceFile *tm_source_file_dup(TMSourceFile *source_file)
235 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
237 g_return_val_if_fail(NULL != source_file, NULL);
239 g_atomic_int_inc(&priv->refcount);
240 return source_file;
243 /* Destroys the contents of the source file. Note that the tags are owned by the
244 source file and are also destroyed when the source file is destroyed. If pointers
245 to these tags are used elsewhere, then those tag arrays should be rebuilt.
247 static void tm_source_file_destroy(TMSourceFile *source_file)
249 #ifdef TM_DEBUG
250 g_message("Destroying source file: %s", source_file->file_name);
251 #endif
253 g_free(source_file->file_name);
254 tm_tags_array_free(source_file->tags_array, TRUE);
255 source_file->tags_array = NULL;
258 /** Decrements the reference count of @a source_file
260 * If the reference count drops to 0, then @a source_file is freed, including all contents.
261 * Make sure the @a source_file is already removed from any TMWorkSpace before the
262 * this happens.
263 * @param source_file The source file to free.
264 * @see tm_workspace_remove_source_file()
266 GEANY_API_SYMBOL
267 void tm_source_file_free(TMSourceFile *source_file)
269 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
271 if (NULL != priv && g_atomic_int_dec_and_test(&priv->refcount))
273 tm_source_file_destroy(source_file);
274 SOURCE_FILE_FREE(priv);
278 /** Gets the GBoxed-derived GType for TMSourceFile
280 * @return TMSourceFile type . */
281 GEANY_API_SYMBOL
282 GType tm_source_file_get_type(void);
284 G_DEFINE_BOXED_TYPE(TMSourceFile, tm_source_file, tm_source_file_dup, tm_source_file_free);
286 /* Parses the text-buffer or source file and regenarates the tags.
287 @param source_file The source file to parse
288 @param text_buf The text buffer to parse
289 @param buf_size The size of text_buf.
290 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
291 TRUE to parse the buffer and ignore the file content.
292 @return TRUE on success, FALSE on failure
294 gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
295 gboolean use_buffer)
297 const char *file_name;
298 gboolean retry = TRUE;
299 gboolean parse_file = FALSE;
300 gboolean free_buf = FALSE;
302 if ((NULL == source_file) || (NULL == source_file->file_name))
304 g_warning("Attempt to parse NULL file");
305 return FALSE;
308 if (source_file->lang == TM_PARSER_NONE)
310 tm_tags_array_free(source_file->tags_array, FALSE);
311 return FALSE;
314 file_name = source_file->file_name;
316 if (!use_buffer)
318 GStatBuf s;
320 /* load file to memory and parse it from memory unless the file is too big */
321 if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024)
322 parse_file = TRUE;
323 else
325 if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL))
327 g_warning("Unable to open %s", file_name);
328 return FALSE;
330 free_buf = TRUE;
334 if (!parse_file && (NULL == text_buf || 0 == buf_size))
336 /* Empty buffer, "parse" by setting empty tag array */
337 tm_tags_array_free(source_file->tags_array, FALSE);
338 if (free_buf)
339 g_free(text_buf);
340 return TRUE;
343 current_source_file = source_file;
344 if (! LanguageTable [source_file->lang]->enabled)
346 #ifdef TM_DEBUG
347 g_warning("ignoring %s (language disabled)\n", file_name);
348 #endif
350 else
352 guint passCount = 0;
353 while (retry && passCount < 3)
355 tm_tags_array_free(source_file->tags_array, FALSE);
356 if (parse_file && fileOpen (file_name, source_file->lang))
358 if (LanguageTable [source_file->lang]->parser != NULL)
360 LanguageTable [source_file->lang]->parser ();
361 fileClose ();
362 retry = FALSE;
363 break;
365 else if (LanguageTable [source_file->lang]->parser2 != NULL)
366 retry = LanguageTable [source_file->lang]->parser2 (passCount);
367 fileClose ();
369 else if (!parse_file && bufferOpen (text_buf, buf_size, file_name, source_file->lang))
371 if (LanguageTable [source_file->lang]->parser != NULL)
373 LanguageTable [source_file->lang]->parser ();
374 bufferClose ();
375 retry = FALSE;
376 break;
378 else if (LanguageTable [source_file->lang]->parser2 != NULL)
379 retry = LanguageTable [source_file->lang]->parser2 (passCount);
380 bufferClose ();
382 else
384 g_warning("Unable to open %s", file_name);
385 return FALSE;
387 ++ passCount;
391 if (free_buf)
392 g_free(text_buf);
393 return !retry;
396 /* Gets the name associated with the language index.
397 @param lang The language index.
398 @return The language name, or NULL.
400 const gchar *tm_source_file_get_lang_name(TMParserType lang)
402 return getLanguageName(lang);
405 /* Gets the language index for \a name.
406 @param name The language name.
407 @return The language index, or TM_PARSER_NONE.
409 TMParserType tm_source_file_get_named_lang(const gchar *name)
411 return getNamedLanguage(name);
414 #if 0
416 Writes all tags of a source file (including the file tag itself) to the passed
417 file pointer.
418 @param source_file The source file to write.
419 @param fp The file pointer to write to.
420 @param attrs The attributes to write.
421 @return TRUE on success, FALSE on failure.
423 static gboolean tm_source_file_write(TMSourceFile *source_file, FILE *fp, guint attrs)
425 TMTag *tag;
426 guint i;
428 if (NULL != source_file)
430 if (NULL != (tag = tm_tag_new(source_file, NULL)))
432 tm_tag_write(tag, fp, tm_tag_attr_max_t);
433 tm_tag_unref(tag);
434 if (NULL != source_file->tags_array)
436 for (i=0; i < source_file->tags_array->len; ++i)
438 tag = TM_TAG(source_file->tags_array->pdata[i]);
439 if (TRUE != tm_tag_write(tag, fp, attrs))
440 return FALSE;
445 return TRUE;
447 #endif