Merge pull request #862 from techee/tm_workspace_find_cleanup3
[geany-mirror.git] / tagmanager / src / tm_source_file.c
blobaf3d6499c5a320f8199b4f90a334692c475dfdd8
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"
38 typedef struct
40 TMSourceFile public;
41 guint refcount;
42 } TMSourceFilePriv;
44 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
45 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
47 static TMSourceFile *current_source_file = NULL;
49 static int get_path_max(const char *path)
51 #ifdef PATH_MAX
52 return PATH_MAX;
53 #else
54 int path_max = pathconf(path, _PC_PATH_MAX);
55 if (path_max <= 0)
56 path_max = 4096;
57 return path_max;
58 #endif
62 #ifdef G_OS_WIN32
63 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
64 * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
65 * with special chars within the filename */
66 static char *realpath (const char *pathname, char *resolved_path)
68 int size;
70 if (resolved_path != NULL)
72 int path_max = get_path_max(pathname);
73 size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
74 if (size > path_max)
75 return NULL;
76 else
77 return resolved_path;
79 else
81 size = GetFullPathNameA (pathname, 0, NULL, NULL);
82 resolved_path = g_new0 (char, size);
83 GetFullPathNameA (pathname, size, resolved_path, NULL);
84 return resolved_path;
87 #endif
89 /**
90 Given a file name, returns a newly allocated string containing the realpath()
91 of the file.
92 @param file_name The original file_name
93 @return A newly allocated string containing the real path to the file. NULL if none is available.
95 GEANY_API_SYMBOL
96 gchar *tm_get_real_path(const gchar *file_name)
98 if (file_name)
100 gsize len = get_path_max(file_name) + 1;
101 gchar *path = g_malloc0(len);
103 if (realpath(file_name, path))
104 return path;
105 else
106 g_free(path);
108 return NULL;
112 This function is registered into the ctags parser when a file is parsed for
113 the first time. The function is then called by the ctags parser each time
114 it finds a new tag. You should not have to use this function.
115 @see tm_source_file_parse()
117 static int tm_source_file_tags(const tagEntryInfo *tag)
119 if (NULL == current_source_file)
120 return 0;
121 g_ptr_array_add(current_source_file->tags_array,
122 tm_tag_new(current_source_file, tag));
123 return TRUE;
126 /* Set the argument list of tag identified by its name */
127 static void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist)
129 guint i;
131 if (NULL == arglist ||
132 NULL == tag_name ||
133 NULL == current_source_file)
135 return;
138 /* going in reverse order because the tag was added recently */
139 for (i = current_source_file->tags_array->len; i > 0; i--)
141 TMTag *tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
142 if (g_strcmp0(tag->name, tag_name) == 0)
144 g_free(tag->arglist);
145 tag->arglist = g_strdup(arglist);
146 break;
151 /* Initializes a TMSourceFile structure from a file name. */
152 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
153 const char* name)
155 GStatBuf s;
156 int status;
158 #ifdef TM_DEBUG
159 g_message("Source File init: %s", file_name);
160 #endif
162 if (file_name != NULL)
164 status = g_stat(file_name, &s);
165 if (0 != status)
167 /* g_warning("Unable to stat %s", file_name);*/
168 return FALSE;
170 if (!S_ISREG(s.st_mode))
172 g_warning("%s: Not a regular file", file_name);
173 return FALSE;
175 source_file->file_name = tm_get_real_path(file_name);
176 source_file->short_name = strrchr(source_file->file_name, '/');
177 if (source_file->short_name)
178 ++ source_file->short_name;
179 else
180 source_file->short_name = source_file->file_name;
183 source_file->tags_array = g_ptr_array_new();
185 if (NULL == LanguageTable)
187 initializeParsing();
188 installLanguageMapDefaults();
189 if (NULL == TagEntryFunction)
190 TagEntryFunction = tm_source_file_tags;
191 if (NULL == TagEntrySetArglistFunction)
192 TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
195 if (name == NULL)
196 source_file->lang = LANG_AUTO;
197 else
198 source_file->lang = getNamedLanguage(name);
200 return TRUE;
203 /** Initializes a TMSourceFile structure and returns a pointer to it. The
204 * TMSourceFile has to be added to TMWorkspace to start its parsing.
205 * @param file_name The file name.
206 * @param name Name of the used programming language, NULL for autodetection.
207 * @return The created unparsed TMSourceFile object.
208 * */
209 GEANY_API_SYMBOL
210 TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
212 TMSourceFilePriv *priv;
214 SOURCE_FILE_NEW(priv);
215 if (TRUE != tm_source_file_init(&priv->public, file_name, name))
217 SOURCE_FILE_FREE(priv);
218 return NULL;
220 priv->refcount = 1;
221 return &priv->public;
225 static TMSourceFile *tm_source_file_dup(TMSourceFile *source_file)
227 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
229 g_return_val_if_fail(NULL != source_file, NULL);
231 g_atomic_int_inc(&priv->refcount);
232 return source_file;
235 /* Destroys the contents of the source file. Note that the tags are owned by the
236 source file and are also destroyed when the source file is destroyed. If pointers
237 to these tags are used elsewhere, then those tag arrays should be rebuilt.
239 static void tm_source_file_destroy(TMSourceFile *source_file)
241 #ifdef TM_DEBUG
242 g_message("Destroying source file: %s", source_file->file_name);
243 #endif
245 g_free(source_file->file_name);
246 tm_tags_array_free(source_file->tags_array, TRUE);
247 source_file->tags_array = NULL;
250 /** Decrements the reference count of @a source_file
252 * If the reference count drops to 0, then @a source_file is freed, including all contents.
253 * Make sure the @a source_file is already removed from any TMWorkSpace before the
254 * this happens.
255 * @param source_file The source file to free.
256 * @see tm_workspace_remove_source_file()
258 GEANY_API_SYMBOL
259 void tm_source_file_free(TMSourceFile *source_file)
261 TMSourceFilePriv *priv = (TMSourceFilePriv *) source_file;
263 if (NULL != priv && g_atomic_int_dec_and_test(&priv->refcount))
265 tm_source_file_destroy(source_file);
266 SOURCE_FILE_FREE(priv);
270 /** Gets the GBoxed-derived GType for TMSourceFile
272 * @return TMSourceFile type . */
273 GEANY_API_SYMBOL
274 GType tm_source_file_get_type(void);
276 G_DEFINE_BOXED_TYPE(TMSourceFile, tm_source_file, tm_source_file_dup, tm_source_file_free);
278 /* Parses the text-buffer or source file and regenarates the tags.
279 @param source_file The source file to parse
280 @param text_buf The text buffer to parse
281 @param buf_size The size of text_buf.
282 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
283 TRUE to parse the buffer and ignore the file content.
284 @return TRUE on success, FALSE on failure
286 gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
287 gboolean use_buffer)
289 const char *file_name;
290 gboolean retry = TRUE;
291 gboolean parse_file = FALSE;
292 gboolean free_buf = FALSE;
294 if ((NULL == source_file) || (NULL == source_file->file_name))
296 g_warning("Attempt to parse NULL file");
297 return FALSE;
300 if (source_file->lang == LANG_IGNORE)
302 tm_tags_array_free(source_file->tags_array, FALSE);
303 return FALSE;
306 file_name = source_file->file_name;
308 if (!use_buffer)
310 GStatBuf s;
312 /* load file to memory and parse it from memory unless the file is too big */
313 if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024)
314 parse_file = TRUE;
315 else
317 if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL))
319 g_warning("Unable to open %s", file_name);
320 return FALSE;
322 free_buf = TRUE;
326 if (!parse_file && (NULL == text_buf || 0 == buf_size))
328 /* Empty buffer, "parse" by setting empty tag array */
329 tm_tags_array_free(source_file->tags_array, FALSE);
330 if (free_buf)
331 g_free(text_buf);
332 return TRUE;
335 if (NULL == LanguageTable)
337 initializeParsing();
338 installLanguageMapDefaults();
339 if (NULL == TagEntryFunction)
340 TagEntryFunction = tm_source_file_tags;
341 if (NULL == TagEntrySetArglistFunction)
342 TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
344 current_source_file = source_file;
345 if (LANG_AUTO == source_file->lang)
346 source_file->lang = getFileLanguage (file_name);
347 if (source_file->lang == LANG_IGNORE)
349 #ifdef TM_DEBUG
350 g_warning("ignoring %s (unknown language)\n", file_name);
351 #endif
353 else if (! LanguageTable [source_file->lang]->enabled)
355 #ifdef TM_DEBUG
356 g_warning("ignoring %s (language disabled)\n", file_name);
357 #endif
359 else
361 guint passCount = 0;
362 while (retry && passCount < 3)
364 tm_tags_array_free(source_file->tags_array, FALSE);
365 if (parse_file && fileOpen (file_name, source_file->lang))
367 if (LanguageTable [source_file->lang]->parser != NULL)
369 LanguageTable [source_file->lang]->parser ();
370 fileClose ();
371 retry = FALSE;
372 break;
374 else if (LanguageTable [source_file->lang]->parser2 != NULL)
375 retry = LanguageTable [source_file->lang]->parser2 (passCount);
376 fileClose ();
378 else if (!parse_file && bufferOpen (text_buf, buf_size, file_name, source_file->lang))
380 if (LanguageTable [source_file->lang]->parser != NULL)
382 LanguageTable [source_file->lang]->parser ();
383 bufferClose ();
384 retry = FALSE;
385 break;
387 else if (LanguageTable [source_file->lang]->parser2 != NULL)
388 retry = LanguageTable [source_file->lang]->parser2 (passCount);
389 bufferClose ();
391 else
393 g_warning("Unable to open %s", file_name);
394 return FALSE;
396 ++ passCount;
400 if (free_buf)
401 g_free(text_buf);
402 return !retry;
405 /* Gets the name associated with the language index.
406 @param lang The language index.
407 @return The language name, or NULL.
409 const gchar *tm_source_file_get_lang_name(gint lang)
411 if (NULL == LanguageTable)
413 initializeParsing();
414 installLanguageMapDefaults();
415 if (NULL == TagEntryFunction)
416 TagEntryFunction = tm_source_file_tags;
417 if (NULL == TagEntrySetArglistFunction)
418 TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
420 return getLanguageName(lang);
423 /* Gets the language index for \a name.
424 @param name The language name.
425 @return The language index, or -2.
427 gint tm_source_file_get_named_lang(const gchar *name)
429 if (NULL == LanguageTable)
431 initializeParsing();
432 installLanguageMapDefaults();
433 if (NULL == TagEntryFunction)
434 TagEntryFunction = tm_source_file_tags;
435 if (NULL == TagEntrySetArglistFunction)
436 TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
438 return getNamedLanguage(name);
441 #if 0
443 Writes all tags of a source file (including the file tag itself) to the passed
444 file pointer.
445 @param source_file The source file to write.
446 @param fp The file pointer to write to.
447 @param attrs The attributes to write.
448 @return TRUE on success, FALSE on failure.
450 static gboolean tm_source_file_write(TMSourceFile *source_file, FILE *fp, guint attrs)
452 TMTag *tag;
453 guint i;
455 if (NULL != source_file)
457 if (NULL != (tag = tm_tag_new(source_file, NULL)))
459 tm_tag_write(tag, fp, tm_tag_attr_max_t);
460 tm_tag_unref(tag);
461 if (NULL != source_file->tags_array)
463 for (i=0; i < source_file->tags_array->len; ++i)
465 tag = TM_TAG(source_file->tags_array->pdata[i]);
466 if (TRUE != tm_tag_write(tag, fp, attrs))
467 return FALSE;
472 return TRUE;
474 #endif