1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* amp-group.c
3  *
4  * Copyright (C) 2010  Sébastien Granjoux
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include "amp-group.h"
28 
29 #include "amp-node.h"
30 #include "amp-source.h"
31 #include "am-scanner.h"
32 #include "am-properties.h"
33 #include "am-writer.h"
34 
35 
36 #include <libanjuta/interfaces/ianjuta-project.h>
37 
38 #include <libanjuta/anjuta-debug.h>
39 
40 #include <glib/gi18n.h>
41 
42 #include <memory.h>
43 #include <string.h>
44 #include <ctype.h>
45 
46 /* Types
47  *---------------------------------------------------------------------------*/
48 
49 G_DEFINE_DYNAMIC_TYPE (AmpGroupNode, amp_group_node, AMP_TYPE_NODE);
50 
51 
52 /* Helper functions
53  *---------------------------------------------------------------------------*/
54 
55 static void
error_set(GError ** error,gint code,const gchar * message)56 error_set (GError **error, gint code, const gchar *message)
57 {
58         if (error != NULL) {
59                 if (*error != NULL) {
60                         gchar *tmp;
61 
62                         /* error already created, just change the code
63                          * and prepend the string */
64                         (*error)->code = code;
65                         tmp = (*error)->message;
66                         (*error)->message = g_strconcat (message, "\n\n", tmp, NULL);
67                         g_free (tmp);
68 
69                 } else {
70                         *error = g_error_new_literal (IANJUTA_PROJECT_ERROR,
71                                                       code,
72                                                       message);
73                 }
74         }
75 }
76 
77 /* Private functions
78  *---------------------------------------------------------------------------*/
79 
80 /* Find if pkg-config modules are used in group targets */
81 static gboolean
project_load_group_module(AmpProject * project,AmpGroupNode * group)82 project_load_group_module (AmpProject *project, AmpGroupNode *group)
83 {
84 	AnjutaProjectNode *target;
85 	AnjutaProjectProperty *prop;
86 	gchar **group_cpp = NULL;
87 
88 	prop = amp_node_get_property_from_token (ANJUTA_PROJECT_NODE (group), AM_TOKEN__CPPFLAGS, 0);
89 	if (prop && (prop->value != NULL)) group_cpp = g_strsplit_set (prop->value, " \t", 0);
90 
91 	/* Check all targets */
92 	for (target = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (group)); target != NULL; target = anjuta_project_node_next_sibling (target))
93 	{
94 		gint type = anjuta_project_node_get_full_type (target) & (ANJUTA_PROJECT_ID_MASK | ANJUTA_PROJECT_TYPE_MASK);
95 		gchar **target_lib = NULL;
96 		gchar **target_cpp = NULL;
97 
98 		prop = NULL;
99 		switch (type)
100 		{
101 		case ANJUTA_PROJECT_TARGET | ANJUTA_PROJECT_PROGRAM:
102 			prop = amp_node_get_property_from_token (target, AM_TOKEN_TARGET_LDADD, 0);
103 			break;
104 		case ANJUTA_PROJECT_TARGET | ANJUTA_PROJECT_STATICLIB:
105 		case ANJUTA_PROJECT_TARGET | ANJUTA_PROJECT_SHAREDLIB:
106 		case ANJUTA_PROJECT_TARGET | ANJUTA_PROJECT_LT_MODULE:
107 			prop = amp_node_get_property_from_token (target, AM_TOKEN_TARGET_LIBADD, 0);
108 			break;
109 		default:
110 			break;
111 		}
112 		if (prop && (prop->value != NULL)) target_lib = g_strsplit_set (prop->value, " \t", 0);
113 
114 		/* Check if targets use libraries */
115 		if (target_lib != NULL)
116 		{
117 			AnjutaProjectNode *module;
118 
119 			prop = amp_node_get_property_from_token (target, AM_TOKEN_TARGET_CPPFLAGS, 0);
120 			if (prop && (prop->value != NULL)) target_cpp = g_strsplit_set (prop->value, " \t", 0);
121 
122 			for (module = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (project)); module != NULL; module = anjuta_project_node_next_sibling (module))
123 			{
124 				if (anjuta_project_node_get_node_type (module) == ANJUTA_PROJECT_MODULE)
125 				{
126 					const gchar *name = anjuta_project_node_get_name (module);
127 					gchar *lib_flags = g_strconcat ("$(", name, "_LIBS)", NULL);
128 					gchar **flags;
129 
130 					for (flags = target_lib; *flags != NULL; flags++)
131 					{
132 
133 						if (strcmp (*flags, lib_flags) == 0)
134 						{
135 							gchar *cpp_flags = g_strconcat ("$(", name, "_CFLAGS)", NULL);
136 							gchar **cflags;
137 							gboolean found = FALSE;
138 
139 							if (group_cpp != NULL)
140 							{
141 								for (cflags = group_cpp; *cflags != NULL; cflags++)
142 								{
143 									if (strcmp (*cflags, cpp_flags) == 0)
144 									{
145 										found = TRUE;
146 										break;
147 									}
148 								}
149 							}
150 							if ((target_cpp != NULL) && !found)
151 							{
152 								for (cflags = target_cpp; *cflags != NULL; cflags++)
153 								{
154 									if (strcmp (*cflags, cpp_flags) == 0)
155 									{
156 										found = TRUE;
157 										break;
158 									}
159 								}
160 							}
161 							if (found)
162 							{
163 								/* Add new module */
164 								AnjutaProjectNode *new_module;
165 
166 								new_module = amp_node_new_valid (target, ANJUTA_PROJECT_MODULE, NULL, name, NULL);
167 								anjuta_project_node_append (target, new_module);
168 							}
169 							g_free (cpp_flags);
170 						}
171 					}
172 					g_free (lib_flags);
173 				}
174 			}
175 			g_strfreev (target_cpp);
176 			g_strfreev (target_lib);
177 		}
178 	}
179 	g_strfreev (group_cpp);
180 
181 	return TRUE;
182 }
183 
184 
185 extern const gchar *valid_am_makefiles[];
186 
187 static AmpGroupNode*
project_load_makefile(AmpProject * project,AmpGroupNode * group)188 project_load_makefile (AmpProject *project, AmpGroupNode *group)
189 {
190 	const gchar **filename;
191 	AnjutaTokenFile *tfile;
192 	GFile *makefile = NULL;
193 	GFile *file = anjuta_project_node_get_file ((AnjutaProjectNode *)group);
194 
195 	/* Find makefile name
196 	 * It has to be in the config_files list with .am extension */
197 	for (filename = valid_am_makefiles; *filename != NULL; filename++)
198 	{
199 		makefile = g_file_get_child (file, *filename);
200 		if (file_type (file, *filename) == G_FILE_TYPE_REGULAR)
201 		{
202 			gchar *final_filename = g_strdup (*filename);
203 			gchar *ptr;
204 			GFile *final_file;
205 			AnjutaToken *token;
206 
207 			ptr = g_strrstr (final_filename,".am");
208 			if (ptr != NULL) *ptr = '\0';
209 			final_file = g_file_get_child (file, final_filename);
210 			g_free (final_filename);
211 
212 			token = amp_project_get_config_token (project, final_file);
213 			g_object_unref (final_file);
214 			if (token != NULL)
215 			{
216 				amp_group_node_add_token (group, token, AM_GROUP_TOKEN_CONFIGURE);
217 				break;
218 			}
219 		}
220 		g_object_unref (makefile);
221 	}
222 
223 	if (*filename == NULL)
224 	{
225 		/* Unable to find automake file */
226 		return group;
227 	}
228 
229 	/* Parse makefile.am */
230 	DEBUG_PRINT ("Parse: %s", g_file_get_uri (makefile));
231 	tfile = amp_group_node_set_makefile (group, makefile, project);
232 
233 	project_load_group_module (project, group);
234 
235 	return group;
236 }
237 
238 
239 /* Variable object
240  *---------------------------------------------------------------------------*/
241 
242 AmpVariable*
amp_variable_new(gchar * name,AnjutaTokenType assign,AnjutaToken * value)243 amp_variable_new (gchar *name, AnjutaTokenType assign, AnjutaToken *value)
244 {
245     AmpVariable *variable = NULL;
246 
247 	g_return_val_if_fail (name != NULL, NULL);
248 
249 	variable = g_slice_new0(AmpVariable);
250 	variable->name = g_strdup (name);
251 	variable->assign = assign;
252 	variable->value = value;
253 
254 	return variable;
255 }
256 
257 static void
amp_variable_free(AmpVariable * variable)258 amp_variable_free (AmpVariable *variable)
259 {
260 	g_free (variable->name);
261 
262     g_slice_free (AmpVariable, variable);
263 }
264 
265 
266 
267 /* Group objects
268  *---------------------------------------------------------------------------*/
269 
270 
271 void
amp_group_node_add_token(AmpGroupNode * group,AnjutaToken * token,AmpGroupNodeTokenCategory category)272 amp_group_node_add_token (AmpGroupNode *group, AnjutaToken *token, AmpGroupNodeTokenCategory category)
273 {
274 	if (token != NULL) group->tokens[category] = g_list_prepend (group->tokens[category], token);
275 }
276 
277 void
amp_group_node_remove_token(AmpGroupNode * group,AnjutaToken * token)278 amp_group_node_remove_token (AmpGroupNode *group, AnjutaToken *token)
279 {
280 	gint i;
281 
282 	for (i = 0; i < AM_GROUP_TOKEN_LAST; i++)
283 	{
284 		group->tokens[i] = g_list_remove (group->tokens[i], token);
285 	}
286 }
287 
288 GList *
amp_group_node_get_token(AmpGroupNode * group,AmpGroupNodeTokenCategory category)289 amp_group_node_get_token (AmpGroupNode *group, AmpGroupNodeTokenCategory category)
290 {
291 	return group->tokens[category];
292 }
293 
294 GList *
amp_group_node_get_all_token(AmpGroupNode * group)295 amp_group_node_get_all_token (AmpGroupNode *group)
296 {
297 	gint i;
298 	GList *tokens = NULL;
299 
300 	for (i = 0; i < AM_GROUP_TOKEN_LAST; i++)
301 	{
302 		tokens = g_list_concat (tokens, g_list_copy (group->tokens[i]));
303 	}
304 
305 	return tokens;
306 }
307 
308 AnjutaToken*
amp_group_node_get_first_token(AmpGroupNode * group,AmpGroupNodeTokenCategory category)309 amp_group_node_get_first_token (AmpGroupNode *group, AmpGroupNodeTokenCategory category)
310 {
311 	GList *list;
312 
313 	list = amp_group_node_get_token (group, category);
314 	if (list == NULL) return NULL;
315 
316 	return (AnjutaToken *)list->data;
317 }
318 
319 void
amp_group_node_set_dist_only(AmpGroupNode * group,gboolean dist_only)320 amp_group_node_set_dist_only (AmpGroupNode *group, gboolean dist_only)
321 {
322  	group->dist_only = dist_only;
323 }
324 
325 static void
on_group_monitor_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer data)326 on_group_monitor_changed (GFileMonitor *monitor,
327 											GFile *file,
328 											GFile *other_file,
329 											GFileMonitorEvent event_type,
330 											gpointer data)
331 {
332 	AnjutaProjectNode *node = ANJUTA_PROJECT_NODE (data);
333 	AnjutaProjectNode *root;
334 
335 	switch (event_type) {
336 		case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
337 		case G_FILE_MONITOR_EVENT_CHANGED:
338 		case G_FILE_MONITOR_EVENT_DELETED:
339 			/* project can be NULL, if the node is dummy node because the
340 			 * original one is reloaded. */
341 			root = anjuta_project_node_root (node);
342 			if (root != NULL) g_signal_emit_by_name (G_OBJECT (root), "file-changed", data);
343 			break;
344 		default:
345 			break;
346 	}
347 }
348 
349 static void
amp_group_node_update_preset_variable(AmpGroupNode * group)350 amp_group_node_update_preset_variable (AmpGroupNode *group)
351 {
352 	gchar *path;
353 	AnjutaToken *value;
354 	AmpVariable *var;
355 	GFile *root;
356 	GFile *file;
357 	AnjutaProjectNode *node;
358 
359 	if (group->preset_token != NULL) anjuta_token_free (group->preset_token);
360 	group->preset_token = anjuta_token_new_static (ANJUTA_TOKEN_FILE,  NULL);
361 
362 	/* Get project root */
363 	for (node = ANJUTA_PROJECT_NODE (group); anjuta_project_node_parent (node) != NULL; node = anjuta_project_node_parent (node));
364 	root = anjuta_project_node_get_file (node);
365 
366 	/* Set source directory variables */
367 	file = anjuta_project_node_get_file (group);
368 	value = anjuta_token_insert_token_list (FALSE, NULL,
369 	                                  ANJUTA_TOKEN_LIST, NULL,
370 	                                  ANJUTA_TOKEN_ARGUMENT, NULL,
371 	                                  ANJUTA_TOKEN_CONTENT, ".",
372 	                                  NULL);
373 	anjuta_token_append_child (group->preset_token, value);
374 	var = amp_variable_new ("srcdir", 0, value);
375 	g_hash_table_insert (group->variables, var->name, var);
376 	var = amp_variable_new ("builddir", 0, value);
377 	g_hash_table_insert (group->variables, var->name, var);
378 
379 	path = g_file_get_path (file);
380 	value = anjuta_token_insert_token_list (FALSE, NULL,
381 	                                  ANJUTA_TOKEN_LIST, NULL,
382 	                                  ANJUTA_TOKEN_ARGUMENT, NULL,
383 	                                  ANJUTA_TOKEN_CONTENT, path,
384 	                                  NULL);
385 	g_free (path);
386 	anjuta_token_append_child (group->preset_token, value);
387 	var = amp_variable_new ("abs_srcdir", 0, value);
388 	g_hash_table_insert (group->variables, var->name, var);
389 	var = amp_variable_new ("abs_builddir", 0, value);
390 	g_hash_table_insert (group->variables, var->name, var);
391 
392 	path = get_relative_path (file, root);
393 	value = anjuta_token_insert_token_list (FALSE, NULL,
394 	                                  ANJUTA_TOKEN_LIST, NULL,
395 	                                  ANJUTA_TOKEN_ARGUMENT, NULL,
396 	                                  ANJUTA_TOKEN_CONTENT, path,
397 	                                  NULL);
398 	g_free (path);
399 	anjuta_token_append_child (group->preset_token, value);
400 	var = amp_variable_new ("top_srcdir", 0, value);
401 	g_hash_table_insert (group->variables, var->name, var);
402 	var = amp_variable_new ("top_builddir", 0, value);
403 	g_hash_table_insert (group->variables, var->name, var);
404 
405 	path = g_file_get_path (root);
406 	value = anjuta_token_insert_token_list (FALSE, NULL,
407 	                                  ANJUTA_TOKEN_LIST, NULL,
408 	                                  ANJUTA_TOKEN_ARGUMENT, NULL,
409 	                                  ANJUTA_TOKEN_CONTENT, path,
410 	                                  NULL);
411 	g_free (path);
412 	anjuta_token_append_child (group->preset_token, value);
413 	var = amp_variable_new ("abs_top_srcdir", 0, value);
414 	g_hash_table_insert (group->variables, var->name, var);
415 	var = amp_variable_new ("abs_top_builddir", 0, value);
416 	g_hash_table_insert (group->variables, var->name, var);
417 }
418 
419 AnjutaTokenFile*
amp_group_node_set_makefile(AmpGroupNode * group,GFile * makefile,AmpProject * project)420 amp_group_node_set_makefile (AmpGroupNode *group, GFile *makefile, AmpProject *project)
421 {
422 	if (group->makefile != NULL) g_object_unref (group->makefile);
423 	if (group->tfile != NULL) anjuta_token_file_free (group->tfile);
424 	if (makefile != NULL)
425 	{
426 		AnjutaToken *token;
427 		AmpAmScanner *scanner;
428 		AnjutaProjectNode *source;
429 
430 		group->makefile = g_object_ref (makefile);
431 		group->tfile = anjuta_token_file_new (makefile);
432 		source = amp_source_node_new (makefile, ANJUTA_PROJECT_PROJECT | ANJUTA_PROJECT_FRAME | ANJUTA_PROJECT_READ_ONLY);
433 		anjuta_project_node_append (ANJUTA_PROJECT_NODE (group), source);
434 
435 		token = anjuta_token_file_load (group->tfile, NULL);
436 		amp_project_add_file (project, makefile, group->tfile);
437 
438 		amp_group_node_update_preset_variable (group);
439 
440 		scanner = amp_am_scanner_new (project, group);
441 		group->make_token = amp_am_scanner_parse_token (scanner, anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL), token, makefile, NULL);
442 		amp_am_scanner_free (scanner);
443 
444 		group->monitor = g_file_monitor_file (makefile,
445 						      									G_FILE_MONITOR_NONE,
446 						       									NULL,
447 						       									NULL);
448 		if (group->monitor != NULL)
449 		{
450 			g_signal_connect (G_OBJECT (group->monitor),
451 					  "changed",
452 					  G_CALLBACK (on_group_monitor_changed),
453 					  group);
454 		}
455 	}
456 	else
457 	{
458 		group->makefile = NULL;
459 		group->tfile = NULL;
460 		group->make_token = NULL;
461 		if (group->monitor) g_object_unref (group->monitor);
462 		group->monitor = NULL;
463 	}
464 
465 	return group->tfile;
466 }
467 
468 AnjutaToken*
amp_group_node_get_makefile_token(AmpGroupNode * group)469 amp_group_node_get_makefile_token (AmpGroupNode *group)
470 {
471 	return group->make_token;
472 }
473 
474 AnjutaTokenFile *
amp_group_node_get_make_token_file(AmpGroupNode * group)475 amp_group_node_get_make_token_file (AmpGroupNode *group)
476 {
477 	return group->tfile;
478 }
479 
480 gboolean
amp_group_node_update_makefile(AmpGroupNode * group,AnjutaToken * token)481 amp_group_node_update_makefile (AmpGroupNode *group, AnjutaToken *token)
482 {
483 	return anjuta_token_file_update (group->tfile, token);
484 }
485 
486 gchar *
amp_group_node_get_makefile_name(AmpGroupNode * group)487 amp_group_node_get_makefile_name (AmpGroupNode *group)
488 {
489 	gchar *basename = NULL;
490 
491 	if (group->makefile != NULL)
492 	{
493 		basename = g_file_get_basename (group->makefile);
494 	}
495 
496 	return basename;
497 }
498 
499 void
amp_group_node_update_node(AmpGroupNode * group,AmpGroupNode * new_group)500 amp_group_node_update_node (AmpGroupNode *group, AmpGroupNode *new_group)
501 {
502 	gint i;
503 	GHashTable *hash;
504 
505 	if (group->monitor != NULL)
506 	{
507 		g_object_unref (group->monitor);
508 		group->monitor = NULL;
509 	}
510 	if (group->makefile != NULL)
511 	{
512 		g_object_unref (group->makefile);
513 		group->monitor = NULL;
514 	}
515 	if (group->preset_token != NULL)
516 	{
517 		anjuta_token_free (group->preset_token);
518 		group->preset_token = NULL;
519 	}
520 	if (group->tfile) anjuta_token_file_free (group->tfile);
521 	for (i = 0; i < AM_GROUP_TOKEN_LAST; i++)
522 	{
523 		if (group->tokens[i] != NULL) g_list_free (group->tokens[i]);
524 	}
525 	if (group->variables) g_hash_table_remove_all (group->variables);
526 
527 	group->dist_only = new_group->dist_only;
528 	group->makefile = new_group->makefile;
529 	new_group->makefile = NULL;
530 	group->tfile = new_group->tfile;
531 	new_group->tfile = NULL;
532 	group->make_token = new_group->make_token;
533 	new_group->make_token = NULL;
534 	group->preset_token = new_group->preset_token;
535 	new_group->preset_token = NULL;
536 	memcpy (group->tokens, new_group->tokens, sizeof (group->tokens));
537 	memset (new_group->tokens, 0, sizeof (new_group->tokens));
538 	hash = group->variables;
539 	group->variables = new_group->variables;
540 	new_group->variables = hash;
541 
542 	if (group->makefile != NULL)
543 	{
544 		group->monitor = g_file_monitor_file (group->makefile,
545 					      									G_FILE_MONITOR_NONE,
546 					       									NULL,
547 					       									NULL);
548 		if (group->monitor != NULL)
549 		{
550 			g_signal_connect (G_OBJECT (group->monitor),
551 					  "changed",
552 					  G_CALLBACK (on_group_monitor_changed),
553 					  group);
554 		}
555 	}
556 }
557 
558 void
amp_group_node_update_variable(AmpGroupNode * group,AnjutaToken * variable)559 amp_group_node_update_variable (AmpGroupNode *group, AnjutaToken *variable)
560 {
561 	AnjutaToken *arg;
562 	char *name = NULL;
563 	AnjutaToken *value = NULL;
564 	AmpVariable *var;
565 
566 	arg = anjuta_token_first_item (variable);
567 	name = g_strstrip (anjuta_token_evaluate (arg));
568 	value = anjuta_token_last_item (variable);
569 
570 	var = (AmpVariable *)g_hash_table_lookup (group->variables, name);
571 	if (var != NULL)
572 	{
573 		var->value = value;
574 	}
575 	else
576 	{
577 		var = amp_variable_new (name, 0, value);
578 		g_hash_table_insert (group->variables, var->name, var);
579 	}
580 
581 	if (name) g_free (name);
582 }
583 
584 AnjutaToken*
amp_group_node_get_variable_token(AmpGroupNode * group,const gchar * name)585 amp_group_node_get_variable_token (AmpGroupNode *group, const gchar *name)
586 {
587 	AmpVariable *var;
588 
589 	var = g_hash_table_lookup (group->variables, name);
590 
591 	return var != NULL ? var->value : NULL;
592 }
593 
594 gboolean
amp_group_node_set_file(AmpGroupNode * group,GFile * new_file)595 amp_group_node_set_file (AmpGroupNode *group, GFile *new_file)
596 {
597 	if (group->base.file != NULL) g_object_unref (group->base.file);
598 	g_free (group->base.name);
599 	group->base.name = NULL;
600 	group->base.file = g_object_ref (new_file);
601 
602 	return TRUE;
603 }
604 
605 AmpGroupNode*
amp_group_node_new(GFile * file,const gchar * name,gboolean dist_only)606 amp_group_node_new (GFile *file, const gchar *name, gboolean dist_only)
607 {
608 	AmpGroupNode *node = NULL;
609 
610 	node = g_object_new (AMP_TYPE_GROUP_NODE, NULL);
611 	node->base.file = g_object_ref (file);
612 	node->base.name = g_strdup (name);
613 	node->dist_only = dist_only;
614 	memset (node->tokens, 0, sizeof (node->tokens));
615 
616     return node;
617 }
618 
619 AmpGroupNode*
amp_group_node_new_valid(GFile * file,const gchar * name,gboolean dist_only,GError ** error)620 amp_group_node_new_valid (GFile *file, const gchar *name, gboolean dist_only, GError **error)
621 {
622 	/* Validate group name */
623 	if (!name || strlen (name) <= 0)
624 	{
625 		g_free (name);
626 		error_set (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
627 			   _("Please specify group name"));
628 		return NULL;
629 	}
630 	{
631 		gboolean failed = FALSE;
632 		const gchar *ptr = name;
633 		while (*ptr) {
634 			if (!isalnum (*ptr) && (strchr ("#$:%+,-.=@^_`~/", *ptr) == NULL))
635 				failed = TRUE;
636 			ptr++;
637 		}
638 		if (failed) {
639 			g_free (name);
640 			error_set (error, IANJUTA_PROJECT_ERROR_VALIDATION_FAILED,
641 			           _("Group name can only contain alphanumeric or \"#$:%+,-.=@^_`~/\" characters"));
642 			return NULL;
643 		}
644 	}
645 
646 	return amp_group_node_new (file, name, dist_only);
647 }
648 
649 void
amp_group_node_free(AmpGroupNode * node)650 amp_group_node_free (AmpGroupNode *node)
651 {
652 	g_object_unref (G_OBJECT (node));
653 }
654 
655 /* AmpNode implementation
656  *---------------------------------------------------------------------------*/
657 
658 static gboolean
amp_group_node_load(AmpNode * group,AmpNode * parent,AmpProject * project,GError ** error)659 amp_group_node_load (AmpNode *group, AmpNode *parent, AmpProject *project, GError **error)
660 {
661 	if (project_load_makefile (project, AMP_GROUP_NODE (group)) == NULL)
662 	{
663 		g_set_error (error, IANJUTA_PROJECT_ERROR,
664 					IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
665 			_("Project doesn't exist or invalid path"));
666 
667 		return FALSE;
668 	}
669 
670 	return TRUE;
671 }
672 
673 static gboolean
amp_group_node_save(AmpNode * group,AmpNode * parent,AmpProject * project,GError ** error)674 amp_group_node_save (AmpNode *group, AmpNode *parent, AmpProject *project, GError **error)
675 {
676 	AnjutaTokenFile *tfile;
677 	AnjutaProjectNode *child;
678 	gboolean ok = TRUE;
679 	GFile *directory;
680 
681 	/* Check if Makefile.am is missing, it happens in po directory by example */
682 	if (AMP_GROUP_NODE (group)->makefile == NULL) return FALSE;
683 
684 	/* Create directory */
685 	directory = g_file_get_parent (AMP_GROUP_NODE (group)->makefile);
686 	g_file_make_directory (directory, NULL, NULL);
687 	g_object_unref (directory);
688 
689 	/* Save group */
690 	tfile = AMP_GROUP_NODE (group)->tfile;
691 	if (tfile == NULL)
692 	{
693 		/* Create an empty makefile */
694 		g_file_replace_contents (AMP_GROUP_NODE (group)->makefile, "", 0, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
695 
696 		return TRUE;
697 	}
698 
699 	if (anjuta_token_file_is_dirty (tfile))
700 	{
701 		if (!anjuta_token_file_save (tfile, error)) return FALSE;
702 	}
703 
704 	/* Save all children */
705 	for (child = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (group)); child != NULL; child = anjuta_project_node_next_sibling (child))
706 	{
707 		/* Try to save all children even if some fail */
708 		if (!amp_node_save (AMP_NODE (child), group, project, error)) ok = FALSE;
709 	}
710 
711 	return ok;
712 }
713 
714 static gboolean
amp_group_node_update(AmpNode * node,AmpNode * new_node)715 amp_group_node_update (AmpNode *node, AmpNode *new_node)
716 {
717 	amp_group_node_update_node (AMP_GROUP_NODE (node), AMP_GROUP_NODE (new_node));
718 
719 	return TRUE;
720 }
721 
722 static AmpNode *
amp_group_node_copy(AmpNode * old_node)723 amp_group_node_copy (AmpNode *old_node)
724 {
725 	AmpNode *new_node;
726 
727 	new_node = AMP_NODE_CLASS (amp_group_node_parent_class)->copy (old_node);
728 	amp_group_node_add_token (AMP_GROUP_NODE (new_node), amp_group_node_get_first_token (AMP_GROUP_NODE (old_node), AM_GROUP_TOKEN_CONFIGURE), AM_GROUP_TOKEN_CONFIGURE);
729 	amp_group_node_add_token (AMP_GROUP_NODE (new_node), amp_group_node_get_first_token (AMP_GROUP_NODE (old_node), AM_GROUP_TOKEN_SUBDIRS), AM_GROUP_TOKEN_SUBDIRS);
730 	amp_group_node_add_token (AMP_GROUP_NODE (new_node), amp_group_node_get_first_token (AMP_GROUP_NODE (old_node), AM_GROUP_TOKEN_DIST_SUBDIRS), AM_GROUP_TOKEN_DIST_SUBDIRS);
731 
732 	return new_node;
733 }
734 
735 static gboolean
amp_group_node_write(AmpNode * node,AmpNode * parent,AmpProject * project,GError ** error)736 amp_group_node_write (AmpNode *node, AmpNode *parent, AmpProject *project, GError **error)
737 {
738 	return amp_group_node_create_token (project, AMP_GROUP_NODE (node), error);
739 }
740 
741 static gboolean
amp_group_node_erase(AmpNode * node,AmpNode * parent,AmpProject * project,GError ** error)742 amp_group_node_erase (AmpNode *node, AmpNode *parent, AmpProject *project, GError **error)
743 {
744 	return amp_group_node_delete_token (project, AMP_GROUP_NODE (node), error);
745 }
746 
747 
748 
749 /* GObjet implementation
750  *---------------------------------------------------------------------------*/
751 
752 static void
amp_group_node_init(AmpGroupNode * node)753 amp_group_node_init (AmpGroupNode *node)
754 {
755 	node->base.type = ANJUTA_PROJECT_GROUP;
756 	node->base.properties_info = amp_get_group_property_list();
757 	node->base.state = ANJUTA_PROJECT_CAN_ADD_GROUP |
758 						ANJUTA_PROJECT_CAN_ADD_TARGET |
759 						ANJUTA_PROJECT_CAN_REMOVE |
760 						ANJUTA_PROJECT_CAN_SAVE;
761 	node->dist_only = FALSE;
762 	node->variables = NULL;
763 	node->makefile = NULL;
764 	node->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)amp_variable_free);
765 	node->monitor = NULL;
766 	memset (node->tokens, 0, sizeof (node->tokens));
767 	node->preset_token = NULL;
768 }
769 
770 static void
amp_group_node_dispose(GObject * object)771 amp_group_node_dispose (GObject *object)
772 {
773 	AmpGroupNode *node = AMP_GROUP_NODE (object);
774 
775 	if (node->monitor) g_object_unref (node->monitor);
776 	node->monitor = NULL;
777 
778 	if (node->preset_token) anjuta_token_free (node->preset_token);
779 	node->preset_token = NULL;
780 
781 	G_OBJECT_CLASS (amp_group_node_parent_class)->dispose (object);
782 }
783 
784 static void
amp_group_node_finalize(GObject * object)785 amp_group_node_finalize (GObject *object)
786 {
787 	AmpGroupNode *node = AMP_GROUP_NODE (object);
788 	gint i;
789 
790 	if (node->tfile) anjuta_token_file_free (node->tfile);
791 	if (node->makefile) g_object_unref (node->makefile);
792 
793 	for (i = 0; i < AM_GROUP_TOKEN_LAST; i++)
794 	{
795 		if (node->tokens[i] != NULL) g_list_free (node->tokens[i]);
796 	}
797 	if (node->variables) g_hash_table_destroy (node->variables);
798 
799 	G_OBJECT_CLASS (amp_group_node_parent_class)->finalize (object);
800 }
801 
802 static void
amp_group_node_class_init(AmpGroupNodeClass * klass)803 amp_group_node_class_init (AmpGroupNodeClass *klass)
804 {
805 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
806 	AmpNodeClass* node_class;
807 
808 	object_class->finalize = amp_group_node_finalize;
809 	object_class->dispose = amp_group_node_dispose;
810 
811 	node_class = AMP_NODE_CLASS (klass);
812 	node_class->load = amp_group_node_load;
813 	node_class->save = amp_group_node_save;
814 	node_class->update = amp_group_node_update;
815 	node_class->copy = amp_group_node_copy;
816 	node_class->write = amp_group_node_write;
817 	node_class->erase = amp_group_node_erase;
818 }
819 
820 static void
amp_group_node_class_finalize(AmpGroupNodeClass * klass)821 amp_group_node_class_finalize (AmpGroupNodeClass *klass)
822 {
823 }
824 
825 void
amp_group_node_register(GTypeModule * module)826 amp_group_node_register (GTypeModule *module)
827 {
828 	amp_group_node_register_type (module);
829 }
830