Fix format string vulnerability by using g_set_error_literal ()
[anjuta.git] / plugins / mk-project / mk-project.c
blob27a09cf3d9aee4e875a00c91098768fd50e7dc2a
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* mk-project.c
4 * Copyright (C) 2009 Sébastien Granjoux
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include "mk-project.h"
28 #include "mk-rule.h"
30 #include "mk-project-private.h"
32 #include <libanjuta/interfaces/ianjuta-project.h>
33 #include <libanjuta/anjuta-debug.h>
34 #include <libanjuta/anjuta-utils.h>
35 #include <libanjuta/anjuta-pkg-config.h>
37 #include <string.h>
38 #include <memory.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44 #include <signal.h>
45 #include <glib/gi18n.h>
46 #include <gio/gio.h>
47 #include <glib.h>
48 #include "mk-scanner.h"
51 #define UNIMPLEMENTED G_STMT_START { g_warning (G_STRLOC": unimplemented"); } G_STMT_END
53 /* Constant strings for parsing perl script error output */
54 #define ERROR_PREFIX "ERROR("
55 #define WARNING_PREFIX "WARNING("
56 #define MESSAGE_DELIMITER ": "
58 static const gchar *valid_makefiles[] = {"GNUmakefile", "makefile", "Makefile", NULL};
61 /* Target types
62 *---------------------------------------------------------------------------*/
64 static MkpNodeInfo MkpNodeInformation[] = {
65 {{ANJUTA_PROJECT_GROUP,
66 N_("Group"),
67 ""}},
69 {{ANJUTA_PROJECT_SOURCE,
70 N_("Source"),
71 ""}},
73 {{ANJUTA_PROJECT_TARGET,
74 N_("Unknown"),
75 "text/plain"}},
77 {{ANJUTA_PROJECT_UNKNOWN,
78 NULL,
79 NULL}}
83 /* ----- Standard GObject types and variables ----- */
85 enum {
86 PROP_0,
87 PROP_PROJECT_DIR
90 static GObject *parent_class;
92 /* Helper functions
93 *---------------------------------------------------------------------------*/
95 #if 0
96 static void
97 error_set (GError **error, gint code, const gchar *message)
99 if (error != NULL) {
100 if (*error != NULL) {
101 gchar *tmp;
103 /* error already created, just change the code
104 * and prepend the string */
105 (*error)->code = code;
106 tmp = (*error)->message;
107 (*error)->message = g_strconcat (message, "\n\n", tmp, NULL);
108 g_free (tmp);
110 } else {
111 *error = g_error_new_literal (IANJUTA_PROJECT_ERROR,
112 code,
113 message);
117 #endif
119 /* Work even if file is not a descendant of parent */
120 static gchar*
121 get_relative_path (GFile *parent, GFile *file)
123 gchar *relative;
125 relative = g_file_get_relative_path (parent, file);
126 if (relative == NULL)
128 if (g_file_equal (parent, file))
130 relative = g_strdup ("");
132 else
134 GFile *grand_parent = g_file_get_parent (parent);
135 gint level;
136 gchar *grand_relative;
137 gchar *ptr;
138 gsize len;
141 for (level = 1; !g_file_has_prefix (file, grand_parent); level++)
143 GFile *next = g_file_get_parent (grand_parent);
145 g_object_unref (grand_parent);
146 grand_parent = next;
149 grand_relative = g_file_get_relative_path (grand_parent, file);
150 g_object_unref (grand_parent);
152 len = strlen (grand_relative);
153 relative = g_new (gchar, len + level * 3 + 1);
154 ptr = relative;
155 for (; level; level--)
157 memcpy(ptr, ".." G_DIR_SEPARATOR_S, 3);
158 ptr += 3;
160 memcpy (ptr, grand_relative, len + 1);
161 g_free (grand_relative);
165 return relative;
168 static GFileType
169 file_type (GFile *file, const gchar *filename)
171 GFile *child;
172 GFileInfo *info;
173 GFileType type;
175 child = filename != NULL ? g_file_get_child (file, filename) : g_object_ref (file);
177 //g_message ("check file %s", g_file_get_path (child));
179 info = g_file_query_info (child,
180 G_FILE_ATTRIBUTE_STANDARD_TYPE,
181 G_FILE_QUERY_INFO_NONE,
182 NULL,
183 NULL);
184 if (info != NULL)
186 type = g_file_info_get_file_type (info);
187 g_object_unref (info);
189 else
191 type = G_FILE_TYPE_UNKNOWN;
194 g_object_unref (child);
196 return type;
199 /* Group node
200 *---------------------------------------------------------------------------*/
202 static AnjutaProjectNode*
203 mkp_group_new (GFile *file)
205 MkpGroup *group = g_object_new (MKP_TYPE_GROUP, NULL);;
206 group->base.file = g_object_ref (file);
208 group->base.type = ANJUTA_PROJECT_GROUP;
209 group->base.native_properties = NULL;
210 group->base.custom_properties = NULL;
211 group->base.name = NULL;
212 group->base.state = 0;
215 return ANJUTA_PROJECT_NODE(group);
218 static void
219 mkp_group_class_init (MkpGroupClass *klass)
224 static void
225 mkp_group_init (MkpGroup *obj)
230 G_DEFINE_TYPE (MkpGroup, mkp_group, ANJUTA_TYPE_PROJECT_NODE);
232 /* Target node
233 *---------------------------------------------------------------------------*/
235 void
236 mkp_target_add_token (MkpTarget *target, AnjutaToken *token)
238 target->tokens = g_list_prepend (target->tokens, token);
242 static GList *
243 mkp_target_get_token (MkpTarget *target)
245 return target->tokens;
248 AnjutaProjectNode*
249 mkp_target_new (const gchar *name, AnjutaProjectNodeType type)
251 MkpTarget *target = NULL;
253 target = g_object_new (MKP_TYPE_TARGET, NULL);
254 target->base.name = g_strdup (name);
255 target->base.type = ANJUTA_PROJECT_TARGET;
256 target->base.state = 0;
258 return ANJUTA_PROJECT_NODE(target);
261 static void
262 mkp_target_class_init (MkpTargetClass *klass)
267 static void
268 mkp_target_init (MkpTarget *obj)
273 G_DEFINE_TYPE (MkpTarget, mkp_target, ANJUTA_TYPE_PROJECT_NODE);
275 /* Object node
276 *---------------------------------------------------------------------------*/
278 AnjutaProjectNode*
279 mkp_object_new (const gchar *name)
281 MkpObject *node = NULL;
283 node = g_object_new (MKP_TYPE_OBJECT, NULL);
284 node->base.name = g_strdup (name);
285 node->base.type = ANJUTA_PROJECT_OBJECT;
286 node->base.state = 0;
288 return ANJUTA_PROJECT_NODE(node);
291 static void
292 mkp_object_class_init (MkpObjectClass *klass)
297 static void
298 mkp_object_init (MkpObject *obj)
303 G_DEFINE_TYPE (MkpObject, mkp_object, ANJUTA_TYPE_PROJECT_NODE);
306 /* Source node
307 *---------------------------------------------------------------------------*/
309 AnjutaProjectNode*
310 mkp_source_new (GFile *file)
312 MkpSource *source = NULL;
314 source = g_object_new (MKP_TYPE_SOURCE, NULL);
315 source->base.file = g_object_ref (file);
316 source->base.type = ANJUTA_PROJECT_SOURCE;
317 source->base.native_properties = NULL;
318 source->base.custom_properties = NULL;
319 source->base.name = NULL;
320 source->base.state = 0;
322 return ANJUTA_PROJECT_NODE (source);
325 static void
326 mkp_source_class_init (MkpSourceClass *klass)
331 static void
332 mkp_source_init (MkpSource *obj)
337 G_DEFINE_TYPE (MkpSource, mkp_source, ANJUTA_TYPE_PROJECT_NODE);
340 * File monitoring support --------------------------------
341 * FIXME: review these
343 static void
344 monitor_cb (GFileMonitor *monitor,
345 GFile *file,
346 GFile *other_file,
347 GFileMonitorEvent event_type,
348 gpointer data)
350 MkpProject *project = data;
352 g_return_if_fail (project != NULL && MKP_IS_PROJECT (project));
354 switch (event_type) {
355 case G_FILE_MONITOR_EVENT_CHANGED:
356 case G_FILE_MONITOR_EVENT_CREATED:
357 g_signal_emit_by_name (G_OBJECT (project), "file-changed", data);
358 break;
359 default:
360 break;
365 static void
366 monitor_add (MkpProject *project, GFile *file)
368 GFileMonitor *monitor = NULL;
370 g_return_if_fail (project != NULL);
371 g_return_if_fail (project->monitors != NULL);
373 if (file == NULL)
374 return;
376 monitor = g_hash_table_lookup (project->monitors, file);
377 if (!monitor) {
378 gboolean exists;
380 /* FIXME clarify if uri is uri, path or both */
381 exists = g_file_query_exists (file, NULL);
383 if (exists) {
384 monitor = g_file_monitor_file (file,
385 G_FILE_MONITOR_NONE,
386 NULL,
387 NULL);
388 if (monitor != NULL)
390 g_signal_connect (G_OBJECT (monitor),
391 "changed",
392 G_CALLBACK (monitor_cb),
393 project);
394 g_hash_table_insert (project->monitors,
395 g_object_ref (file),
396 monitor);
402 static void
403 monitors_remove (MkpProject *project)
405 g_return_if_fail (project != NULL);
407 if (project->monitors)
408 g_hash_table_destroy (project->monitors);
409 project->monitors = NULL;
412 static void
413 files_hash_foreach_monitor (gpointer key,
414 gpointer value,
415 gpointer user_data)
417 GFile *makefile = (GFile *)key;
418 MkpProject *project = user_data;
420 monitor_add (project, makefile);
423 static void
424 monitors_setup (MkpProject *project)
426 g_return_if_fail (project != NULL);
428 monitors_remove (project);
430 /* setup monitors hash */
431 project->monitors = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
432 (GDestroyNotify) g_file_monitor_cancel);
434 if (project->files)
435 g_hash_table_foreach (project->files, files_hash_foreach_monitor, project);
440 * ---------------- Data structures managment
443 static AnjutaProjectNode *
444 project_node_new (MkpProject *project, AnjutaProjectNode *parent, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
446 AnjutaProjectNode *node = NULL;
448 switch (type & ANJUTA_PROJECT_TYPE_MASK) {
449 case ANJUTA_PROJECT_ROOT:
450 case ANJUTA_PROJECT_GROUP:
451 node = ANJUTA_PROJECT_NODE (mkp_group_new (file));
452 break;
453 case ANJUTA_PROJECT_TARGET:
454 node = ANJUTA_PROJECT_NODE (mkp_target_new (name, 0));
455 break;
456 case ANJUTA_PROJECT_SOURCE:
457 node = ANJUTA_PROJECT_NODE (mkp_source_new (file));
458 break;
459 default:
460 g_assert_not_reached ();
461 break;
463 if (node != NULL) node->type = type;
465 return node;
468 static AnjutaProjectNode*
469 project_load_makefile (MkpProject *project, GFile *file, MkpGroup *parent, GError **error)
471 MkpScanner *scanner;
472 AnjutaToken *arg;
473 AnjutaTokenFile *tfile;
474 AnjutaToken *parse;
475 gboolean ok;
476 GError *err = NULL;
478 /* Parse makefile */
479 DEBUG_PRINT ("Parse: %s", g_file_get_uri (file));
480 tfile = anjuta_token_file_new (file);
481 g_hash_table_insert (project->files, g_object_ref (file), g_object_ref (tfile));
482 arg = anjuta_token_file_load (tfile, NULL);
483 scanner = mkp_scanner_new (project);
484 parse = mkp_scanner_parse_token (scanner, arg, &err);
485 ok = parse != NULL;
486 mkp_scanner_free (scanner);
487 if (!ok)
489 if (err != NULL)
491 g_set_error_literal (error, IANJUTA_PROJECT_ERROR,
492 IANJUTA_PROJECT_ERROR_PROJECT_MALFORMED,
493 err->message);
494 g_error_free (err);
496 else
498 g_set_error (error, IANJUTA_PROJECT_ERROR,
499 IANJUTA_PROJECT_ERROR_PROJECT_MALFORMED,
500 _("Unable to parse make file"));
503 return NULL;
506 /* Load target */
507 mkp_project_enumerate_targets (project, ANJUTA_PROJECT_NODE(parent));
509 return ANJUTA_PROJECT_NODE(parent);
512 /* Project access functions
513 *---------------------------------------------------------------------------*/
515 MkpProject *
516 mkp_project_get_root (MkpProject *project)
518 return MKP_PROJECT(project);
521 static const GList *
522 mkp_project_get_node_info (MkpProject *project, GError **error)
524 static GList *info_list = NULL;
526 if (info_list == NULL)
528 MkpNodeInfo *node;
530 for (node = MkpNodeInformation; node->base.type != 0; node++)
532 info_list = g_list_prepend (info_list, node);
535 info_list = g_list_reverse (info_list);
538 return info_list;
541 gboolean
542 mkp_project_get_token_location (MkpProject *project, AnjutaTokenFileLocation *location, AnjutaToken *token)
544 GHashTableIter iter;
545 gpointer key;
546 gpointer value;
548 g_hash_table_iter_init (&iter, project->files);
549 while (g_hash_table_iter_next (&iter, &key, &value))
551 if (anjuta_token_file_get_token_location ((AnjutaTokenFile *)value, location, token))
553 return TRUE;
557 return FALSE;
560 /* Variable access functions
561 *---------------------------------------------------------------------------*/
563 const gchar *
564 mkp_variable_get_name (MkpVariable *variable)
566 return variable->name;
569 gchar *
570 mkp_variable_evaluate (MkpVariable *variable, MkpProject *project)
572 return anjuta_token_evaluate (variable->value);
575 static MkpVariable*
576 mkp_variable_new (gchar *name, AnjutaTokenType assign, AnjutaToken *value)
578 MkpVariable *variable = NULL;
580 g_return_val_if_fail (name != NULL, NULL);
582 variable = g_slice_new0(MkpVariable);
583 variable->name = g_strdup (name);
584 variable->assign = assign;
585 variable->value = value;
587 return variable;
590 static void
591 mkp_variable_free (MkpVariable *variable)
593 g_free (variable->name);
595 g_slice_free (MkpVariable, variable);
598 /* Public functions
599 *---------------------------------------------------------------------------*/
601 void
602 mkp_project_update_variable (MkpProject *project, AnjutaToken *variable)
604 AnjutaToken *arg;
605 char *name = NULL;
606 MakeTokenType assign = 0;
607 AnjutaToken *value = NULL;
609 //fprintf(stdout, "update variable");
610 //anjuta_token_dump (variable);
612 arg = anjuta_token_first_item (variable);
613 name = g_strstrip (anjuta_token_evaluate (arg));
614 arg = anjuta_token_next_item (arg);
616 //g_message ("new variable %s", name);
617 switch (anjuta_token_get_type (arg))
619 case MK_TOKEN_EQUAL:
620 case MK_TOKEN_IMMEDIATE_EQUAL:
621 case MK_TOKEN_CONDITIONAL_EQUAL:
622 case MK_TOKEN_APPEND:
623 assign = anjuta_token_get_type (arg);
624 break;
625 default:
626 break;
629 value = anjuta_token_next_item (arg);
631 if (assign != 0)
633 MkpVariable *var;
635 //g_message ("assign %d name %s value %s\n", assign, name, anjuta_token_evaluate (value));
636 var = (MkpVariable *)g_hash_table_lookup (project->variables, name);
637 if (var != NULL)
639 var->assign = assign;
640 var->value = value;
642 else
644 var = mkp_variable_new (name, assign, value);
645 g_hash_table_insert (project->variables, var->name, var);
650 //g_message ("update variable %s", name);
652 if (name) g_free (name);
655 AnjutaToken*
656 mkp_project_get_variable_token (MkpProject *project, AnjutaToken *variable)
658 guint length;
659 const gchar *string;
660 gchar *name;
661 MkpVariable *var;
663 length = anjuta_token_get_length (variable);
664 string = anjuta_token_get_string (variable);
665 if (string[1] == '(')
667 name = g_strndup (string + 2, length - 3);
669 else
671 name = g_strndup (string + 1, 1);
673 var = g_hash_table_lookup (project->variables, name);
674 g_free (name);
676 return var != NULL ? var->value : NULL;
679 static AnjutaProjectNode *
680 mkp_project_load_root (MkpProject *project, AnjutaProjectNode *node, GError **error)
682 GFile *root_file;
683 GFile *make_file = NULL;
684 const gchar **makefile;
685 MkpGroup *group;
687 /* Unload current project */
688 root_file = g_object_ref (anjuta_project_node_get_file (node));
689 mkp_project_unload (project);
690 project->root_file = root_file;
691 DEBUG_PRINT ("reload project %p root file %p", project, project->root_file);
693 /* shortcut hash tables */
694 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
695 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
696 project->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mkp_variable_free);
698 /* Initialize rules data */
699 mkp_project_init_rules (project);
701 /* Initialize list styles */
702 project->space_list = anjuta_token_style_new (NULL, " ", "\n", NULL, 0);
703 project->arg_list = anjuta_token_style_new (NULL, ", ", ",\n ", ")", 0);
705 /* Find make file */
706 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
708 if (file_type (root_file, *makefile) == G_FILE_TYPE_REGULAR)
710 make_file = g_file_get_child (root_file, *makefile);
711 break;
714 if (make_file == NULL)
716 g_set_error (error, IANJUTA_PROJECT_ERROR,
717 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
718 _("Project doesn't exist or invalid path"));
720 return NULL;
723 /* Create group */
724 group = MKP_GROUP(mkp_group_new (root_file));
725 anjuta_project_node_append (node, ANJUTA_PROJECT_NODE(group));
726 g_hash_table_insert (project->groups, g_file_get_uri (root_file), group);
729 /* Parse make file */
730 project_load_makefile (project, make_file, group, error);
731 g_object_unref (make_file);
733 monitors_setup (project);
735 return node;
738 AnjutaProjectNode*
739 mkp_project_load_node (MkpProject *project, AnjutaProjectNode *node, GError **error)
741 switch (anjuta_project_node_get_node_type (node))
743 case ANJUTA_PROJECT_ROOT:
744 project->loading++;
745 DEBUG_PRINT("*** Loading project: %p root file: %p\n", project, project->root_file);
746 return mkp_project_load_root (project, node, error);
747 case ANJUTA_PROJECT_GROUP:
748 project->loading++;
749 return project_load_makefile (project, node->file, MKP_GROUP(node), error);
750 default:
751 return NULL;
755 gboolean
756 mkp_project_reload (MkpProject *project, GError **error)
758 GFile *root_file;
759 GFile *make_file;
760 const gchar **makefile;
761 MkpGroup *group;
762 gboolean ok = TRUE;
764 /* Unload current project */
765 root_file = g_object_ref (project->root_file);
766 mkp_project_unload (project);
767 project->root_file = root_file;
768 DEBUG_PRINT ("reload project %p root file %p", project, project->root_file);
770 /* shortcut hash tables */
771 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
772 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
773 project->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mkp_variable_free);
775 /* Initialize rules data */
776 mkp_project_init_rules (project);
778 /* Initialize list styles */
779 project->space_list = anjuta_token_style_new (NULL, " ", "\n", NULL, 0);
780 project->arg_list = anjuta_token_style_new (NULL, ", ", ",\n ", ")", 0);
782 /* Find make file */
783 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
785 if (file_type (root_file, *makefile) == G_FILE_TYPE_REGULAR)
787 make_file = g_file_get_child (root_file, *makefile);
788 break;
791 if (make_file == NULL)
793 g_set_error (error, IANJUTA_PROJECT_ERROR,
794 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
795 _("Project doesn't exist or invalid path"));
797 return FALSE;
800 /* Create group */
801 group = MKP_GROUP(mkp_group_new (root_file));
802 anjuta_project_node_append (ANJUTA_PROJECT_NODE(project), ANJUTA_PROJECT_NODE(group));
803 g_hash_table_insert (project->groups, g_file_get_uri (root_file), group);
805 /* Parse make file */
806 project_load_makefile (project, make_file, group, error);
807 g_object_unref (make_file);
809 monitors_setup (project);
812 return ok;
815 gboolean
816 mkp_project_load (MkpProject *project,
817 GFile *directory,
818 GError **error)
820 g_return_val_if_fail (directory != NULL, FALSE);
822 return mkp_project_load_root (project, ANJUTA_PROJECT_NODE(project), error) != NULL;
825 void
826 mkp_project_unload (MkpProject *project)
828 AnjutaProjectNode *node;
830 monitors_remove (project);
832 /* project data */
833 if (project->root_file) g_object_unref (project->root_file);
834 project->root_file = NULL;
836 /* Remove all children */
837 while ((node = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (project))) != NULL)
839 g_object_unref (node);
842 /* shortcut hash tables */
843 if (project->groups) g_hash_table_destroy (project->groups);
844 project->groups = NULL;
845 if (project->files) g_hash_table_destroy (project->files);
846 project->files = NULL;
847 if (project->variables) g_hash_table_destroy (project->variables);
848 project->variables = NULL;
850 mkp_project_free_rules (project);
852 /* List styles */
853 if (project->space_list) anjuta_token_style_free (project->space_list);
854 if (project->arg_list) anjuta_token_style_free (project->arg_list);
857 gint
858 mkp_project_probe (GFile *directory,
859 GError **error)
861 gboolean probe;
862 gboolean dir;
864 dir = (file_type (directory, NULL) == G_FILE_TYPE_DIRECTORY);
865 if (!dir)
867 g_set_error (error, IANJUTA_PROJECT_ERROR,
868 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
869 _("Project doesn't exist or invalid path"));
872 probe = dir;
873 if (probe)
875 const gchar **makefile;
877 /* Look for makefiles */
878 probe = FALSE;
879 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
881 if (file_type (directory, *makefile) == G_FILE_TYPE_REGULAR)
883 probe = TRUE;
884 break;
889 return probe ? IANJUTA_PROJECT_PROBE_MAKE_FILES : 0;
892 gboolean
893 mkp_project_save (MkpProject *project, GError **error)
895 gpointer key;
896 gpointer value;
897 GHashTableIter iter;
899 g_return_val_if_fail (project != NULL, FALSE);
901 g_hash_table_iter_init (&iter, project->files);
902 while (g_hash_table_iter_next (&iter, &key, &value))
904 GError *error = NULL;
905 AnjutaTokenFile *tfile = (AnjutaTokenFile *)value;
906 anjuta_token_file_save (tfile, &error);
909 return TRUE;
912 gboolean
913 mkp_project_move (MkpProject *project, const gchar *path)
915 GFile *old_root_file;
916 GFile *new_file;
917 gchar *relative;
918 GHashTableIter iter;
919 gpointer key;
920 gpointer value;
921 AnjutaTokenFile *tfile;
922 GHashTable* old_hash;
924 /* Change project root directory */
925 old_root_file = project->root_file;
926 project->root_file = g_file_new_for_path (path);
928 /* Change project root directory in groups */
929 old_hash = project->groups;
930 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
931 g_hash_table_iter_init (&iter, old_hash);
932 while (g_hash_table_iter_next (&iter, &key, &value))
934 MkpGroup *group = (MkpGroup *)value;
936 relative = get_relative_path (old_root_file, group->base.file);
937 new_file = g_file_resolve_relative_path (project->root_file, relative);
938 g_free (relative);
939 g_object_unref (group->base.file);
940 group->base.file = new_file;
942 g_hash_table_insert (project->groups, g_file_get_uri (new_file), group);
944 g_hash_table_destroy (old_hash);
946 /* Change all files */
947 old_hash = project->files;
948 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
949 g_hash_table_iter_init (&iter, old_hash);
950 while (g_hash_table_iter_next (&iter, &key, (gpointer *)&tfile))
952 relative = get_relative_path (old_root_file, anjuta_token_file_get_file (tfile));
953 new_file = g_file_resolve_relative_path (project->root_file, relative);
954 g_free (relative);
955 anjuta_token_file_move (tfile, new_file);
957 g_hash_table_insert (project->files, new_file, tfile);
958 g_object_unref (key);
960 g_hash_table_steal_all (old_hash);
961 g_hash_table_destroy (old_hash);
963 g_object_unref (old_root_file);
965 return TRUE;
968 MkpProject *
969 mkp_project_new (GFile *file, GError **error)
971 MkpProject *project;
973 project = MKP_PROJECT (g_object_new (MKP_TYPE_PROJECT, NULL));
974 project->parent.file = (file != NULL) ? g_file_dup (file) : NULL;
975 project->parent.type = ANJUTA_PROJECT_ROOT;
977 return project;
980 static gboolean
981 mkp_project_is_loaded (MkpProject *project)
983 return project->loading == 0;
987 /* Implement IAnjutaProject
988 *---------------------------------------------------------------------------*/
990 static gboolean
991 iproject_load_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
993 if (node == NULL) node = ANJUTA_PROJECT_NODE (obj);
996 if (mkp_project_load_node (MKP_PROJECT (obj), node, err) != NULL) {
997 (MKP_PROJECT(obj))->loading--;
998 g_signal_emit_by_name (MKP_PROJECT(obj), "node-loaded", node, err);
1000 return TRUE;
1003 return FALSE;
1006 static gboolean
1007 iproject_save_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
1009 return mkp_project_save (MKP_PROJECT(node), err);
1012 static AnjutaProjectProperty *
1013 iproject_set_property (IAnjutaProject *obj, AnjutaProjectNode *node, AnjutaProjectProperty *property, const gchar *value, GError **error)
1015 g_set_error (error, IANJUTA_PROJECT_ERROR,
1016 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED,
1017 _("Project doesn't allow to set properties"));
1019 return NULL;
1022 static gboolean
1023 iproject_remove_property (IAnjutaProject *obj, AnjutaProjectNode *node, AnjutaProjectProperty *property, GError **error)
1025 g_set_error (error, IANJUTA_PROJECT_ERROR,
1026 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED,
1027 _("Project doesn't allow to set properties"));
1029 return FALSE;
1032 static const GList*
1033 iproject_get_node_info (IAnjutaProject *obj, GError **err)
1035 return mkp_project_get_node_info (MKP_PROJECT (obj), err);
1038 static AnjutaProjectNode *
1039 iproject_get_root (IAnjutaProject *obj, GError **err)
1041 return ANJUTA_PROJECT_NODE(mkp_project_get_root (MKP_PROJECT(obj)));
1044 static gboolean
1045 iproject_is_loaded (IAnjutaProject *obj, GError **err)
1047 return mkp_project_is_loaded (MKP_PROJECT (obj));
1050 static gboolean
1051 iproject_remove_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
1053 anjuta_project_node_set_state (node, ANJUTA_PROJECT_REMOVED);
1054 g_signal_emit_by_name (obj, "node-modified", node, NULL);
1056 return TRUE;
1059 static AnjutaProjectNode *
1060 iproject_add_node_before (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
1062 AnjutaProjectNode *node;
1064 node = project_node_new (MKP_PROJECT (obj), parent, type, file, name, error);
1065 anjuta_project_node_set_state (node, ANJUTA_PROJECT_MODIFIED);
1066 anjuta_project_node_insert_before (parent, sibling, node);
1068 g_signal_emit_by_name (obj, "node-changed", node, NULL);
1070 return node;
1073 static AnjutaProjectNode *
1074 iproject_add_node_after (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
1076 AnjutaProjectNode *node;
1078 node = project_node_new (MKP_PROJECT (obj), parent, type, file, name, error);
1079 anjuta_project_node_set_state (node, ANJUTA_PROJECT_MODIFIED);
1080 anjuta_project_node_insert_after (parent, sibling, node);
1082 g_signal_emit_by_name (obj, "node-modified", node, NULL);
1084 return node;
1087 static void
1088 iproject_iface_init(IAnjutaProjectIface* iface)
1090 iface->load_node = iproject_load_node;
1091 iface->save_node = iproject_save_node;
1092 iface->set_property = iproject_set_property;
1093 iface->remove_property = iproject_remove_property;
1094 iface->get_node_info = iproject_get_node_info;
1095 iface->get_root = iproject_get_root;
1097 iface->is_loaded = iproject_is_loaded;
1099 iface->remove_node = iproject_remove_node;
1101 iface->add_node_before = iproject_add_node_before;
1102 iface->add_node_after = iproject_add_node_after;
1106 /* GObject implementation
1107 *---------------------------------------------------------------------------*/
1109 static void
1110 mkp_project_dispose (GObject *object)
1112 g_return_if_fail (MKP_IS_PROJECT (object));
1114 mkp_project_unload (MKP_PROJECT (object));
1116 G_OBJECT_CLASS (parent_class)->dispose (object);
1119 static void
1120 mkp_project_instance_init (MkpProject *project)
1122 g_return_if_fail (project != NULL);
1123 g_return_if_fail (MKP_IS_PROJECT (project));
1125 /* project data */
1126 project->root_file = NULL;
1127 project->suffix = NULL;
1128 project->rules = NULL;
1130 project->space_list = NULL;
1131 project->arg_list = NULL;
1134 static void
1135 mkp_project_class_init (MkpProjectClass *klass)
1137 GObjectClass *object_class;
1139 parent_class = g_type_class_peek_parent (klass);
1141 object_class = G_OBJECT_CLASS (klass);
1142 object_class->dispose = mkp_project_dispose;
1146 ANJUTA_TYPE_BEGIN(MkpProject, mkp_project, ANJUTA_TYPE_PROJECT_NODE);
1147 ANJUTA_TYPE_ADD_INTERFACE(iproject, IANJUTA_TYPE_PROJECT);
1148 ANJUTA_TYPE_END;