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 #include <glib/gstdio.h>
25 #define LIBCTAGS_DEFINED
27 #include "tm_workspace.h"
28 #include "tm_source_file.h"
29 #include "tm_file_entry.h"
30 #include "tm_project.h"
32 #define TM_FILE_NAME ".tm_project.cache"
34 static const char *s_sources
[] = { "*.c", "*.pc" /* C/Pro*C files */
35 , "*.C", "*.cpp", "*.cc", "*.cxx", "*.c++" /* C++ files */
36 , "*.h", "*.hh", "*.hpp", "*.H", "*.h++", "*.i" /* Header files */
37 , "*.oaf", "*.gob", "*.idl" /* CORBA/Bonobo files */
38 , "*.l", "*.y" /* Lex/Yacc files */
40 , "*.ui", "*.moc" /* KDE/QT Files */
41 , "*.glade" /* UI files */
43 , "*.java", "*.pl", "*.pm", "*.py", "*.sh" /* Other languages */
44 , NULL
/* Must terminate with NULL */
47 static const char *s_ignore
[] = { "CVS", "intl", "po", NULL
};
49 static GList
*glist_from_array(const char **arr
)
53 for (i
=0; arr
[i
]; ++ i
)
54 l
= g_list_prepend(l
, (gpointer
) arr
[i
]);
55 return g_list_reverse(l
);
58 guint project_class_id
= 0;
60 gboolean
tm_project_init(TMProject
*project
, const char *dir
61 , const char **sources
, const char **ignore
, gboolean force
)
66 g_return_val_if_fail((project
&& dir
), FALSE
);
68 g_message("Initializing project %s", dir
);
70 if (0 == project_class_id
)
72 project_class_id
= tm_work_object_register(tm_project_free
, tm_project_update
73 , tm_project_find_file
);
76 if ((0 != g_stat(dir
, &s
)) || (!S_ISDIR(s
.st_mode
)))
78 g_warning("%s: Not a valid directory", dir
);
81 project
->dir
= tm_get_real_path(dir
);
83 project
->sources
= sources
;
85 project
->sources
= s_sources
;
87 project
->ignore
= ignore
;
89 project
->ignore
= s_ignore
;
90 project
->file_list
= NULL
;
91 path
= g_strdup_printf("%s/%s", project
->dir
, TM_FILE_NAME
);
92 if ((0 != g_stat(path
, &s
)) || (0 == s
.st_size
))
94 if (FALSE
== tm_work_object_init(&(project
->work_object
),
95 project_class_id
, path
, force
))
97 g_warning("Unable to init project file %s", path
);
102 if (! tm_workspace_add_object(TM_WORK_OBJECT(project
)))
104 g_warning("Unable to init project file %s", path
);
105 g_free(project
->dir
);
110 tm_project_open(project
, force
);
111 if (!project
->file_list
|| (0 == project
->file_list
->len
))
112 tm_project_autoscan(project
);
119 TMWorkObject
*tm_project_new(const char *dir
, const char **sources
120 , const char **ignore
, gboolean force
)
122 TMProject
*project
= g_new(TMProject
, 1);
123 if (FALSE
== tm_project_init(project
, dir
, sources
, ignore
, force
))
128 return (TMWorkObject
*) project
;
131 void tm_project_destroy(TMProject
*project
)
133 g_return_if_fail (project
!= NULL
);
135 g_message("Destroying project: %s", project
->work_object
.file_name
);
138 if (project
->file_list
)
141 for (i
= 0; i
< project
->file_list
->len
; ++i
)
142 tm_source_file_free(project
->file_list
->pdata
[i
]);
143 g_ptr_array_free(project
->file_list
, TRUE
);
145 tm_workspace_remove_object(TM_WORK_OBJECT(project
), FALSE
, TRUE
);
146 g_free(project
->dir
);
147 tm_work_object_destroy(&(project
->work_object
));
150 void tm_project_free(gpointer project
)
154 tm_project_destroy(TM_PROJECT(project
));
159 gboolean
tm_project_add_file(TMProject
*project
, const char *file_name
162 TMWorkObject
*source_file
;
163 const TMWorkObject
*workspace
= TM_WORK_OBJECT(tm_get_workspace());
165 gboolean exists
= FALSE
;
167 g_return_val_if_fail((project
&& file_name
), FALSE
);
168 path
= tm_get_real_path(file_name
);
170 g_message("Adding %s to project", path
);
172 /* Check if the file is already loaded in the workspace */
173 source_file
= tm_workspace_find_object(TM_WORK_OBJECT(workspace
), path
, FALSE
);
174 if (NULL
!= source_file
)
176 if ((workspace
== source_file
->parent
) || (NULL
== source_file
->parent
))
179 g_message("%s moved from workspace to project", path
);
181 tm_workspace_remove_object(source_file
, FALSE
, TRUE
);
183 else if (TM_WORK_OBJECT(project
) == source_file
->parent
)
186 g_message("%s already exists in project", path
);
192 g_warning("Source file %s is shared among projects - will be duplicated!", path
);
196 if (NULL
== source_file
)
198 if (NULL
== (source_file
= tm_source_file_new(file_name
, TRUE
, NULL
)))
200 g_warning("Unable to create source file for file %s", file_name
);
205 source_file
->parent
= TM_WORK_OBJECT(project
);
206 if (NULL
== project
->file_list
)
207 project
->file_list
= g_ptr_array_new();
209 g_ptr_array_add(project
->file_list
, source_file
);
210 TM_SOURCE_FILE(source_file
)->inactive
= FALSE
;
212 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
217 TMWorkObject
*tm_project_find_file(TMWorkObject
*work_object
218 , const char *file_name
, gboolean name_only
)
222 g_return_val_if_fail(work_object
&& file_name
, NULL
);
223 if (!IS_TM_PROJECT(work_object
))
225 g_warning("Non project pointer passed to tm_project_find_file(%s)", file_name
);
228 project
= TM_PROJECT(work_object
);
229 if ((NULL
== project
->file_list
) || (0 == project
->file_list
->len
))
237 name
= strrchr(file_name
, '/');
239 name
= g_strdup(name
+ 1);
241 name
= g_strdup(file_name
);
244 name
= tm_get_real_path(file_name
);
245 for (i
=0; i
< project
->file_list
->len
; ++i
)
248 name1
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
])->short_name
;
250 name1
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
])->file_name
;
251 if (0 == strcmp(name
, name1
))
254 return TM_WORK_OBJECT(project
->file_list
->pdata
[i
]);
262 gboolean
tm_project_remove_object(TMProject
*project
, TMWorkObject
*w
)
266 g_return_val_if_fail((project
&& w
), FALSE
);
267 if (!project
->file_list
)
269 for (i
=0; i
< project
->file_list
->len
; ++i
)
271 if (w
== project
->file_list
->pdata
[i
])
273 tm_work_object_free(w
);
274 g_ptr_array_remove_index(project
->file_list
, i
);
275 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
282 void tm_project_recreate_tags_array(TMProject
*project
)
285 TMWorkObject
*source_file
;
287 g_return_if_fail(project
);
289 g_message("Recreating tags for project: %s", project
->work_object
.file_name
);
292 if (NULL
!= project
->work_object
.tags_array
)
293 g_ptr_array_set_size(project
->work_object
.tags_array
, 0);
295 project
->work_object
.tags_array
= g_ptr_array_new();
297 if (!project
->file_list
)
299 for (i
=0; i
< project
->file_list
->len
; ++i
)
301 source_file
= TM_WORK_OBJECT(project
->file_list
->pdata
[i
]);
302 if ((NULL
!= source_file
) && !(TM_SOURCE_FILE(source_file
)->inactive
) &&
303 (NULL
!= source_file
->tags_array
) && (source_file
->tags_array
->len
> 0))
305 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
307 g_ptr_array_add(project
->work_object
.tags_array
,
308 source_file
->tags_array
->pdata
[j
]);
312 tm_tags_sort(project
->work_object
.tags_array
, NULL
, FALSE
);
315 gboolean
tm_project_update(TMWorkObject
*work_object
, gboolean force
316 , gboolean recurse
, gboolean update_parent
)
320 gboolean update_tags
= force
;
322 if (!work_object
|| !IS_TM_PROJECT(work_object
))
324 g_warning("Non project pointer passed to project update");
329 g_message("Updating project: %s", work_object
->file_name
);
332 project
= TM_PROJECT(work_object
);
333 if ((NULL
!= project
->file_list
) && (project
->file_list
->len
> 0))
337 for (i
=0; i
< project
->file_list
->len
; ++i
)
339 if (TRUE
== tm_source_file_update(TM_WORK_OBJECT(
340 project
->file_list
->pdata
[i
]), FALSE
, FALSE
, FALSE
))
344 if (update_tags
|| (TM_WORK_OBJECT (project
)->tags_array
== NULL
))
347 g_message ("Tags updated, recreating tags array");
349 tm_project_recreate_tags_array(project
);
352 /* work_object->analyze_time = time(NULL); */
353 if ((work_object
->parent
) && (update_parent
))
354 tm_workspace_update(work_object
->parent
, TRUE
, FALSE
, FALSE
);
359 #define IGNORE_FILE ".tm_ignore"
360 static void tm_project_set_ignorelist(TMProject
*project
)
363 char *ignore_file
= g_strconcat(project
->dir
, "/", IGNORE_FILE
, NULL
);
364 if (0 == g_stat(ignore_file
, &s
))
366 if (NULL
!= Option
.ignore
)
367 stringListClear(Option
.ignore
);
368 addIgnoreListFromFile(ignore_file
);
373 gboolean
tm_project_open(TMProject
*project
, gboolean force
)
376 TMSourceFile
*source_file
= NULL
;
379 if (!project
|| !IS_TM_PROJECT(TM_WORK_OBJECT(project
)))
382 g_message("Opening project %s", project
->work_object
.file_name
);
384 tm_project_set_ignorelist(project
);
385 if (NULL
== (fp
= g_fopen(project
->work_object
.file_name
, "r")))
387 while (NULL
!= (tag
= tm_tag_new_from_file(source_file
, fp
, 0, FALSE
)))
389 if (tm_tag_file_t
== tag
->type
)
391 if (!(source_file
= TM_SOURCE_FILE(
392 tm_source_file_new(tag
->name
, FALSE
, NULL
))))
395 g_warning("Unable to create source file %s", tag
->name
);
408 source_file
->work_object
.parent
= TM_WORK_OBJECT(project
);
409 source_file
->lang
= tag
->atts
.file
.lang
;
410 source_file
->inactive
= tag
->atts
.file
.inactive
;
411 if (!project
->file_list
)
412 project
->file_list
= g_ptr_array_new();
413 g_ptr_array_add(project
->file_list
, source_file
);
419 if ((NULL
== source_file
) || (source_file
->inactive
)) /* Dangling tag */
422 g_warning("Dangling tag %s", tag
->name
);
433 if (NULL
== source_file
->work_object
.tags_array
)
434 source_file
->work_object
.tags_array
= g_ptr_array_new();
435 g_ptr_array_add(source_file
->work_object
.tags_array
, tag
);
437 g_message ("Added tag %s", tag
->name
);
443 tm_project_update((TMWorkObject
*) project
, FALSE
, TRUE
, TRUE
);
447 gboolean
tm_project_save(TMProject
*project
)
454 if (NULL
== (fp
= g_fopen(project
->work_object
.file_name
, "w")))
456 g_warning("Unable to save project %s", project
->work_object
.file_name
);
459 if (project
->file_list
)
461 for (i
=0; i
< project
->file_list
->len
; ++i
)
463 if (FALSE
== tm_source_file_write(TM_WORK_OBJECT(project
->file_list
->pdata
[i
])
464 , fp
, tm_tag_attr_max_t
))
475 static void tm_project_add_file_recursive(TMFileEntry
*entry
476 , gpointer user_data
, guint UNUSED level
)
479 if (!user_data
|| !entry
|| (tm_file_dir_t
== entry
->type
))
481 project
= TM_PROJECT(user_data
);
482 tm_project_add_file(project
, entry
->path
, FALSE
);
485 gboolean
tm_project_autoscan(TMProject
*project
)
487 TMFileEntry
*root_dir
;
491 file_match
= glist_from_array(project
->sources
);
492 dir_unmatch
= glist_from_array(project
->ignore
);
494 if (!project
|| !IS_TM_PROJECT(TM_WORK_OBJECT(project
))
497 if (!(root_dir
= tm_file_entry_new(project
->dir
, NULL
, TRUE
498 , file_match
, NULL
, NULL
, dir_unmatch
, TRUE
, TRUE
)))
500 g_warning("Unable to create file entry");
503 g_list_free(file_match
);
504 g_list_free(dir_unmatch
);
505 tm_file_entry_foreach(root_dir
, tm_project_add_file_recursive
506 , project
, 0, FALSE
);
507 tm_file_entry_free(root_dir
);
508 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
512 gboolean
tm_project_sync(TMProject
*project
, GList
*files
)
517 if (project
->file_list
)
519 for (i
= 0; i
< project
->file_list
->len
; ++i
)
520 tm_source_file_free(project
->file_list
->pdata
[i
]);
521 g_ptr_array_free(project
->file_list
, TRUE
);
522 project
->file_list
= NULL
;
523 if (project
->work_object
.tags_array
)
525 g_ptr_array_free(project
->work_object
.tags_array
, TRUE
);
526 project
->work_object
.tags_array
= NULL
;
529 for (tmp
= files
; tmp
; tmp
= g_list_next(tmp
))
531 tm_project_add_file(project
, (const char *) tmp
->data
, FALSE
);
533 tm_project_update(TM_WORK_OBJECT(project
), TRUE
, FALSE
, TRUE
);
537 void tm_project_dump(const TMProject
*p
)
541 tm_work_object_dump(TM_WORK_OBJECT(p
));
545 for (i
=0; i
< p
->file_list
->len
; ++i
)
547 fprintf(stderr
, "->\t");
548 tm_work_object_dump(TM_WORK_OBJECT(p
->file_list
->pdata
[i
]));
551 fprintf(stderr
, "-------------------------\n");
555 gboolean
tm_project_is_source_file(TMProject
*project
, const char *file_name
)
557 const char **pr_extn
;
559 if (!(project
&& IS_TM_PROJECT(TM_WORK_OBJECT(project
))
560 && file_name
&& project
->sources
))
562 for (pr_extn
= project
->sources
; pr_extn
&& *pr_extn
; ++ pr_extn
)
564 if (0 == fnmatch(*pr_extn
, file_name
, 0))