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.
23 #include <glib/gstdio.h>
26 # define WIN32_LEAN_AND_MEAN
27 # include <windows.h> /* for GetFullPathName */
30 #include "tm_source_file.h"
32 #include "tm_parser.h"
33 #include "tm_ctags_wrappers.h"
43 TM_FILE_FORMAT_TAGMANAGER
,
48 /* Note: To preserve binary compatibility, it is very important
49 that you only *append* to this list ! */
55 TA_POS
, /* Obsolete */
65 TA_INACTIVE
, /* Obsolete */
70 #define SOURCE_FILE_NEW(S) ((S) = g_slice_new(TMSourceFilePriv))
71 #define SOURCE_FILE_FREE(S) g_slice_free(TMSourceFilePriv, (TMSourceFilePriv *) S)
73 static int get_path_max(const char *path
)
78 int path_max
= pathconf(path
, _PC_PATH_MAX
);
87 /* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
88 * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
89 * with special chars within the filename */
90 static char *realpath (const char *pathname
, char *resolved_path
)
94 if (resolved_path
!= NULL
)
96 int path_max
= get_path_max(pathname
);
97 size
= GetFullPathNameA (pathname
, path_max
, resolved_path
, NULL
);
101 return resolved_path
;
105 size
= GetFullPathNameA (pathname
, 0, NULL
, NULL
);
106 resolved_path
= g_new0 (char, size
);
107 GetFullPathNameA (pathname
, size
, resolved_path
, NULL
);
108 return resolved_path
;
114 Given a file name, returns a newly allocated string containing the realpath()
116 @param file_name The original file_name
117 @return A newly allocated string containing the real path to the file. NULL if none is available.
120 gchar
*tm_get_real_path(const gchar
*file_name
)
124 gsize len
= get_path_max(file_name
) + 1;
125 gchar
*path
= g_malloc0(len
);
127 if (realpath(file_name
, path
))
135 static char get_tag_impl(const char *impl
)
137 if ((0 == strcmp("virtual", impl
))
138 || (0 == strcmp("pure virtual", impl
)))
139 return TAG_IMPL_VIRTUAL
;
142 g_warning("Unknown implementation %s", impl
);
144 return TAG_IMPL_UNKNOWN
;
147 static char get_tag_access(const char *access
)
149 if (0 == strcmp("public", access
))
150 return TAG_ACCESS_PUBLIC
;
151 else if (0 == strcmp("protected", access
))
152 return TAG_ACCESS_PROTECTED
;
153 else if (0 == strcmp("private", access
))
154 return TAG_ACCESS_PRIVATE
;
155 else if (0 == strcmp("friend", access
))
156 return TAG_ACCESS_FRIEND
;
157 else if (0 == strcmp("default", access
))
158 return TAG_ACCESS_DEFAULT
;
161 g_warning("Unknown access type %s", access
);
163 return TAG_ACCESS_UNKNOWN
;
167 Initializes a TMTag structure with information from a tagEntryInfo struct
168 used by the ctags parsers. Note that the TMTag structure must be malloc()ed
169 before calling this function. This function is called by tm_tag_new() - you
170 should not need to call this directly.
171 @param tag The TMTag structure to initialize
172 @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
173 @param tag_entry Tag information gathered by the ctags parser
174 @return TRUE on success, FALSE on failure
176 static gboolean
init_tag(TMTag
*tag
, TMSourceFile
*file
, const tagEntryInfo
*tag_entry
)
183 type
= tm_parser_get_tag_type(tag_entry
->kind
, file
->lang
);
184 if (!tag_entry
->name
|| type
== tm_tag_undef_t
)
187 tag
->name
= g_strdup(tag_entry
->name
);
189 tag
->local
= tag_entry
->isFileScope
;
190 tag
->pointerOrder
= 0; /* backward compatibility (use var_type instead) */
191 tag
->line
= tag_entry
->lineNumber
;
192 if (NULL
!= tag_entry
->extensionFields
.arglist
)
193 tag
->arglist
= g_strdup(tag_entry
->extensionFields
.arglist
);
194 if ((NULL
!= tag_entry
->extensionFields
.scope
[1]) &&
195 (0 != tag_entry
->extensionFields
.scope
[1][0]))
196 tag
->scope
= g_strdup(tag_entry
->extensionFields
.scope
[1]);
197 if (tag_entry
->extensionFields
.inheritance
!= NULL
)
198 tag
->inheritance
= g_strdup(tag_entry
->extensionFields
.inheritance
);
199 if (tag_entry
->extensionFields
.varType
!= NULL
)
200 tag
->var_type
= g_strdup(tag_entry
->extensionFields
.varType
);
201 if (tag_entry
->extensionFields
.access
!= NULL
)
202 tag
->access
= get_tag_access(tag_entry
->extensionFields
.access
);
203 if (tag_entry
->extensionFields
.implementation
!= NULL
)
204 tag
->impl
= get_tag_impl(tag_entry
->extensionFields
.implementation
);
205 if ((tm_tag_macro_t
== tag
->type
) && (NULL
!= tag
->arglist
))
206 tag
->type
= tm_tag_macro_with_arg_t
;
208 tag
->lang
= file
->lang
;
213 Initializes an already malloc()ed TMTag structure by reading a tag entry
214 line from a file. The structure should be allocated beforehand.
215 @param tag The TMTag structure to populate
216 @param file The TMSourceFile struct (assigned to the file member)
217 @param fp FILE pointer from where the tag line is read
218 @return TRUE on success, FALSE on FAILURE
220 static gboolean
init_tag_from_file(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
225 guchar changed_char
= TA_NAME
;
228 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
230 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
232 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
234 if (('\0' == *end
) || ('\n' == *end
))
238 if (NULL
== tag
->name
)
240 if (!isprint(*start
))
243 tag
->name
= g_strdup((gchar
*)start
);
250 tag
->line
= atol((gchar
*)start
+ 1);
253 tag
->local
= atoi((gchar
*)start
+ 1);
256 tag
->type
= (TMTagType
) atoi((gchar
*)start
+ 1);
259 tag
->arglist
= g_strdup((gchar
*)start
+ 1);
262 tag
->scope
= g_strdup((gchar
*)start
+ 1);
265 tag
->pointerOrder
= atoi((gchar
*)start
+ 1);
268 tag
->var_type
= g_strdup((gchar
*)start
+ 1);
271 tag
->inheritance
= g_strdup((gchar
*)start
+ 1);
273 case TA_TIME
: /* Obsolete */
275 case TA_LANG
: /* Obsolete */
277 case TA_INACTIVE
: /* Obsolete */
280 tag
->access
= (char) *(start
+ 1);
283 tag
->impl
= (char) *(start
+ 1);
287 g_warning("Unknown attribute %s", start
+ 1);
294 if (NULL
== tag
->name
)
300 /* alternative parser for Pascal and LaTeX global tags files with the following format
301 * tagname|return value|arglist|description\n */
302 static gboolean
init_tag_from_file_alt(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
)
307 /*guchar changed_char = TA_NAME;*/
310 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
315 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
317 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
319 if (('\0' == *end
) || ('\n' == *end
))
321 /*changed_char = *end;*/
323 if (NULL
== tag
->name
&& !isprint(*start
))
326 fields
= g_strsplit((gchar
*)start
, "|", -1);
327 field_len
= g_strv_length(fields
);
329 if (field_len
>= 1) tag
->name
= g_strdup(fields
[0]);
330 else tag
->name
= NULL
;
331 if (field_len
>= 2 && fields
[1] != NULL
) tag
->var_type
= g_strdup(fields
[1]);
332 if (field_len
>= 3 && fields
[2] != NULL
) tag
->arglist
= g_strdup(fields
[2]);
333 tag
->type
= tm_tag_prototype_t
;
338 if (NULL
== tag
->name
)
345 CTags tag file format (http://ctags.sourceforge.net/FORMAT)
347 static gboolean
init_tag_from_file_ctags(TMTag
*tag
, TMSourceFile
*file
, FILE *fp
, TMParserType lang
)
353 tag
->type
= tm_tag_function_t
; /* default type is function if no kind is specified */
356 if ((NULL
== fgets(buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
359 while (strncmp(buf
, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
364 if (! (tab
= strchr(p
, '\t')) || p
== tab
)
366 tag
->name
= g_strndup(p
, (gsize
)(tab
- p
));
369 /* tagfile, unused */
370 if (! (tab
= strchr(p
, '\t')))
377 /* Ex command, unused */
378 if (*p
== '/' || *p
== '?')
381 for (++p
; *p
&& *p
!= c
; p
++)
383 if (*p
== '\\' && p
[1])
387 else /* assume a line */
389 tab
= strstr(p
, ";\"");
390 /* read extension fields */
394 while (*p
&& *p
!= '\n' && *p
!= '\r')
397 const gchar
*key
, *value
= NULL
;
399 /* skip leading tabulations */
400 while (*p
&& *p
== '\t') p
++;
401 /* find the separator (:) and end (\t) */
403 while (*end
&& *end
!= '\t' && *end
!= '\n' && *end
!= '\r')
405 if (*end
== ':' && ! value
)
407 *end
= 0; /* terminate the key */
412 /* move p paste the so we won't stop parsing by setting *end=0 below */
413 p
= *end
? end
+ 1 : end
;
414 *end
= 0; /* terminate the value (or key if no value) */
416 if (! value
|| 0 == strcmp(key
, "kind")) /* tag kind */
418 const gchar
*kind
= value
? value
: key
;
420 if (kind
[0] && kind
[1])
421 tag
->type
= tm_parser_get_tag_type(tm_ctags_get_kind_from_name(kind
, lang
), lang
);
423 tag
->type
= tm_parser_get_tag_type(*kind
, lang
);
425 else if (0 == strcmp(key
, "inherits")) /* comma-separated list of classes this class inherits from */
427 g_free(tag
->inheritance
);
428 tag
->inheritance
= g_strdup(value
);
430 else if (0 == strcmp(key
, "implementation")) /* implementation limit */
431 tag
->impl
= get_tag_impl(value
);
432 else if (0 == strcmp(key
, "line")) /* line */
433 tag
->line
= atol(value
);
434 else if (0 == strcmp(key
, "access")) /* access */
435 tag
->access
= get_tag_access(value
);
436 else if (0 == strcmp(key
, "class") ||
437 0 == strcmp(key
, "enum") ||
438 0 == strcmp(key
, "function") ||
439 0 == strcmp(key
, "struct") ||
440 0 == strcmp(key
, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
443 tag
->scope
= g_strdup(value
);
445 else if (0 == strcmp(key
, "file")) /* static (local) tag */
447 else if (0 == strcmp(key
, "signature")) /* arglist */
449 g_free(tag
->arglist
);
450 tag
->arglist
= g_strdup(value
);
459 static TMTag
*new_tag_from_tags_file(TMSourceFile
*file
, FILE *fp
, TMParserType mode
, TMFileFormat format
)
461 TMTag
*tag
= tm_tag_new();
462 gboolean result
= FALSE
;
466 case TM_FILE_FORMAT_TAGMANAGER
:
467 result
= init_tag_from_file(tag
, file
, fp
);
469 case TM_FILE_FORMAT_PIPE
:
470 result
= init_tag_from_file_alt(tag
, file
, fp
);
472 case TM_FILE_FORMAT_CTAGS
:
473 result
= init_tag_from_file_ctags(tag
, file
, fp
, mode
);
487 Writes tag information to the given FILE *.
488 @param tag The tag information to write.
489 @param file FILE pointer to which the tag information is written.
490 @param attrs Attributes to be written (bitmask).
491 @return TRUE on success, FALSE on failure.
493 static gboolean
write_tag(TMTag
*tag
, FILE *fp
, TMTagAttrType attrs
)
495 fprintf(fp
, "%s", tag
->name
);
496 if (attrs
& tm_tag_attr_type_t
)
497 fprintf(fp
, "%c%d", TA_TYPE
, tag
->type
);
498 if ((attrs
& tm_tag_attr_arglist_t
) && (NULL
!= tag
->arglist
))
499 fprintf(fp
, "%c%s", TA_ARGLIST
, tag
->arglist
);
500 if (attrs
& tm_tag_attr_line_t
)
501 fprintf(fp
, "%c%ld", TA_LINE
, tag
->line
);
502 if (attrs
& tm_tag_attr_local_t
)
503 fprintf(fp
, "%c%d", TA_LOCAL
, tag
->local
);
504 if ((attrs
& tm_tag_attr_scope_t
) && (NULL
!= tag
->scope
))
505 fprintf(fp
, "%c%s", TA_SCOPE
, tag
->scope
);
506 if ((attrs
& tm_tag_attr_inheritance_t
) && (NULL
!= tag
->inheritance
))
507 fprintf(fp
, "%c%s", TA_INHERITS
, tag
->inheritance
);
508 if (attrs
& tm_tag_attr_pointer_t
)
509 fprintf(fp
, "%c%d", TA_POINTER
, tag
->pointerOrder
);
510 if ((attrs
& tm_tag_attr_vartype_t
) && (NULL
!= tag
->var_type
))
511 fprintf(fp
, "%c%s", TA_VARTYPE
, tag
->var_type
);
512 if ((attrs
& tm_tag_attr_access_t
) && (TAG_ACCESS_UNKNOWN
!= tag
->access
))
513 fprintf(fp
, "%c%c", TA_ACCESS
, tag
->access
);
514 if ((attrs
& tm_tag_attr_impl_t
) && (TAG_IMPL_UNKNOWN
!= tag
->impl
))
515 fprintf(fp
, "%c%c", TA_IMPL
, tag
->impl
);
517 if (fprintf(fp
, "\n"))
523 GPtrArray
*tm_source_file_read_tags_file(const gchar
*tags_file
, TMParserType mode
)
527 GPtrArray
*file_tags
, *new_tags
;
529 TMFileFormat format
= TM_FILE_FORMAT_TAGMANAGER
;
531 if (NULL
== (fp
= g_fopen(tags_file
, "r")))
533 if ((NULL
== fgets((gchar
*) buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
536 return NULL
; /* early out on error */
539 { /* We read the first line for the format specification. */
540 if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=pipe") != NULL
)
541 format
= TM_FILE_FORMAT_PIPE
;
542 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=tagmanager") != NULL
)
543 format
= TM_FILE_FORMAT_TAGMANAGER
;
544 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=ctags") != NULL
)
545 format
= TM_FILE_FORMAT_CTAGS
;
546 else if (strncmp((gchar
*) buf
, "!_TAG_", 6) == 0)
547 format
= TM_FILE_FORMAT_CTAGS
;
549 { /* We didn't find a valid format specification, so we try to auto-detect the format
550 * by counting the pipe characters on the first line and asumme pipe format when
551 * we find more than one pipe on the line. */
552 guint i
, pipe_cnt
= 0, tab_cnt
= 0;
553 for (i
= 0; i
< BUFSIZ
&& buf
[i
] != '\0' && pipe_cnt
< 2; i
++)
557 else if (buf
[i
] == '\t')
561 format
= TM_FILE_FORMAT_PIPE
;
562 else if (tab_cnt
> 1)
563 format
= TM_FILE_FORMAT_CTAGS
;
565 rewind(fp
); /* reset the file pointer, to start reading again from the beginning */
568 file_tags
= g_ptr_array_new();
569 while (NULL
!= (tag
= new_tag_from_tags_file(NULL
, fp
, mode
, format
)))
570 g_ptr_array_add(file_tags
, tag
);
576 gboolean
tm_source_file_write_tags_file(const gchar
*tags_file
, GPtrArray
*tags_array
)
581 g_return_val_if_fail(tags_array
&& tags_file
, FALSE
);
583 fp
= g_fopen(tags_file
, "w");
587 fprintf(fp
, "# format=tagmanager\n");
588 for (i
= 0; i
< tags_array
->len
; i
++)
590 TMTag
*tag
= TM_TAG(tags_array
->pdata
[i
]);
592 write_tag(tag
, fp
, tm_tag_attr_type_t
593 | tm_tag_attr_scope_t
| tm_tag_attr_arglist_t
| tm_tag_attr_vartype_t
594 | tm_tag_attr_pointer_t
);
601 /* add argument list of __init__() Python methods to the class tag */
602 static void update_python_arglist(const TMTag
*tag
, TMSourceFile
*current_source_file
)
605 const char *parent_tag_name
;
607 if (tag
->type
!= tm_tag_method_t
|| tag
->scope
== NULL
||
608 g_strcmp0(tag
->name
, "__init__") != 0)
611 parent_tag_name
= strrchr(tag
->scope
, '.');
615 parent_tag_name
= tag
->scope
;
617 /* going in reverse order because the tag was added recently */
618 for (i
= current_source_file
->tags_array
->len
; i
> 0; i
--)
620 TMTag
*prev_tag
= (TMTag
*) current_source_file
->tags_array
->pdata
[i
- 1];
621 if (g_strcmp0(prev_tag
->name
, parent_tag_name
) == 0)
623 g_free(prev_tag
->arglist
);
624 prev_tag
->arglist
= g_strdup(tag
->arglist
);
631 This function is registered into the ctags parser when a file is parsed for
632 the first time. The function is then called by the ctags parser each time
633 it finds a new tag. You should not have to use this function.
634 @see tm_source_file_parse()
636 static gboolean
tm_source_file_tags(const tagEntryInfo
*const tag
,
637 gboolean invalidate
, void *user_data
)
639 TMSourceFile
*current_source_file
= user_data
;
644 tm_tags_array_free(current_source_file
->tags_array
, FALSE
);
648 tm_tag
= tm_tag_new();
650 if (!init_tag(tm_tag
, current_source_file
, tag
))
652 tm_tag_unref(tm_tag
);
656 if (tm_tag
->lang
== TM_PARSER_PYTHON
)
657 update_python_arglist(tm_tag
, current_source_file
);
659 g_ptr_array_add(current_source_file
->tags_array
, tm_tag
);
664 void tm_source_file_ctags_init(void)
667 tm_parser_verify_type_mappings();
670 /* Initializes a TMSourceFile structure from a file name. */
671 static gboolean
tm_source_file_init(TMSourceFile
*source_file
, const char *file_name
,
678 g_message("Source File init: %s", file_name
);
681 if (file_name
!= NULL
)
683 status
= g_stat(file_name
, &s
);
686 /* g_warning("Unable to stat %s", file_name);*/
689 if (!S_ISREG(s
.st_mode
))
691 g_warning("%s: Not a regular file", file_name
);
694 source_file
->file_name
= tm_get_real_path(file_name
);
695 source_file
->short_name
= strrchr(source_file
->file_name
, '/');
696 if (source_file
->short_name
)
697 ++ source_file
->short_name
;
699 source_file
->short_name
= source_file
->file_name
;
702 source_file
->tags_array
= g_ptr_array_new();
705 source_file
->lang
= TM_PARSER_NONE
;
707 source_file
->lang
= tm_ctags_get_named_lang(name
);
712 /** Initializes a TMSourceFile structure and returns a pointer to it. The
713 * TMSourceFile has to be added to TMWorkspace to start its parsing.
714 * @param file_name The file name.
715 * @param name Name of the used programming language, NULL to disable parsing.
716 * @return The created unparsed TMSourceFile object.
719 TMSourceFile
*tm_source_file_new(const char *file_name
, const char *name
)
721 TMSourceFilePriv
*priv
;
723 SOURCE_FILE_NEW(priv
);
724 if (TRUE
!= tm_source_file_init(&priv
->public, file_name
, name
))
726 SOURCE_FILE_FREE(priv
);
730 return &priv
->public;
734 static TMSourceFile
*tm_source_file_dup(TMSourceFile
*source_file
)
736 TMSourceFilePriv
*priv
= (TMSourceFilePriv
*) source_file
;
738 g_return_val_if_fail(NULL
!= source_file
, NULL
);
740 g_atomic_int_inc(&priv
->refcount
);
744 /* Destroys the contents of the source file. Note that the tags are owned by the
745 source file and are also destroyed when the source file is destroyed. If pointers
746 to these tags are used elsewhere, then those tag arrays should be rebuilt.
748 static void tm_source_file_destroy(TMSourceFile
*source_file
)
751 g_message("Destroying source file: %s", source_file
->file_name
);
754 g_free(source_file
->file_name
);
755 tm_tags_array_free(source_file
->tags_array
, TRUE
);
756 source_file
->tags_array
= NULL
;
759 /** Decrements the reference count of @a source_file
761 * If the reference count drops to 0, then @a source_file is freed, including all contents.
762 * Make sure the @a source_file is already removed from any TMWorkSpace before the
764 * @param source_file The source file to free.
765 * @see tm_workspace_remove_source_file()
768 void tm_source_file_free(TMSourceFile
*source_file
)
770 TMSourceFilePriv
*priv
= (TMSourceFilePriv
*) source_file
;
772 if (NULL
!= priv
&& g_atomic_int_dec_and_test(&priv
->refcount
))
774 tm_source_file_destroy(source_file
);
775 SOURCE_FILE_FREE(priv
);
779 /** Gets the GBoxed-derived GType for TMSourceFile
781 * @return TMSourceFile type . */
783 GType
tm_source_file_get_type(void);
785 G_DEFINE_BOXED_TYPE(TMSourceFile
, tm_source_file
, tm_source_file_dup
, tm_source_file_free
);
787 /* Parses the text-buffer or source file and regenarates the tags.
788 @param source_file The source file to parse
789 @param text_buf The text buffer to parse
790 @param buf_size The size of text_buf.
791 @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
792 TRUE to parse the buffer and ignore the file content.
793 @return TRUE on success, FALSE on failure
795 gboolean
tm_source_file_parse(TMSourceFile
*source_file
, guchar
* text_buf
, gsize buf_size
,
798 const char *file_name
;
799 gboolean retry
= TRUE
;
800 gboolean parse_file
= FALSE
;
801 gboolean free_buf
= FALSE
;
803 if ((NULL
== source_file
) || (NULL
== source_file
->file_name
))
805 g_warning("Attempt to parse NULL file");
809 if (source_file
->lang
== TM_PARSER_NONE
)
811 tm_tags_array_free(source_file
->tags_array
, FALSE
);
815 file_name
= source_file
->file_name
;
821 /* load file to memory and parse it from memory unless the file is too big */
822 if (g_stat(file_name
, &s
) != 0 || s
.st_size
> 10*1024*1024)
826 if (!g_file_get_contents(file_name
, (gchar
**)&text_buf
, (gsize
*)&buf_size
, NULL
))
828 g_warning("Unable to open %s", file_name
);
835 if (!parse_file
&& (NULL
== text_buf
|| 0 == buf_size
))
837 /* Empty buffer, "parse" by setting empty tag array */
838 tm_tags_array_free(source_file
->tags_array
, FALSE
);
844 tm_tags_array_free(source_file
->tags_array
, FALSE
);
846 tm_ctags_parse(parse_file
? NULL
: text_buf
, buf_size
, file_name
,
847 source_file
->lang
, tm_source_file_tags
, source_file
);
854 /* Gets the name associated with the language index.
855 @param lang The language index.
856 @return The language name, or NULL.
858 const gchar
*tm_source_file_get_lang_name(TMParserType lang
)
860 return tm_ctags_get_lang_name(lang
);
863 /* Gets the language index for \a name.
864 @param name The language name.
865 @return The language index, or TM_PARSER_NONE.
867 TMParserType
tm_source_file_get_named_lang(const gchar
*name
)
869 return tm_ctags_get_named_lang(name
);