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.
15 #include <sys/types.h>
21 #define LIBCTAGS_DEFINED
23 #include "tm_workspace.h"
24 #include "tm_source_file.h"
25 #include "tm_file_entry.h"
26 #include "tm_project.h"
28 #define TM_FILE_NAME ".tm_project2.cache"
30 static const char *s_sources
[] = { "*.c", "*.pc" /* C/Pro*C files */
31 , "*.C", "*.cpp", "*.cc", "*.cxx", "*.c++" /* C++ files */
32 , "*.h", "*.hh", "*.hpp", "*.H", "*.h++", "*.i" /* Header files */
33 , "*.oaf", "*.gob", "*.idl" /* CORBA/Bonobo files */
34 , "*.l", "*.y" /* Lex/Yacc files */
36 , "*.ui", "*.moc" /* KDE/QT Files */
37 , "*.glade" /* UI files */
39 , "*.java", "*.pl", "*.pm", "*.py", "*.sh" /* Other languages */
40 , NULL
/* Must terminate with NULL */
43 static const char *s_ignore
[] = { "CVS", "intl", "po", NULL
};
45 static GList
*glist_from_array(const char **arr
)
49 for (i
=0; arr
[i
]; ++ i
)
50 l
= g_list_prepend(l
, (gpointer
) arr
[i
]);
51 return g_list_reverse(l
);
54 guint project_class_id
= 0;
56 gboolean
tm_project_init(TMProject
*project
, const char *dir
57 , const char **sources
, const char **ignore
, gboolean force
)
62 g_return_val_if_fail((project
&& dir
), FALSE
);
64 g_message("Initializing project %s", dir
);
66 if (0 == project_class_id
)
68 project_class_id
= tm_work_object_register(tm_project_free
, tm_project_update
69 , tm_project_find_file
);
72 if ((0 != stat(dir
, &s
)) || (!S_ISDIR(s
.st_mode
)))
74 g_warning("%s: Not a valid directory", dir
);
77 project
->dir
= tm_get_real_path(dir
);
79 project
->sources
= sources
;
81 project
->sources
= s_sources
;
83 project
->ignore
= ignore
;
85 project
->ignore
= s_ignore
;
86 project
->file_list
= NULL
;
87 g_snprintf(path
, PATH_MAX
, "%s/%s", project
->dir
, TM_FILE_NAME
);
88 if ((0 != stat(path
, &s
)) || (0 == s
.st_size
))
90 if (FALSE
== tm_work_object_init(&(project
->work_object
),
91 project_class_id
, path
, force
))
93 g_warning("Unable to init project file %s", path
);
97 tm_workspace_add_object(TM_WORK_OBJECT(project
));
98 tm_project_open(project
, force
);
99 if (!project
->file_list
|| (0 == project
->file_list
->len
))
100 tm_project_autoscan(project
);
107 TMWorkObject
*tm_project_new(const char *dir
, const char **sources
108 , const char **ignore
, gboolean force
)
110 TMProject
*project
= g_new(TMProject
, 1);
111 if (FALSE
== tm_project_init(project
, dir
, sources
, ignore
, force
))
116 return (TMWorkObject
*) project
;
119 void tm_project_destroy(TMProject
*project
)
121 g_return_if_fail (project
!= NULL
);
123 g_message("Destroying project: %s", project
->work_object
.file_name
);
126 if (project
->file_list
)
129 for (i
= 0; i
< project
->file_list
->len
; ++i
)
130 tm_source_file_free(project
->file_list
->pdata
[i
]);
131 g_ptr_array_free(project
->file_list
, TRUE
);
133 tm_workspace_remove_object(TM_WORK_OBJECT(project
), FALSE
);
134 g_free(project
->dir
);
135 tm_work_object_destroy(&(project
->work_object
));
138 void tm_project_free(gpointer project
)
142 tm_project_destroy(TM_PROJECT(project
));
147 gboolean
tm_project_add_file(TMProject
*project
, const char *file_name
150 TMWorkObject
*source_file
;
151 const TMWorkObject
*workspace
= TM_WORK_OBJECT(tm_get_workspace());
153 gboolean exists
= FALSE
;
155 g_return_val_if_fail((project
&& file_name
), FALSE
);
156 path
= tm_get_real_path(file_name
);
158 g_message("Adding %s to project", path
);
160 /* Check if the file is already loaded in the workspace */
161 source_file
= tm_workspace_find_object(TM_WORK_OBJECT(workspace
), path
, FALSE
);
162 if (NULL
!= source_file
)
164 if ((workspace
== source_file
->parent
) || (NULL
== source_file
->parent
))
167 g_message("%s moved from workspace to project", path
);
169 tm_workspace_remove_object(source_file
, FALSE
);
171 else if (TM_WORK_OBJECT(project
) == source_file
->parent
)
174 g_message("%s already exists in project", path
);
180 g_warning("Source file %s is shared among projects - will be duplicated!", path
);
184 if (NULL
== source_file
)
186 if (NULL
== (source_file
= tm_source_file_new(file_name
, TRUE
)))
188 /* g_warning("Unable to create source file for file %s", file_name); */
193 source_file
->parent
= TM_WORK_OBJECT(project
);
194 if (NULL
== project
->file_list
)
195 project
->file_list
= g_ptr_array_new();
197 g_ptr_array_add(project
->file_list
, source_file
);
198 TM_SOURCE_FILE(source_file
)->inactive
= FALSE
;
200 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
205 TMWorkObject
*tm_project_find_file(TMWorkObject
*work_object
206 , const char *file_name
, gboolean name_only
)
210 g_return_val_if_fail(work_object
&& file_name
, NULL
);
211 if (!IS_TM_PROJECT(work_object
))
213 g_warning("Non project pointer passed to tm_project_find_file(%s)", file_name
);
216 project
= TM_PROJECT(work_object
);
217 if ((NULL
== project
->file_list
) || (0 == project
->file_list
->len
))
225 name
= strrchr(file_name
, '/');
227 name
= g_strdup(name
+ 1);
229 name
= g_strdup(file_name
);
232 name
= tm_get_real_path(file_name
);
233 for (i
=0; i
< project
->file_list
->len
; ++i
)
236 name1
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
])->short_name
;
238 name1
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
])->file_name
;
239 if (0 == strcmp(name
, name1
))
242 return TM_WORK_OBJECT(project
->file_list
->pdata
[i
]);
250 gboolean
tm_project_remove_object(TMProject
*project
, TMWorkObject
*w
)
254 g_return_val_if_fail((project
&& w
), FALSE
);
255 if (!project
->file_list
)
257 for (i
=0; i
< project
->file_list
->len
; ++i
)
259 if (w
== project
->file_list
->pdata
[i
])
261 tm_work_object_free(w
);
262 g_ptr_array_remove_index(project
->file_list
, i
);
263 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
271 void tm_project_recreate_tags_array(TMProject
*project
)
274 TMWorkObject
*source_file
;
276 g_return_if_fail(project
);
278 g_message("Recreating tags for project: %s", project
->work_object
.file_name
);
281 /* FIXME: project doesn't free the old tags_array GPtrArray. But just sets its
282 size to 0 and recreate the tags. There'll be some dangling pointer. */
283 if (NULL
!= project
->work_object
.tags_array
)
284 g_ptr_array_set_size(project
->work_object
.tags_array
, 0);
286 project
->work_object
.tags_array
= g_ptr_array_new();
288 if (!project
->file_list
)
290 for (i
=0; i
< project
->file_list
->len
; ++i
)
292 source_file
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
]);
293 if ((NULL
!= source_file
) && !(TM_SOURCE_FILE(source_file
)->inactive
) &&
294 (NULL
!= source_file
->tags_array
) && (source_file
->tags_array
->len
> 0))
296 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
298 g_ptr_array_add(project
->work_object
.tags_array
,
299 source_file
->tags_array
->pdata
[j
]);
303 tm_tags_sort(project
->work_object
.tags_array
, NULL
, FALSE
);
306 gboolean
tm_project_update(TMWorkObject
*work_object
, gboolean force
307 , gboolean recurse
, gboolean update_parent
)
311 gboolean update_tags
= force
;
313 if (!work_object
|| !IS_TM_PROJECT(work_object
))
315 g_warning("Non project pointer passed to project update");
320 g_message("Updating project: %s", work_object
->file_name
);
323 project
= TM_PROJECT(work_object
);
324 if ((NULL
!= project
->file_list
) && (project
->file_list
->len
> 0))
329 g_message ("Gonna recursing in file-tags updating from [project], %d times", project
->file_list
->len
);
332 for (i
=0; i
< project
->file_list
->len
; ++i
)
334 if (TRUE
== tm_source_file_update(TM_WORK_OBJECT(
335 project
->file_list
->pdata
[i
]), FALSE
, FALSE
, FALSE
))
339 if (update_tags
|| (TM_WORK_OBJECT (project
)->tags_array
== NULL
))
342 g_message ("Tags updated, recreating tags array from project");
344 tm_project_recreate_tags_array(project
);
347 work_object
->analyze_time
= time(NULL
);
348 if ((work_object
->parent
) && (update_parent
)) {
349 tm_workspace_update(work_object
->parent
, TRUE
, FALSE
, FALSE
);
351 g_message ("Gonna reparse workspace too");
354 g_message ("Won't reparse workspace. Parent is %s and update_parent is %s"
355 , work_object
->parent
?"NOT NULL":"NULL", update_parent
?"TRUE":"FALSE");
363 #define IGNORE_FILE ".tm_ignore"
364 static void tm_project_set_ignorelist(TMProject
*project
)
367 char *ignore_file
= g_strconcat(project
->dir
, "/", IGNORE_FILE
, NULL
);
368 if (0 == stat(ignore_file
, &s
))
370 if (NULL
!= Option
.ignore
)
371 stringListClear(Option
.ignore
);
372 addIgnoreListFromFile(ignore_file
);
377 gboolean
tm_project_open(TMProject
*project
, gboolean force
)
380 TMSourceFile
*source_file
= NULL
;
383 if (!project
|| !IS_TM_PROJECT(TM_WORK_OBJECT(project
)))
386 g_message("Opening project %s", project
->work_object
.file_name
);
388 tm_project_set_ignorelist(project
);
389 if (NULL
== (fp
= fopen(project
->work_object
.file_name
, "r")))
391 while (NULL
!= (tag
= tm_tag_new_from_file(source_file
, fp
)))
393 if (tm_tag_file_t
== tag
->type
)
395 if (!(source_file
= TM_SOURCE_FILE(
396 tm_source_file_new(tag
->name
, FALSE
))))
399 g_warning("Unable to create source file %s", tag
->name
);
412 source_file
->work_object
.parent
= TM_WORK_OBJECT(project
);
413 source_file
->work_object
.analyze_time
= tag
->atts
.file
.timestamp
;
414 source_file
->lang
= tag
->atts
.file
.lang
;
415 source_file
->inactive
= tag
->atts
.file
.inactive
;
416 if (!project
->file_list
)
417 project
->file_list
= g_ptr_array_new();
418 g_ptr_array_add(project
->file_list
, source_file
);
424 if ((NULL
== source_file
) || (source_file
->inactive
)) /* Dangling tag */
427 g_warning("Dangling tag %s", tag
->name
);
438 if (NULL
== source_file
->work_object
.tags_array
)
439 source_file
->work_object
.tags_array
= g_ptr_array_new();
440 g_ptr_array_add(source_file
->work_object
.tags_array
, tag
);
442 g_message ("Added tag %s", tag
->name
);
448 tm_project_update((TMWorkObject
*) project
, FALSE
, TRUE
, TRUE
);
452 gboolean
tm_project_save(TMProject
*project
)
459 if (NULL
== (fp
= fopen(project
->work_object
.file_name
, "w")))
461 g_warning("Unable to save project %s", project
->work_object
.file_name
);
464 if (project
->file_list
)
466 for (i
=0; i
< project
->file_list
->len
; ++i
)
468 if (FALSE
== tm_source_file_write(TM_WORK_OBJECT(project
->file_list
->pdata
[i
])
469 , fp
, tm_tag_attr_max_t
))
480 static void tm_project_add_file_recursive(TMFileEntry
*entry
481 , gpointer user_data
, guint __unused__ level
)
484 if (!user_data
|| !entry
|| (tm_file_dir_t
== entry
->type
))
486 project
= TM_PROJECT(user_data
);
487 tm_project_add_file(project
, entry
->path
, FALSE
);
490 gboolean
tm_project_autoscan(TMProject
*project
)
492 TMFileEntry
*root_dir
;
496 file_match
= glist_from_array(project
->sources
);
497 dir_unmatch
= glist_from_array(project
->ignore
);
499 if (!project
|| !IS_TM_PROJECT(TM_WORK_OBJECT(project
))
502 if (!(root_dir
= tm_file_entry_new(project
->dir
, NULL
, TRUE
503 , file_match
, NULL
, NULL
, dir_unmatch
, TRUE
, TRUE
)))
505 g_warning("Unable to create file entry");
508 g_list_free(file_match
);
509 g_list_free(dir_unmatch
);
510 tm_file_entry_foreach(root_dir
, tm_project_add_file_recursive
511 , project
, 0, FALSE
);
512 tm_file_entry_free(root_dir
);
513 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
517 gboolean
tm_project_sync(TMProject
*project
, GList
*files
)
522 if (project
->file_list
)
524 for (i
= 0; i
< project
->file_list
->len
; ++i
)
525 tm_source_file_free(project
->file_list
->pdata
[i
]);
526 g_ptr_array_free(project
->file_list
, TRUE
);
527 project
->file_list
= NULL
;
528 if (project
->work_object
.tags_array
)
530 g_ptr_array_free(project
->work_object
.tags_array
, TRUE
);
531 project
->work_object
.tags_array
= NULL
;
534 for (tmp
= files
; tmp
; tmp
= g_list_next(tmp
))
536 tm_project_add_file(project
, (const char *) tmp
->data
, FALSE
);
538 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
542 void tm_project_dump(const TMProject
*p
)
546 tm_work_object_dump(TM_WORK_OBJECT(p
));
550 for (i
=0; i
< p
->file_list
->len
; ++i
)
552 fprintf(stderr
, "->\t");
553 tm_work_object_dump(TM_WORK_OBJECT(p
->file_list
->pdata
[i
]));
556 fprintf(stderr
, "-------------------------\n");
560 gboolean
tm_project_is_source_file(TMProject
*project
, const char *file_name
)
562 const char **pr_extn
;
564 if (!(project
&& IS_TM_PROJECT(TM_WORK_OBJECT(project
))
565 && file_name
&& project
->sources
))
567 for (pr_extn
= project
->sources
; pr_extn
&& *pr_extn
; ++ pr_extn
)
569 if (0 == fnmatch(*pr_extn
, file_name
, 0))