Rename pointerOrder TMtag member to flags
[geany-mirror.git] / src / tagmanager / tm_source_file.c
blob329f4fc117ed1828bbe23873a4c132905ef8363c
1 /*
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.
9 */
11 /**
12 * @file tm_source_file.h
13 The TMSourceFile structure and associated functions are used to maintain
14 tags for individual files.
18 #include <stdio.h>
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <glib/gstdio.h>
26 #ifdef G_OS_WIN32
27 # define VC_EXTRALEAN
28 # define WIN32_LEAN_AND_MEAN
29 # include <windows.h> /* for GetFullPathName */
30 #endif
32 #include "tm_source_file.h"
33 #include "tm_tag.h"
34 #include "tm_parser.h"
35 #include "tm_ctags.h"
37 typedef struct
39 TMSourceFile public;
40 guint refcount;
41 } TMSourceFilePriv;
44 typedef enum {
45 TM_FILE_FORMAT_TAGMANAGER,
46 TM_FILE_FORMAT_PIPE,
47 TM_FILE_FORMAT_CTAGS
48 } TMFileFormat;
50 /* Note: To preserve binary compatibility, it is very important
51 that you only *append* to this list ! */
52 enum
54 TA_NAME = 200,
55 TA_LINE,
56 TA_LOCAL,
57 TA_POS, /* Obsolete */
58 TA_TYPE,
59 TA_ARGLIST,
60 TA_SCOPE,
61 TA_VARTYPE,
62 TA_INHERITS,
63 TA_TIME,
64 TA_ACCESS,
65 TA_IMPL,
66 TA_LANG,
67 TA_INACTIVE, /* Obsolete */
68 TA_FLAGS
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)
77 #ifdef PATH_MAX
78 return PATH_MAX;
79 #else
80 int path_max = pathconf(path, _PC_PATH_MAX);
81 if (path_max <= 0)
82 path_max = 4096;
83 return path_max;
84 #endif
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)
94 int size;
96 if (resolved_path != NULL)
98 int path_max = get_path_max(pathname);
99 size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
100 if (size > path_max)
101 return NULL;
102 else
103 return resolved_path;
105 else
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;
113 #endif
116 Given a file name, returns a newly allocated string containing the realpath()
117 of the file.
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()
123 GEANY_API_SYMBOL
124 gchar *tm_get_real_path(const gchar *file_name)
126 if (file_name)
128 gsize len = get_path_max(file_name) + 1;
129 gchar *path = g_malloc0(len);
131 if (realpath(file_name, path))
132 return path;
133 else
134 g_free(path);
136 return NULL;
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;
145 #ifdef TM_DEBUG
146 g_warning("Unknown implementation %s", impl);
147 #endif
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;
164 #ifdef TM_DEBUG
165 g_warning("Unknown access type %s", access);
166 #endif
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)
180 guchar buf[BUFSIZ];
181 guchar *start, *end;
182 gboolean status;
183 guchar changed_char = TA_NAME;
185 tag->refcount = 1;
186 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
187 return FALSE;
188 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
190 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
191 ++ end;
192 if (('\0' == *end) || ('\n' == *end))
193 status = FALSE;
194 changed_char = *end;
195 *end = '\0';
196 if (NULL == tag->name)
198 if (!isprint(*start))
199 return FALSE;
200 else
201 tag->name = g_strdup((gchar*)start);
203 else
205 switch (*start)
207 case TA_LINE:
208 tag->line = atol((gchar*)start + 1);
209 break;
210 case TA_LOCAL:
211 tag->local = atoi((gchar*)start + 1);
212 break;
213 case TA_TYPE:
214 tag->type = (TMTagType) atoi((gchar*)start + 1);
215 break;
216 case TA_ARGLIST:
217 tag->arglist = g_strdup((gchar*)start + 1);
218 break;
219 case TA_SCOPE:
220 tag->scope = g_strdup((gchar*)start + 1);
221 break;
222 case TA_FLAGS:
223 tag->flags = atoi((gchar*)start + 1);
224 break;
225 case TA_VARTYPE:
226 tag->var_type = g_strdup((gchar*)start + 1);
227 break;
228 case TA_INHERITS:
229 tag->inheritance = g_strdup((gchar*)start + 1);
230 break;
231 case TA_TIME: /* Obsolete */
232 break;
233 case TA_LANG: /* Obsolete */
234 break;
235 case TA_INACTIVE: /* Obsolete */
236 break;
237 case TA_ACCESS:
238 tag->access = (char) *(start + 1);
239 break;
240 case TA_IMPL:
241 tag->impl = (char) *(start + 1);
242 break;
243 default:
244 #ifdef GEANY_DEBUG
245 g_warning("Unknown attribute %s", start + 1);
246 #endif
247 break;
250 *end = changed_char;
252 if (NULL == tag->name)
253 return FALSE;
254 tag->file = file;
255 return TRUE;
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)
262 guchar buf[BUFSIZ];
263 guchar *start, *end;
264 gboolean status;
265 /*guchar changed_char = TA_NAME;*/
267 tag->refcount = 1;
268 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
269 return FALSE;
271 gchar **fields;
272 guint field_len;
273 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
275 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
276 ++ end;
277 if (('\0' == *end) || ('\n' == *end))
278 status = FALSE;
279 /*changed_char = *end;*/
280 *end = '\0';
281 if (NULL == tag->name && !isprint(*start))
282 return FALSE;
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;
292 g_strfreev(fields);
296 if (NULL == tag->name)
297 return FALSE;
298 tag->file = file;
299 return TRUE;
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)
307 gchar buf[BUFSIZ];
308 gchar *p, *tab;
310 tag->refcount = 1;
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))
315 return FALSE;
317 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
319 p = buf;
321 /* tag name */
322 if (! (tab = strchr(p, '\t')) || p == tab)
323 return FALSE;
324 tag->name = g_strndup(p, (gsize)(tab - p));
325 p = tab + 1;
327 /* tagfile, unused */
328 if (! (tab = strchr(p, '\t')))
330 g_free(tag->name);
331 tag->name = NULL;
332 return FALSE;
334 p = tab + 1;
335 /* Ex command, unused */
336 if (*p == '/' || *p == '?')
338 gchar c = *p;
339 for (++p; *p && *p != c; p++)
341 if (*p == '\\' && p[1])
342 p++;
345 else /* assume a line */
346 tag->line = atol(p);
347 tab = strstr(p, ";\"");
348 /* read extension fields */
349 if (tab)
351 p = tab + 2;
352 while (*p && *p != '\n' && *p != '\r')
354 gchar *end;
355 const gchar *key, *value = NULL;
357 /* skip leading tabulations */
358 while (*p && *p == '\t') p++;
359 /* find the separator (:) and end (\t) */
360 key = end = p;
361 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
363 if (*end == ':' && ! value)
365 *end = 0; /* terminate the key */
366 value = end + 1;
368 end++;
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);
380 else
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 */
400 g_free(tag->scope);
401 tag->scope = g_strdup(value);
403 else if (0 == strcmp(key, "file")) /* static (local) tag */
404 tag->local = TRUE;
405 else if (0 == strcmp(key, "signature")) /* arglist */
407 g_free(tag->arglist);
408 tag->arglist = g_strdup(value);
413 tag->file = file;
414 return TRUE;
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;
422 switch (format)
424 case TM_FILE_FORMAT_TAGMANAGER:
425 result = init_tag_from_file(tag, file, fp);
426 break;
427 case TM_FILE_FORMAT_PIPE:
428 result = init_tag_from_file_alt(tag, file, fp);
429 break;
430 case TM_FILE_FORMAT_CTAGS:
431 result = init_tag_from_file_ctags(tag, file, fp, mode);
432 break;
435 if (! result)
437 tm_tag_unref(tag);
438 return NULL;
440 tag->lang = mode;
441 return tag;
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"))
476 return TRUE;
477 else
478 return FALSE;
481 GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode)
483 guchar buf[BUFSIZ];
484 FILE *fp;
485 GPtrArray *file_tags;
486 TMTag *tag;
487 TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
489 if (NULL == (fp = g_fopen(tags_file, "r")))
490 return NULL;
491 if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf))
493 fclose(fp);
494 return NULL; /* early out on error */
496 else
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;
506 else
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++)
513 if (buf[i] == '|')
514 pipe_cnt++;
515 else if (buf[i] == '\t')
516 tab_cnt++;
518 if (pipe_cnt > 1)
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 */
523 rewind(fp);
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);
530 fclose(fp);
532 return file_tags;
535 gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array)
537 guint i;
538 FILE *fp;
539 gboolean ret = TRUE;
541 g_return_val_if_fail(tags_array && tags_file, FALSE);
543 fp = g_fopen(tags_file, "w");
544 if (!fp)
545 return FALSE;
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);
556 if (!ret)
557 break;
559 fclose(fp);
561 return ret;
565 /* Initializes a TMSourceFile structure from a file name. */
566 static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
567 const char* name)
569 GStatBuf s;
570 int status;
572 #ifdef TM_DEBUG
573 g_message("Source File init: %s", file_name);
574 #endif
576 if (file_name != NULL)
578 status = g_stat(file_name, &s);
579 if (0 != status)
581 /* g_warning("Unable to stat %s", file_name);*/
582 return FALSE;
584 if (!S_ISREG(s.st_mode))
586 g_warning("%s: Not a regular file", file_name);
587 return FALSE;
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;
593 else
594 source_file->short_name = source_file->file_name;
597 source_file->tags_array = g_ptr_array_new();
599 if (name == NULL)
600 source_file->lang = TM_PARSER_NONE;
601 else
602 source_file->lang = tm_ctags_get_named_lang(name);
604 return TRUE;
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.
612 * */
613 GEANY_API_SYMBOL
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);
622 return NULL;
624 priv->refcount = 1;
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);
636 return source_file;
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)
645 #ifdef TM_DEBUG
646 g_message("Destroying source file: %s", source_file->file_name);
647 #endif
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
658 * this happens.
659 * @param source_file The source file to free.
660 * @see tm_workspace_remove_source_file()
662 GEANY_API_SYMBOL
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 . */
677 GEANY_API_SYMBOL
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,
691 gboolean use_buffer)
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");
699 return FALSE;
702 if (source_file->lang == TM_PARSER_NONE)
704 tm_tags_array_free(source_file->tags_array, FALSE);
705 return 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);
714 return TRUE;
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);
722 return !retry;
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);