Updated Spanish translation
[anjuta-git-plugin.git] / tagmanager / tm_project.c
blobd954e443bb14169776d2c4b2925e75246202dff9
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 #include <fnmatch.h>
20 #include "options.h"
21 #define LIBCTAGS_DEFINED
22 #include "tm_tag.h"
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 */
35 #if 0
36 , "*.ui", "*.moc" /* KDE/QT Files */
37 , "*.glade" /* UI files */
38 #endif
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)
47 GList *l = NULL;
48 int i;
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)
59 struct stat s;
60 char path[PATH_MAX];
62 g_return_val_if_fail((project && dir), FALSE);
63 #ifdef TM_DEBUG
64 g_message("Initializing project %s", dir);
65 #endif
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);
75 return FALSE;
77 project->dir = tm_get_real_path(dir);
78 if (sources)
79 project->sources = sources;
80 else
81 project->sources = s_sources;
82 if (ignore)
83 project->ignore = ignore;
84 else
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))
89 force = TRUE;
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);
94 g_free(project->dir);
95 return FALSE;
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);
101 #ifdef TM_DEBUG
102 tm_workspace_dump();
103 #endif
104 return TRUE;
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))
113 g_free(project);
114 return NULL;
116 return (TMWorkObject *) project;
119 void tm_project_destroy(TMProject *project)
121 g_return_if_fail (project != NULL);
122 #ifdef TM_DEBUG
123 g_message("Destroying project: %s", project->work_object.file_name);
124 #endif
126 if (project->file_list)
128 guint i;
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)
140 if (NULL != project)
142 tm_project_destroy(TM_PROJECT(project));
143 g_free(project);
147 gboolean tm_project_add_file(TMProject *project, const char *file_name
148 ,gboolean update)
150 TMWorkObject *source_file;
151 const TMWorkObject *workspace = TM_WORK_OBJECT(tm_get_workspace());
152 char *path;
153 gboolean exists = FALSE;
155 g_return_val_if_fail((project && file_name), FALSE);
156 path = tm_get_real_path(file_name);
157 #ifdef TM_DEBUG
158 g_message("Adding %s to project", path);
159 #endif
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))
166 #ifdef TM_DEBUG
167 g_message("%s moved from workspace to project", path);
168 #endif
169 tm_workspace_remove_object(source_file, FALSE);
171 else if (TM_WORK_OBJECT(project) == source_file->parent)
173 #ifdef TM_DEBUG
174 g_message("%s already exists in project", path);
175 #endif
176 exists = TRUE;
178 else
180 g_warning("Source file %s is shared among projects - will be duplicated!", path);
181 source_file = NULL;
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); */
189 g_free(path);
190 return FALSE;
193 source_file->parent = TM_WORK_OBJECT(project);
194 if (NULL == project->file_list)
195 project->file_list = g_ptr_array_new();
196 if (!exists)
197 g_ptr_array_add(project->file_list, source_file);
198 TM_SOURCE_FILE(source_file)->inactive = FALSE;
199 if (update)
200 tm_project_update(TM_WORK_OBJECT(project), TRUE, FALSE, TRUE);
201 g_free(path);
202 return TRUE;
205 TMWorkObject *tm_project_find_file(TMWorkObject *work_object
206 , const char *file_name, gboolean name_only)
208 TMProject *project;
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);
214 return NULL;
216 project = TM_PROJECT(work_object);
217 if ((NULL == project->file_list) || (0 == project->file_list->len))
218 return NULL;
219 else
221 guint i;
222 char *name, *name1;
223 if (name_only)
225 name = strrchr(file_name, '/');
226 if (name)
227 name = g_strdup(name + 1);
228 else
229 name = g_strdup(file_name);
231 else
232 name = tm_get_real_path(file_name);
233 for (i=0; i < project->file_list->len; ++i)
235 if (name_only)
236 name1 = TM_WORK_OBJECT(project->file_list->pdata[i])->short_name;
237 else
238 name1 = TM_WORK_OBJECT(project->file_list->pdata[i])->file_name;
239 if (0 == strcmp(name, name1))
241 g_free(name);
242 return TM_WORK_OBJECT(project->file_list->pdata[i]);
245 g_free(name);
247 return NULL;
250 gboolean tm_project_remove_object(TMProject *project, TMWorkObject *w)
252 guint i;
254 g_return_val_if_fail((project && w), FALSE);
255 if (!project->file_list)
256 return FALSE;
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);
264 return TRUE;
267 return FALSE;
271 void tm_project_recreate_tags_array(TMProject *project)
273 guint i, j;
274 TMWorkObject *source_file;
276 g_return_if_fail(project);
277 #ifdef TM_DEBUG
278 g_message("Recreating tags for project: %s", project->work_object.file_name);
279 #endif
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);
285 else
286 project->work_object.tags_array = g_ptr_array_new();
288 if (!project->file_list)
289 return;
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)
309 TMProject *project;
310 guint i;
311 gboolean update_tags = force;
313 if (!work_object || !IS_TM_PROJECT(work_object))
315 g_warning("Non project pointer passed to project update");
316 return FALSE;
319 #ifdef TM_DEBUG
320 g_message("Updating project: %s", work_object->file_name);
321 #endif
323 project = TM_PROJECT(work_object);
324 if ((NULL != project->file_list) && (project->file_list->len > 0))
326 if (recurse)
328 #ifdef TM_DEBUG
329 g_message ("Gonna recursing in file-tags updating from [project], %d times", project->file_list->len);
330 #endif
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))
336 update_tags = TRUE;
339 if (update_tags || (TM_WORK_OBJECT (project)->tags_array == NULL))
341 #ifdef TM_DEBUG
342 g_message ("Tags updated, recreating tags array from project");
343 #endif
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);
350 #ifdef TM_DEBUG
351 g_message ("Gonna reparse workspace too");
353 else {
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");
356 #endif
359 return update_tags;
363 #define IGNORE_FILE ".tm_ignore"
364 static void tm_project_set_ignorelist(TMProject *project)
366 struct stat s;
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);
374 g_free(ignore_file);
377 gboolean tm_project_open(TMProject *project, gboolean force)
379 FILE *fp;
380 TMSourceFile *source_file = NULL;
381 TMTag *tag;
383 if (!project || !IS_TM_PROJECT(TM_WORK_OBJECT(project)))
384 return FALSE;
385 #ifdef TM_DEBUG
386 g_message("Opening project %s", project->work_object.file_name);
387 #endif
388 tm_project_set_ignorelist(project);
389 if (NULL == (fp = fopen(project->work_object.file_name, "r")))
390 return FALSE;
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))))
398 #ifdef TM_DEBUG
399 g_warning("Unable to create source file %s", tag->name);
400 #endif
401 if (!force)
403 tm_tag_free(tag);
404 fclose(fp);
405 return FALSE;
407 else
408 source_file = NULL;
410 else
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);
420 tm_tag_free(tag);
422 else
424 if ((NULL == source_file) || (source_file->inactive)) /* Dangling tag */
426 #ifdef TM_DEBUG
427 g_warning("Dangling tag %s", tag->name);
428 #endif
429 tm_tag_free(tag);
430 if (!force)
432 fclose(fp);
433 return FALSE;
436 else
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);
441 #ifdef TM_DEBUG
442 g_message ("Added tag %s", tag->name);
443 #endif
447 fclose(fp);
448 tm_project_update((TMWorkObject *) project, FALSE, TRUE, TRUE);
449 return TRUE;
452 gboolean tm_project_save(TMProject *project)
454 guint i;
455 FILE *fp;
457 if (!project)
458 return FALSE;
459 if (NULL == (fp = fopen(project->work_object.file_name, "w")))
461 g_warning("Unable to save project %s", project->work_object.file_name);
462 return FALSE;
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))
471 fclose(fp);
472 return FALSE;
476 fclose(fp);
477 return TRUE;
480 static void tm_project_add_file_recursive(TMFileEntry *entry
481 , gpointer user_data, guint __unused__ level)
483 TMProject *project;
484 if (!user_data || !entry || (tm_file_dir_t == entry->type))
485 return;
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;
493 GList *file_match;
494 GList *dir_unmatch;
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))
500 || (!project->dir))
501 return FALSE;
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");
506 return FALSE;
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);
514 return TRUE;
517 gboolean tm_project_sync(TMProject *project, GList *files)
519 GList *tmp;
520 guint i;
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);
539 return TRUE;
542 void tm_project_dump(const TMProject *p)
544 if (p)
546 tm_work_object_dump(TM_WORK_OBJECT(p));
547 if (p->file_list)
549 int i;
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))
566 return FALSE;
567 for (pr_extn = project->sources; pr_extn && *pr_extn; ++ pr_extn)
569 if (0 == fnmatch(*pr_extn, file_name, 0))
570 return TRUE;
572 return FALSE;