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.
11 * @file tm_source_file.h
12 The TMSourceFile structure and associated functions are used to maintain
13 tags for individual files.
22 #include <glib/gstdio.h>
25 # define WIN32_LEAN_AND_MEAN
26 # include <windows.h> /* for GetFullPathName */
34 #define LIBCTAGS_DEFINED
35 #include "tm_source_file.h"
37 #include "tm_parser.h"
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
)
55 int path_max
= pathconf(path
, _PC_PATH_MAX
);
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
)
71 if (resolved_path
!= NULL
)
73 int path_max
= get_path_max(pathname
);
74 size
= GetFullPathNameA (pathname
, path_max
, resolved_path
, NULL
);
82 size
= GetFullPathNameA (pathname
, 0, NULL
, NULL
);
83 resolved_path
= g_new0 (char, size
);
84 GetFullPathNameA (pathname
, size
, resolved_path
, NULL
);
91 Given a file name, returns a newly allocated string containing the realpath()
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.
97 gchar
*tm_get_real_path(const gchar
*file_name
)
101 gsize len
= get_path_max(file_name
) + 1;
102 gchar
*path
= g_malloc0(len
);
104 if (realpath(file_name
, path
))
112 /* add argument list of __init__() Python methods to the class tag */
113 static void update_python_arglist(const TMTag
*tag
)
116 const char *parent_tag_name
;
118 if (tag
->type
!= tm_tag_method_t
|| tag
->scope
== NULL
||
119 g_strcmp0(tag
->name
, "__init__") != 0)
122 parent_tag_name
= strrchr(tag
->scope
, '.');
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
);
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
)
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
);
162 void tm_source_file_ctags_init()
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
,
177 g_message("Source File init: %s", file_name
);
180 if (file_name
!= NULL
)
182 status
= g_stat(file_name
, &s
);
185 /* g_warning("Unable to stat %s", file_name);*/
188 if (!S_ISREG(s
.st_mode
))
190 g_warning("%s: Not a regular file", file_name
);
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
;
198 source_file
->short_name
= source_file
->file_name
;
201 source_file
->tags_array
= g_ptr_array_new();
204 source_file
->lang
= TM_PARSER_NONE
;
206 source_file
->lang
= getNamedLanguage(name
);
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.
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
);
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
);
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
)
250 g_message("Destroying source file: %s", source_file
->file_name
);
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
263 * @param source_file The source file to free.
264 * @see tm_workspace_remove_source_file()
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 . */
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
,
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");
308 if (source_file
->lang
== TM_PARSER_NONE
)
310 tm_tags_array_free(source_file
->tags_array
, FALSE
);
314 file_name
= source_file
->file_name
;
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)
325 if (!g_file_get_contents(file_name
, (gchar
**)&text_buf
, (gsize
*)&buf_size
, NULL
))
327 g_warning("Unable to open %s", file_name
);
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
);
343 current_source_file
= source_file
;
344 if (! LanguageTable
[source_file
->lang
]->enabled
)
347 g_warning("ignoring %s (language disabled)\n", file_name
);
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 ();
365 else if (LanguageTable
[source_file
->lang
]->parser2
!= NULL
)
366 retry
= LanguageTable
[source_file
->lang
]->parser2 (passCount
);
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 ();
378 else if (LanguageTable
[source_file
->lang
]->parser2
!= NULL
)
379 retry
= LanguageTable
[source_file
->lang
]->parser2 (passCount
);
384 g_warning("Unable to open %s", file_name
);
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
);
416 Writes all tags of a source file (including the file tag itself) to the passed
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
)
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
);
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
))