Make sure the current document is still the same after Save All
[geany-mirror.git] / tagmanager / src / tm_project.c
blobf9dd8f0bddaa41378f92ee2d4f14f775768e0aac
1 /*
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.
8 */
9 #include "general.h"
11 #include <stdio.h>
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #ifdef HAVE_FNMATCH_H
19 # include <fnmatch.h>
20 #endif
21 #include <glib/gstdio.h>
24 #include "options.h"
25 #define LIBCTAGS_DEFINED
26 #include "tm_tag.h"
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 */
39 #if 0
40 , "*.ui", "*.moc" /* KDE/QT Files */
41 , "*.glade" /* UI files */
42 #endif
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)
51 GList *l = NULL;
52 int i;
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)
63 struct stat s;
64 char *path;
66 g_return_val_if_fail((project && dir), FALSE);
67 #ifdef TM_DEBUG
68 g_message("Initializing project %s", dir);
69 #endif
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);
79 return FALSE;
81 project->dir = tm_get_real_path(dir);
82 if (sources)
83 project->sources = sources;
84 else
85 project->sources = s_sources;
86 if (ignore)
87 project->ignore = ignore;
88 else
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))
93 force = TRUE;
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);
98 g_free(project->dir);
99 g_free(path);
100 return FALSE;
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);
106 g_free(path);
107 return FALSE;
109 g_free(path);
110 tm_project_open(project, force);
111 if (!project->file_list || (0 == project->file_list->len))
112 tm_project_autoscan(project);
113 #ifdef TM_DEBUG
114 tm_workspace_dump();
115 #endif
116 return TRUE;
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))
125 g_free(project);
126 return NULL;
128 return (TMWorkObject *) project;
131 void tm_project_destroy(TMProject *project)
133 g_return_if_fail (project != NULL);
134 #ifdef TM_DEBUG
135 g_message("Destroying project: %s", project->work_object.file_name);
136 #endif
138 if (project->file_list)
140 guint i;
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)
152 if (NULL != project)
154 tm_project_destroy(TM_PROJECT(project));
155 g_free(project);
159 gboolean tm_project_add_file(TMProject *project, const char *file_name
160 ,gboolean update)
162 TMWorkObject *source_file;
163 const TMWorkObject *workspace = TM_WORK_OBJECT(tm_get_workspace());
164 char *path;
165 gboolean exists = FALSE;
167 g_return_val_if_fail((project && file_name), FALSE);
168 path = tm_get_real_path(file_name);
169 #ifdef TM_DEBUG
170 g_message("Adding %s to project", path);
171 #endif
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))
178 #ifdef TM_DEBUG
179 g_message("%s moved from workspace to project", path);
180 #endif
181 tm_workspace_remove_object(source_file, FALSE, TRUE);
183 else if (TM_WORK_OBJECT(project) == source_file->parent)
185 #ifdef TM_DEBUG
186 g_message("%s already exists in project", path);
187 #endif
188 exists = TRUE;
190 else
192 g_warning("Source file %s is shared among projects - will be duplicated!", path);
193 source_file = NULL;
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);
201 g_free(path);
202 return FALSE;
205 source_file->parent = TM_WORK_OBJECT(project);
206 if (NULL == project->file_list)
207 project->file_list = g_ptr_array_new();
208 if (!exists)
209 g_ptr_array_add(project->file_list, source_file);
210 TM_SOURCE_FILE(source_file)->inactive = FALSE;
211 if (update)
212 tm_project_update(TM_WORK_OBJECT(project), TRUE, FALSE, TRUE);
213 g_free(path);
214 return TRUE;
217 TMWorkObject *tm_project_find_file(TMWorkObject *work_object
218 , const char *file_name, gboolean name_only)
220 TMProject *project;
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);
226 return NULL;
228 project = TM_PROJECT(work_object);
229 if ((NULL == project->file_list) || (0 == project->file_list->len))
230 return NULL;
231 else
233 guint i;
234 char *name, *name1;
235 if (name_only)
237 name = strrchr(file_name, '/');
238 if (name)
239 name = g_strdup(name + 1);
240 else
241 name = g_strdup(file_name);
243 else
244 name = tm_get_real_path(file_name);
245 for (i=0; i < project->file_list->len; ++i)
247 if (name_only)
248 name1 = TM_WORK_OBJECT(project->file_list->pdata[i])->short_name;
249 else
250 name1 = TM_WORK_OBJECT(project->file_list->pdata[i])->file_name;
251 if (0 == strcmp(name, name1))
253 g_free(name);
254 return TM_WORK_OBJECT(project->file_list->pdata[i]);
257 g_free(name);
259 return NULL;
262 gboolean tm_project_remove_object(TMProject *project, TMWorkObject *w)
264 guint i;
266 g_return_val_if_fail((project && w), FALSE);
267 if (!project->file_list)
268 return FALSE;
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);
276 return TRUE;
279 return FALSE;
282 void tm_project_recreate_tags_array(TMProject *project)
284 guint i, j;
285 TMWorkObject *source_file;
287 g_return_if_fail(project);
288 #ifdef TM_DEBUG
289 g_message("Recreating tags for project: %s", project->work_object.file_name);
290 #endif
292 if (NULL != project->work_object.tags_array)
293 g_ptr_array_set_size(project->work_object.tags_array, 0);
294 else
295 project->work_object.tags_array = g_ptr_array_new();
297 if (!project->file_list)
298 return;
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)
318 TMProject *project;
319 guint i;
320 gboolean update_tags = force;
322 if (!work_object || !IS_TM_PROJECT(work_object))
324 g_warning("Non project pointer passed to project update");
325 return FALSE;
328 #ifdef TM_DEBUG
329 g_message("Updating project: %s", work_object->file_name);
330 #endif
332 project = TM_PROJECT(work_object);
333 if ((NULL != project->file_list) && (project->file_list->len > 0))
335 if (recurse)
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))
341 update_tags = TRUE;
344 if (update_tags || (TM_WORK_OBJECT (project)->tags_array == NULL))
346 #ifdef TM_DEBUG
347 g_message ("Tags updated, recreating tags array");
348 #endif
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);
355 return update_tags;
359 #define IGNORE_FILE ".tm_ignore"
360 static void tm_project_set_ignorelist(TMProject *project)
362 struct stat s;
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);
370 g_free(ignore_file);
373 gboolean tm_project_open(TMProject *project, gboolean force)
375 FILE *fp;
376 TMSourceFile *source_file = NULL;
377 TMTag *tag;
379 if (!project || !IS_TM_PROJECT(TM_WORK_OBJECT(project)))
380 return FALSE;
381 #ifdef TM_DEBUG
382 g_message("Opening project %s", project->work_object.file_name);
383 #endif
384 tm_project_set_ignorelist(project);
385 if (NULL == (fp = g_fopen(project->work_object.file_name, "r")))
386 return FALSE;
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))))
394 #ifdef TM_DEBUG
395 g_warning("Unable to create source file %s", tag->name);
396 #endif
397 if (!force)
399 tm_tag_unref(tag);
400 fclose(fp);
401 return FALSE;
403 else
404 source_file = NULL;
406 else
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);
415 tm_tag_unref(tag);
417 else
419 if ((NULL == source_file) || (source_file->inactive)) /* Dangling tag */
421 #ifdef TM_DEBUG
422 g_warning("Dangling tag %s", tag->name);
423 #endif
424 tm_tag_unref(tag);
425 if (!force)
427 fclose(fp);
428 return FALSE;
431 else
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);
436 #ifdef TM_DEBUG
437 g_message ("Added tag %s", tag->name);
438 #endif
442 fclose(fp);
443 tm_project_update((TMWorkObject *) project, FALSE, TRUE, TRUE);
444 return TRUE;
447 gboolean tm_project_save(TMProject *project)
449 guint i;
450 FILE *fp;
452 if (!project)
453 return FALSE;
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);
457 return FALSE;
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))
466 fclose(fp);
467 return FALSE;
471 fclose(fp);
472 return TRUE;
475 static void tm_project_add_file_recursive(TMFileEntry *entry
476 , gpointer user_data, guint UNUSED level)
478 TMProject *project;
479 if (!user_data || !entry || (tm_file_dir_t == entry->type))
480 return;
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;
488 GList *file_match;
489 GList *dir_unmatch;
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))
495 || (!project->dir))
496 return FALSE;
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");
501 return FALSE;
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);
509 return TRUE;
512 gboolean tm_project_sync(TMProject *project, GList *files)
514 GList *tmp;
515 guint i;
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);
534 return TRUE;
537 void tm_project_dump(const TMProject *p)
539 if (p)
541 tm_work_object_dump(TM_WORK_OBJECT(p));
542 if (p->file_list)
544 guint i;
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))
561 return FALSE;
562 for (pr_extn = project->sources; pr_extn && *pr_extn; ++ pr_extn)
564 if (0 == fnmatch(*pr_extn, file_name, 0))
565 return TRUE;
567 return FALSE;