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"
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
)
54 int path_max
= pathconf(path
, _PC_PATH_MAX
);
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
)
70 if (resolved_path
!= NULL
)
72 int path_max
= get_path_max(pathname
);
73 size
= GetFullPathNameA (pathname
, path_max
, resolved_path
, NULL
);
81 size
= GetFullPathNameA (pathname
, 0, NULL
, NULL
);
82 resolved_path
= g_new0 (char, size
);
83 GetFullPathNameA (pathname
, size
, resolved_path
, NULL
);
90 Given a file name, returns a newly allocated string containing the realpath()
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.
96 gchar
*tm_get_real_path(const gchar
*file_name
)
100 gsize len
= get_path_max(file_name
) + 1;
101 gchar
*path
= g_malloc0(len
);
103 if (realpath(file_name
, path
))
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
)
121 g_ptr_array_add(current_source_file
->tags_array
,
122 tm_tag_new(current_source_file
, tag
));
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
)
131 if (NULL
== arglist
||
133 NULL
== current_source_file
)
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
);
151 /* Initializes a TMSourceFile structure from a file name. */
152 static gboolean
tm_source_file_init(TMSourceFile
*source_file
, const char *file_name
,
159 g_message("Source File init: %s", file_name
);
162 if (file_name
!= NULL
)
164 status
= g_stat(file_name
, &s
);
167 /* g_warning("Unable to stat %s", file_name);*/
170 if (!S_ISREG(s
.st_mode
))
172 g_warning("%s: Not a regular file", file_name
);
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
;
180 source_file
->short_name
= source_file
->file_name
;
183 source_file
->tags_array
= g_ptr_array_new();
185 if (NULL
== LanguageTable
)
188 installLanguageMapDefaults();
189 if (NULL
== TagEntryFunction
)
190 TagEntryFunction
= tm_source_file_tags
;
191 if (NULL
== TagEntrySetArglistFunction
)
192 TagEntrySetArglistFunction
= tm_source_file_set_tag_arglist
;
196 source_file
->lang
= LANG_AUTO
;
198 source_file
->lang
= getNamedLanguage(name
);
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.
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
);
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
);
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
)
242 g_message("Destroying source file: %s", source_file
->file_name
);
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
255 * @param source_file The source file to free.
256 * @see tm_workspace_remove_source_file()
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 . */
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
,
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");
300 if (source_file
->lang
== LANG_IGNORE
)
302 tm_tags_array_free(source_file
->tags_array
, FALSE
);
306 file_name
= source_file
->file_name
;
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)
317 if (!g_file_get_contents(file_name
, (gchar
**)&text_buf
, (gsize
*)&buf_size
, NULL
))
319 g_warning("Unable to open %s", file_name
);
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
);
335 if (NULL
== LanguageTable
)
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
)
350 g_warning("ignoring %s (unknown language)\n", file_name
);
353 else if (! LanguageTable
[source_file
->lang
]->enabled
)
356 g_warning("ignoring %s (language disabled)\n", file_name
);
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 ();
374 else if (LanguageTable
[source_file
->lang
]->parser2
!= NULL
)
375 retry
= LanguageTable
[source_file
->lang
]->parser2 (passCount
);
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 ();
387 else if (LanguageTable
[source_file
->lang
]->parser2
!= NULL
)
388 retry
= LanguageTable
[source_file
->lang
]->parser2 (passCount
);
393 g_warning("Unable to open %s", file_name
);
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
)
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
)
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
);
443 Writes all tags of a source file (including the file tag itself) to the passed
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
)
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
);
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
))