3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
4 * Copyright 2005 The Geany contributors
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
12 * @file tm_source_file.h
13 The TMSourceFile structure and associated functions are used to maintain
14 tags for individual files.
25 #include <glib/gstdio.h>
28 # define WIN32_LEAN_AND_MEAN
29 # include <windows.h> /* for GetFullPathName */
32 #include "tm_source_file.h"
34 #include "tm_parser.h"
45 TM_FILE_FORMAT_TAGMANAGER
,
50 /* Note: To preserve binary compatibility, it is very important
51 that you only *append* to this list ! */
57 TA_POS
, /* Obsolete */
67 TA_INACTIVE
, /* Obsolete */
72 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
73 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
75 static int get_path_max(const char *path
)
80 int path_max
= pathconf(path
, _PC_PATH_MAX
);
88 #if defined(G_OS_WIN32) && !defined(HAVE_REALPATH)
89 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
90 * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
91 * with special chars within the filename */
92 static char *realpath (const char *pathname
, char *resolved_path
)
96 if (resolved_path
!= NULL
)
98 int path_max
= get_path_max(pathname
);
99 size
= GetFullPathNameA (pathname
, path_max
, resolved_path
, NULL
);
103 return resolved_path
;
107 size
= GetFullPathNameA (pathname
, 0, NULL
, NULL
);
108 resolved_path
= g_new0 (char, size
);
109 GetFullPathNameA (pathname
, size
, resolved_path
, NULL
);
110 return resolved_path
;
116 Given a file name, returns a newly allocated string containing the realpath()
118 @param file_name The original file_name
119 @return A newly allocated string containing the real path to the file. NULL if none is available.
120 @deprecated since 1.32 (ABI 235)
121 @see utils_get_real_path()
124 gchar
*tm_get_real_path(const gchar
*file_name
)
128 gsize len
= get_path_max(file_name
) + 1;
129 gchar
*path
= g_malloc0(len
);
131 if (realpath(file_name
, path
))
139 gchar
tm_source_file_get_tag_impl(const gchar
*impl
)
141 if ((0 == strcmp("virtual", impl
))
142 || (0 == strcmp("pure virtual", impl
)))
143 return TAG_IMPL_VIRTUAL
;
146 g_warning("Unknown implementation %s", impl
);
148 return TAG_IMPL_UNKNOWN
;
151 gchar
tm_source_file_get_tag_access(const gchar
*access
)
153 if (0 == strcmp("public", access
))
154 return TAG_ACCESS_PUBLIC
;
155 else if (0 == strcmp("protected", access
))
156 return TAG_ACCESS_PROTECTED
;
157 else if (0 == strcmp("private", access
))
158 return TAG_ACCESS_PRIVATE
;
159 else if (0 == strcmp("friend", access
))
160 return TAG_ACCESS_FRIEND
;
161 else if (0 == strcmp("default", access
))
162 return TAG_ACCESS_DEFAULT
;
165 g_warning("Unknown access type %s", access
);
167 return TAG_ACCESS_UNKNOWN
;
171 Initializes an already malloc()ed TMTag structure by reading a tag entry
172 line from a file. The structure should be allocated beforehand.
173 @param tag The TMTag structure to populate
174 @param file The TMSourceFile struct (assigned to the file member)
175 @param fp FILE pointer from where the tag line is read
176 @return TRUE on success, FALSE on FAILURE
178 static gboolean
init_tag_from_file(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
183 guchar changed_char
= TA_NAME
;
186 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
188 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
190 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
192 if (('\0' == *end
) || ('\n' == *end
))
196 if (NULL
== tag
->name
)
198 if (!isprint(*start
))
201 tag
->name
= g_strdup((gchar
*)start
);
208 tag
->line
= atol((gchar
*)start
+ 1);
211 tag
->local
= atoi((gchar
*)start
+ 1);
214 tag
->type
= (TMTagType
) atoi((gchar
*)start
+ 1);
217 tag
->arglist
= g_strdup((gchar
*)start
+ 1);
220 tag
->scope
= g_strdup((gchar
*)start
+ 1);
223 tag
->flags
= atoi((gchar
*)start
+ 1);
226 tag
->var_type
= g_strdup((gchar
*)start
+ 1);
229 tag
->inheritance
= g_strdup((gchar
*)start
+ 1);
231 case TA_TIME
: /* Obsolete */
233 case TA_LANG
: /* Obsolete */
235 case TA_INACTIVE
: /* Obsolete */
238 tag
->access
= (char) *(start
+ 1);
241 tag
->impl
= (char) *(start
+ 1);
245 g_warning("Unknown attribute %s", start
+ 1);
252 if (NULL
== tag
->name
)
258 /* alternative parser for Pascal and LaTeX global tags files with the following format
259 * tagname|return value|arglist|description\n */
260 static gboolean
init_tag_from_file_alt(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
265 /*guchar changed_char = TA_NAME;*/
268 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
273 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
275 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
277 if (('\0' == *end
) || ('\n' == *end
))
279 /*changed_char = *end;*/
281 if (NULL
== tag
->name
&& !isprint(*start
))
284 fields
= g_strsplit((gchar
*)start
, "|", -1);
285 field_len
= g_strv_length(fields
);
287 if (field_len
>= 1) tag
->name
= g_strdup(fields
[0]);
288 else tag
->name
= NULL
;
289 if (field_len
>= 2 && fields
[1] != NULL
) tag
->var_type
= g_strdup(fields
[1]);
290 if (field_len
>= 3 && fields
[2] != NULL
) tag
->arglist
= g_strdup(fields
[2]);
291 tag
->type
= tm_tag_prototype_t
;
296 if (NULL
== tag
->name
)
303 CTags tag file format (http://ctags.sourceforge.net/FORMAT)
305 static gboolean
init_tag_from_file_ctags(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
, TMParserType lang
)
311 tag
->type
= tm_tag_function_t
; /* default type is function if no kind is specified */
314 if ((NULL
== fgets(buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
317 while (strncmp(buf
, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
322 if (! (tab
= strchr(p
, '\t')) || p
== tab
)
324 tag
->name
= g_strndup(p
, (gsize
)(tab
- p
));
327 /* tagfile, unused */
328 if (! (tab
= strchr(p
, '\t')))
335 /* Ex command, unused */
336 if (*p
== '/' || *p
== '?')
339 for (++p
; *p
&& *p
!= c
; p
++)
341 if (*p
== '\\' && p
[1])
345 else /* assume a line */
347 tab
= strstr(p
, ";\"");
348 /* read extension fields */
352 while (*p
&& *p
!= '\n' && *p
!= '\r')
355 const gchar
*key
, *value
= NULL
;
357 /* skip leading tabulations */
358 while (*p
&& *p
== '\t') p
++;
359 /* find the separator (:) and end (\t) */
361 while (*end
&& *end
!= '\t' && *end
!= '\n' && *end
!= '\r')
363 if (*end
== ':' && ! value
)
365 *end
= 0; /* terminate the key */
370 /* move p paste the so we won't stop parsing by setting *end=0 below */
371 p
= *end
? end
+ 1 : end
;
372 *end
= 0; /* terminate the value (or key if no value) */
374 if (! value
|| 0 == strcmp(key
, "kind")) /* tag kind */
376 const gchar
*kind
= value
? value
: key
;
378 if (kind
[0] && kind
[1])
379 tag
->type
= tm_parser_get_tag_type(tm_ctags_get_kind_from_name(kind
, lang
), lang
);
381 tag
->type
= tm_parser_get_tag_type(*kind
, lang
);
383 else if (0 == strcmp(key
, "inherits")) /* comma-separated list of classes this class inherits from */
385 g_free(tag
->inheritance
);
386 tag
->inheritance
= g_strdup(value
);
388 else if (0 == strcmp(key
, "implementation")) /* implementation limit */
389 tag
->impl
= tm_source_file_get_tag_impl(value
);
390 else if (0 == strcmp(key
, "line")) /* line */
391 tag
->line
= atol(value
);
392 else if (0 == strcmp(key
, "access")) /* access */
393 tag
->access
= tm_source_file_get_tag_access(value
);
394 else if (0 == strcmp(key
, "class") ||
395 0 == strcmp(key
, "enum") ||
396 0 == strcmp(key
, "function") ||
397 0 == strcmp(key
, "struct") ||
398 0 == strcmp(key
, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
401 tag
->scope
= g_strdup(value
);
403 else if (0 == strcmp(key
, "file")) /* static (local) tag */
405 else if (0 == strcmp(key
, "signature")) /* arglist */
407 g_free(tag
->arglist
);
408 tag
->arglist
= g_strdup(value
);
417 static TMTag
*new_tag_from_tags_file(TMSourceFile
*file
, FILE *fp
, TMParserType mode
, TMFileFormat format
)
419 TMTag
*tag
= tm_tag_new();
420 gboolean result
= FALSE
;
424 case TM_FILE_FORMAT_TAGMANAGER
:
425 result
= init_tag_from_file(tag
, file
, fp
);
427 case TM_FILE_FORMAT_PIPE
:
428 result
= init_tag_from_file_alt(tag
, file
, fp
);
430 case TM_FILE_FORMAT_CTAGS
:
431 result
= init_tag_from_file_ctags(tag
, file
, fp
, mode
);
445 Writes tag information to the given FILE *.
446 @param tag The tag information to write.
447 @param file FILE pointer to which the tag information is written.
448 @param attrs Attributes to be written (bitmask).
449 @return TRUE on success, FALSE on failure.
451 static gboolean
write_tag(TMTag
*tag
, FILE *fp
, TMTagAttrType attrs
)
453 fprintf(fp
, "%s", tag
->name
);
454 if (attrs
& tm_tag_attr_type_t
)
455 fprintf(fp
, "%c%d", TA_TYPE
, tag
->type
);
456 if ((attrs
& tm_tag_attr_arglist_t
) && (NULL
!= tag
->arglist
))
457 fprintf(fp
, "%c%s", TA_ARGLIST
, tag
->arglist
);
458 if (attrs
& tm_tag_attr_line_t
)
459 fprintf(fp
, "%c%ld", TA_LINE
, tag
->line
);
460 if (attrs
& tm_tag_attr_local_t
)
461 fprintf(fp
, "%c%d", TA_LOCAL
, tag
->local
);
462 if ((attrs
& tm_tag_attr_scope_t
) && (NULL
!= tag
->scope
))
463 fprintf(fp
, "%c%s", TA_SCOPE
, tag
->scope
);
464 if ((attrs
& tm_tag_attr_inheritance_t
) && (NULL
!= tag
->inheritance
))
465 fprintf(fp
, "%c%s", TA_INHERITS
, tag
->inheritance
);
466 if (attrs
& tm_tag_attr_flags_t
)
467 fprintf(fp
, "%c%d", TA_FLAGS
, tag
->flags
);
468 if ((attrs
& tm_tag_attr_vartype_t
) && (NULL
!= tag
->var_type
))
469 fprintf(fp
, "%c%s", TA_VARTYPE
, tag
->var_type
);
470 if ((attrs
& tm_tag_attr_access_t
) && (TAG_ACCESS_UNKNOWN
!= tag
->access
))
471 fprintf(fp
, "%c%c", TA_ACCESS
, tag
->access
);
472 if ((attrs
& tm_tag_attr_impl_t
) && (TAG_IMPL_UNKNOWN
!= tag
->impl
))
473 fprintf(fp
, "%c%c", TA_IMPL
, tag
->impl
);
475 if (fprintf(fp
, "\n"))
481 GPtrArray
*tm_source_file_read_tags_file(const gchar
*tags_file
, TMParserType mode
)
485 GPtrArray
*file_tags
;
487 TMFileFormat format
= TM_FILE_FORMAT_TAGMANAGER
;
489 if (NULL
== (fp
= g_fopen(tags_file
, "r")))
491 if ((NULL
== fgets((gchar
*) buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
494 return NULL
; /* early out on error */
497 { /* We read (and discard) the first line for the format specification. */
498 if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=pipe") != NULL
)
499 format
= TM_FILE_FORMAT_PIPE
;
500 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=tagmanager") != NULL
)
501 format
= TM_FILE_FORMAT_TAGMANAGER
;
502 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=ctags") != NULL
)
503 format
= TM_FILE_FORMAT_CTAGS
;
504 else if (strncmp((gchar
*) buf
, "!_TAG_", 6) == 0)
505 format
= TM_FILE_FORMAT_CTAGS
;
507 { /* We didn't find a valid format specification, so we try to auto-detect the format
508 * by counting the pipe characters on the first line and asumme pipe format when
509 * we find more than one pipe on the line. */
510 guint i
, pipe_cnt
= 0, tab_cnt
= 0;
511 for (i
= 0; i
< BUFSIZ
&& buf
[i
] != '\0' && pipe_cnt
< 2; i
++)
515 else if (buf
[i
] == '\t')
519 format
= TM_FILE_FORMAT_PIPE
;
520 else if (tab_cnt
> 1)
521 format
= TM_FILE_FORMAT_CTAGS
;
522 /* reset the file pointer, to start reading again from the beginning */
527 file_tags
= g_ptr_array_new();
528 while (NULL
!= (tag
= new_tag_from_tags_file(NULL
, fp
, mode
, format
)))
529 g_ptr_array_add(file_tags
, tag
);
535 gboolean
tm_source_file_write_tags_file(const gchar
*tags_file
, GPtrArray
*tags_array
)
541 g_return_val_if_fail(tags_array
&& tags_file
, FALSE
);
543 fp
= g_fopen(tags_file
, "w");
547 fprintf(fp
, "# format=tagmanager\n");
548 for (i
= 0; i
< tags_array
->len
; i
++)
550 TMTag
*tag
= TM_TAG(tags_array
->pdata
[i
]);
552 ret
= write_tag(tag
, fp
, tm_tag_attr_type_t
553 | tm_tag_attr_scope_t
| tm_tag_attr_arglist_t
| tm_tag_attr_vartype_t
554 | tm_tag_attr_flags_t
);
565 /* Initializes a TMSourceFile structure from a file name. */
566 static gboolean
tm_source_file_init(TMSourceFile
*source_file
, const char *file_name
,
573 g_message("Source File init: %s", file_name
);
576 if (file_name
!= NULL
)
578 status
= g_stat(file_name
, &s
);
581 /* g_warning("Unable to stat %s", file_name);*/
584 if (!S_ISREG(s
.st_mode
))
586 g_warning("%s: Not a regular file", file_name
);
589 source_file
->file_name
= tm_get_real_path(file_name
);
590 source_file
->short_name
= strrchr(source_file
->file_name
, '/');
591 if (source_file
->short_name
)
592 ++ source_file
->short_name
;
594 source_file
->short_name
= source_file
->file_name
;
597 source_file
->tags_array
= g_ptr_array_new();
600 source_file
->lang
= TM_PARSER_NONE
;
602 source_file
->lang
= tm_ctags_get_named_lang(name
);
607 /** Initializes a TMSourceFile structure and returns a pointer to it. The
608 * TMSourceFile has to be added to TMWorkspace to start its parsing.
609 * @param file_name The file name.
610 * @param name Name of the used programming language, NULL to disable parsing.
611 * @return The created unparsed TMSourceFile object.
614 TMSourceFile
*tm_source_file_new(const char *file_name
, const char *name
)
616 TMSourceFilePriv
*priv
;
618 SOURCE_FILE_NEW(priv
);
619 if (TRUE
!= tm_source_file_init(&priv
->public, file_name
, name
))
621 SOURCE_FILE_FREE(priv
);
625 return &priv
->public;
629 static TMSourceFile
*tm_source_file_dup(TMSourceFile
*source_file
)
631 TMSourceFilePriv
*priv
= (TMSourceFilePriv
*) source_file
;
633 g_return_val_if_fail(NULL
!= source_file
, NULL
);
635 g_atomic_int_inc(&priv
->refcount
);
639 /* Destroys the contents of the source file. Note that the tags are owned by the
640 source file and are also destroyed when the source file is destroyed. If pointers
641 to these tags are used elsewhere, then those tag arrays should be rebuilt.
643 static void tm_source_file_destroy(TMSourceFile
*source_file
)
646 g_message("Destroying source file: %s", source_file
->file_name
);
649 g_free(source_file
->file_name
);
650 tm_tags_array_free(source_file
->tags_array
, TRUE
);
651 source_file
->tags_array
= NULL
;
654 /** Decrements the reference count of @a source_file
656 * If the reference count drops to 0, then @a source_file is freed, including all contents.
657 * Make sure the @a source_file is already removed from any TMWorkSpace before the
659 * @param source_file The source file to free.
660 * @see tm_workspace_remove_source_file()
663 void tm_source_file_free(TMSourceFile
*source_file
)
665 TMSourceFilePriv
*priv
= (TMSourceFilePriv
*) source_file
;
667 if (NULL
!= priv
&& g_atomic_int_dec_and_test(&priv
->refcount
))
669 tm_source_file_destroy(source_file
);
670 SOURCE_FILE_FREE(priv
);
674 /** Gets the GBoxed-derived GType for TMSourceFile
676 * @return TMSourceFile type . */
678 GType
tm_source_file_get_type(void);
680 G_DEFINE_BOXED_TYPE(TMSourceFile
, tm_source_file
, tm_source_file_dup
, tm_source_file_free
);
682 /* Parses the text-buffer or source file and regenarates the tags.
683 @param source_file The source file to parse
684 @param text_buf The text buffer to parse
685 @param buf_size The size of text_buf.
686 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
687 TRUE to parse the buffer and ignore the file content.
688 @return TRUE on success, FALSE on failure
690 gboolean
tm_source_file_parse(TMSourceFile
*source_file
, guchar
* text_buf
, gsize buf_size
,
693 const char *file_name
;
694 gboolean retry
= TRUE
;
696 if ((NULL
== source_file
) || (NULL
== source_file
->file_name
))
698 g_warning("Attempt to parse NULL file");
702 if (source_file
->lang
== TM_PARSER_NONE
)
704 tm_tags_array_free(source_file
->tags_array
, FALSE
);
708 file_name
= source_file
->file_name
;
710 if (use_buffer
&& (NULL
== text_buf
|| 0 == buf_size
))
712 /* Empty buffer, "parse" by setting empty tag array */
713 tm_tags_array_free(source_file
->tags_array
, FALSE
);
717 tm_tags_array_free(source_file
->tags_array
, FALSE
);
719 tm_ctags_parse(use_buffer
? text_buf
: NULL
, buf_size
, file_name
,
720 source_file
->lang
, source_file
);
725 /* Gets the name associated with the language index.
726 @param lang The language index.
727 @return The language name, or NULL.
729 const gchar
*tm_source_file_get_lang_name(TMParserType lang
)
731 return tm_ctags_get_lang_name(lang
);
734 /* Gets the language index for \a name.
735 @param name The language name.
736 @return The language index, or TM_PARSER_NONE.
738 TMParserType
tm_source_file_get_named_lang(const gchar
*name
)
740 return tm_ctags_get_named_lang(name
);