1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta-session.c
4  * Copyright (c) 2005 Naba Kumar  <naba@gnome.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (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
14  * GNU Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:anjuta-session
23  * @short_description: Store local user settings
24  * @see_also:
25  * @stability: Unstable
26  * @include: libanjuta/anjuta-session.h
27  *
28  * A anjuta session contains local user settings, by example the list of files
29  * open in one project. These settings are stored in
30  * a .ini-like config file in a directory named session. Other libraries can
31  * store their own settings in another format in the same directory.
32  */
33 
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "anjuta-session.h"
38 #include "anjuta-utils.h"
39 
40 struct _AnjutaSessionPriv {
41 	gchar *dir_path;
42 	GKeyFile *key_file;
43 };
44 
45 static gpointer *parent_class = NULL;
46 
47 static void
anjuta_session_finalize(GObject * object)48 anjuta_session_finalize (GObject *object)
49 {
50 	AnjutaSession *cobj;
51 	cobj = ANJUTA_SESSION (object);
52 
53 	g_free (cobj->priv->dir_path);
54 	g_key_file_free (cobj->priv->key_file);
55 	g_free (cobj->priv);
56 
57 	G_OBJECT_CLASS(parent_class)->finalize(object);
58 }
59 
60 static void
anjuta_session_class_init(AnjutaSessionClass * klass)61 anjuta_session_class_init (AnjutaSessionClass *klass)
62 {
63 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
64 
65 	parent_class = g_type_class_peek_parent (klass);
66 	object_class->finalize = anjuta_session_finalize;
67 }
68 
69 static void
anjuta_session_instance_init(AnjutaSession * obj)70 anjuta_session_instance_init (AnjutaSession *obj)
71 {
72 	obj->priv = g_new0 (AnjutaSessionPriv, 1);
73 	obj->priv->dir_path = NULL;
74 }
75 
76 /**
77  * anjuta_session_new:
78  * @session_directory: Directory where session is loaded from/saved to.
79  *
80  * Created a new session object. @session_directory is the directory
81  * where session information will be stored or loaded in case of existing
82  * session.
83  *
84  * Returns: an #AnjutaSession Object
85  */
86 AnjutaSession*
anjuta_session_new(const gchar * session_directory)87 anjuta_session_new (const gchar *session_directory)
88 {
89 	AnjutaSession *obj;
90 	gchar *filename;
91 
92 	g_return_val_if_fail (session_directory != NULL, NULL);
93 	g_return_val_if_fail (g_path_is_absolute (session_directory), NULL);
94 
95 	obj = ANJUTA_SESSION (g_object_new (ANJUTA_TYPE_SESSION, NULL));
96 	obj->priv->dir_path = g_strdup (session_directory);
97 
98 	obj->priv->key_file = g_key_file_new ();
99 
100 	filename = anjuta_session_get_session_filename (obj);
101 	g_key_file_load_from_file (obj->priv->key_file, filename,
102 							   G_KEY_FILE_NONE, NULL);
103 	g_free (filename);
104 
105 	return obj;
106 }
107 
ANJUTA_TYPE_BOILERPLATE(AnjutaSession,anjuta_session,G_TYPE_OBJECT)108 ANJUTA_TYPE_BOILERPLATE (AnjutaSession, anjuta_session, G_TYPE_OBJECT)
109 
110 /**
111  * anjuta_session_get_session_directory:
112  * @session: an #AnjutaSession object
113  *
114  * Returns the directory corresponding to this session object.
115  *
116  * Returns: session directory
117  */
118 const gchar*
119 anjuta_session_get_session_directory (AnjutaSession *session)
120 {
121 	return session->priv->dir_path;
122 }
123 
124 /**
125  * anjuta_session_get_session_filename:
126  * @session: an #AnjutaSession object
127  *
128  * Gets the session filename corresponding to this session object.
129  *
130  * Returns: session (absolute) filename
131  */
132 gchar*
anjuta_session_get_session_filename(AnjutaSession * session)133 anjuta_session_get_session_filename (AnjutaSession *session)
134 {
135 	g_return_val_if_fail (ANJUTA_IS_SESSION (session), NULL);
136 
137 	return g_build_filename (session->priv->dir_path,
138 							 "anjuta.session", NULL);
139 }
140 
141 /**
142  * anjuta_session_sync:
143  * @session: an #AnjutaSession object
144  *
145  * Synchronizes session object with session file
146  */
147 void
anjuta_session_sync(AnjutaSession * session)148 anjuta_session_sync (AnjutaSession *session)
149 {
150 	gchar *filename, *data;
151 
152 	g_return_if_fail (ANJUTA_IS_SESSION (session));
153 
154 	filename = anjuta_session_get_session_filename (session);
155 	data = g_key_file_to_data (session->priv->key_file, NULL, NULL);
156 	g_file_set_contents (filename, data, -1, NULL);
157 
158 	g_free (filename);
159 	g_free (data);
160 }
161 
162 /**
163  * anjuta_session_clear:
164  * @session: an #AnjutaSession object
165  *
166  * Clears the session.
167  */
168 void
anjuta_session_clear(AnjutaSession * session)169 anjuta_session_clear (AnjutaSession *session)
170 {
171 	gchar *cmd;
172 	gchar *quoted;
173 
174 	g_return_if_fail (ANJUTA_IS_SESSION (session));
175 
176 	g_key_file_free (session->priv->key_file);
177 	session->priv->key_file = g_key_file_new ();
178 
179 	anjuta_session_sync (session);
180 
181 	quoted = g_shell_quote (session->priv->dir_path);
182 	cmd = g_strconcat ("rm -fr ", quoted, NULL);
183 	system (cmd);
184 	g_free (cmd);
185 
186 	cmd = g_strconcat ("mkdir -p ", quoted, NULL);
187 	system (cmd);
188 	g_free (cmd);
189 	g_free (quoted);
190 }
191 
192 /**
193  * anjuta_session_clear_section:
194  * @session: an #AnjutaSession object.
195  * @section: Section to clear.
196  *
197  * Clears the given section in session object.
198  */
199 void
anjuta_session_clear_section(AnjutaSession * session,const gchar * section)200 anjuta_session_clear_section (AnjutaSession *session,
201 							  const gchar *section)
202 {
203 	g_return_if_fail (ANJUTA_IS_SESSION (session));
204 	g_return_if_fail (section != NULL);
205 
206 	g_key_file_remove_group (session->priv->key_file, section, NULL);
207 }
208 
209 /**
210  * anjuta_session_set_int:
211  * @session: an #AnjutaSession object
212  * @section: Section.
213  * @key: Key name.
214  * @value: Key value
215  *
216  * Set an integer @value to @key in given @section.
217  */
218 void
anjuta_session_set_int(AnjutaSession * session,const gchar * section,const gchar * key,gint value)219 anjuta_session_set_int (AnjutaSession *session, const gchar *section,
220 						const gchar *key, gint value)
221 {
222 	g_return_if_fail (ANJUTA_IS_SESSION (session));
223 	g_return_if_fail (section != NULL);
224 	g_return_if_fail (key != NULL);
225 
226 	if (!value)
227 	{
228 		g_key_file_remove_key (session->priv->key_file, section, key, NULL);
229 		return;
230 	}
231 
232 	g_key_file_set_integer (session->priv->key_file, section, key, value);
233 }
234 
235 /**
236  * anjuta_session_set_float:
237  * @session: an #AnjutaSession object
238  * @section: Section.
239  * @key: Key name.
240  * @value: Key value
241  *
242  * Set a float @value to @key in given @section.
243  */
244 void
anjuta_session_set_float(AnjutaSession * session,const gchar * section,const gchar * key,gfloat value)245 anjuta_session_set_float (AnjutaSession *session, const gchar *section,
246 						  const gchar *key, gfloat value)
247 {
248 	g_return_if_fail (ANJUTA_IS_SESSION (session));
249 	g_return_if_fail (section != NULL);
250 	g_return_if_fail (key != NULL);
251 
252 	if (!value)
253 	{
254 		g_key_file_remove_key (session->priv->key_file, section, key, NULL);
255 		return;
256 	}
257 
258 	g_key_file_set_double (session->priv->key_file, section, key, value);
259 }
260 
261 /**
262  * anjuta_session_set_string:
263  * @session: an #AnjutaSession object
264  * @section: Section.
265  * @key: Key name.
266  * @value: Key value
267  *
268  * Set a string @value to @key in given @section.
269  */
270 void
anjuta_session_set_string(AnjutaSession * session,const gchar * section,const gchar * key,const gchar * value)271 anjuta_session_set_string (AnjutaSession *session, const gchar *section,
272 						   const gchar *key, const gchar *value)
273 {
274 	g_return_if_fail (ANJUTA_IS_SESSION (session));
275 	g_return_if_fail (section != NULL);
276 	g_return_if_fail (key != NULL);
277 
278 	if (!value)
279 	{
280 		g_key_file_remove_key (session->priv->key_file, section, key, NULL);
281 		return;
282 	}
283 
284 	g_key_file_set_string (session->priv->key_file, section, key, value);
285 }
286 
287 /**
288  * anjuta_session_set_string_list:
289  * @session: an #AnjutaSession object
290  * @section: Section.
291  * @key: Key name.
292  * @value: Key value
293  *
294  * Set a list of strings @value to @key in given @section.
295  */
296 void
anjuta_session_set_string_list(AnjutaSession * session,const gchar * section,const gchar * key,GList * value)297 anjuta_session_set_string_list (AnjutaSession *session,
298 								const gchar *section,
299 								const gchar *key, GList *value)
300 {
301 	gchar *value_str;
302 	GString *str;
303 	GList *node;
304 	gboolean first_item = TRUE;
305 
306 	g_return_if_fail (ANJUTA_IS_SESSION (session));
307 	g_return_if_fail (section != NULL);
308 	g_return_if_fail (key != NULL);
309 
310 	if (!value)
311 	{
312 		g_key_file_remove_key (session->priv->key_file, section, key, NULL);
313 		return;
314 	}
315 
316 	str = g_string_new ("");
317 	node = value;
318 	while (node)
319 	{
320 		/* Keep empty string */
321 		if (node->data != NULL)
322 		{
323 			if (first_item)
324 				first_item = FALSE;
325 			else
326 				g_string_append (str, "%%%");
327 			g_string_append (str, node->data);
328 		}
329 		node = g_list_next (node);
330 	}
331 
332 	value_str = g_string_free (str, FALSE);
333 	g_key_file_set_string (session->priv->key_file, section, key, value_str);
334 
335 	g_free (value_str);
336 }
337 
338 /**
339  * anjuta_session_get_int:
340  * @session: an #AnjutaSession object
341  * @section: Section.
342  * @key: Key name.
343  *
344  * Get an integer @value of @key in given @section.
345  *
346  * Returns: Key value
347  */
348 gint
anjuta_session_get_int(AnjutaSession * session,const gchar * section,const gchar * key)349 anjuta_session_get_int (AnjutaSession *session, const gchar *section,
350 						const gchar *key)
351 {
352 	gint value;
353 
354 	g_return_val_if_fail (ANJUTA_IS_SESSION (session), 0);
355 	g_return_val_if_fail (section != NULL, 0);
356 	g_return_val_if_fail (key != NULL, 0);
357 
358 	value = g_key_file_get_integer (session->priv->key_file, section, key, NULL);
359 
360 	return value;
361 }
362 
363 /**
364  * anjuta_session_get_float:
365  * @session: an #AnjutaSession object
366  * @section: Section.
367  * @key: Key name.
368  *
369  * Get a float @value of @key in given @section.
370  *
371  * Returns: Key value
372  */
373 gfloat
anjuta_session_get_float(AnjutaSession * session,const gchar * section,const gchar * key)374 anjuta_session_get_float (AnjutaSession *session, const gchar *section,
375 						  const gchar *key)
376 {
377 	gfloat value;
378 
379 	g_return_val_if_fail (ANJUTA_IS_SESSION (session), 0);
380 	g_return_val_if_fail (section != NULL, 0);
381 	g_return_val_if_fail (key != NULL, 0);
382 
383 	value = (float)g_key_file_get_double (session->priv->key_file, section, key, NULL);
384 
385 	return value;
386 }
387 
388 /**
389  * anjuta_session_get_string:
390  * @session: an #AnjutaSession object
391  * @section: Section.
392  * @key: Key name.
393  *
394  * Get a string @value of @key in given @section.
395  *
396  * Returns: Key value
397  */
398 gchar*
anjuta_session_get_string(AnjutaSession * session,const gchar * section,const gchar * key)399 anjuta_session_get_string (AnjutaSession *session, const gchar *section,
400 						   const gchar *key)
401 {
402 	gchar *value;
403 
404 	g_return_val_if_fail (ANJUTA_IS_SESSION (session), NULL);
405 	g_return_val_if_fail (section != NULL, NULL);
406 	g_return_val_if_fail (key != NULL, NULL);
407 
408 	value = g_key_file_get_string (session->priv->key_file, section, key, NULL);
409 
410 	return value;
411 }
412 
413 /**
414  * anjuta_session_get_string_list:
415  * @session: an #AnjutaSession object
416  * @section: Section.
417  * @key: Key name.
418  *
419  * Get a list of strings @value of @key in given @section.
420  *
421  * Returns: Key value
422  */
423 GList*
anjuta_session_get_string_list(AnjutaSession * session,const gchar * section,const gchar * key)424 anjuta_session_get_string_list (AnjutaSession *session,
425 								const gchar *section,
426 								const gchar *key)
427 {
428 	gchar *val, **str, **ptr;
429 	GList *value;
430 
431 	g_return_val_if_fail (ANJUTA_IS_SESSION (session), NULL);
432 	g_return_val_if_fail (section != NULL, NULL);
433 	g_return_val_if_fail (key != NULL, NULL);
434 
435 	val = g_key_file_get_string (session->priv->key_file, section, key, NULL);
436 
437 
438 	value = NULL;
439 	if (val)
440 	{
441 		str = g_strsplit (val, "%%%", -1);
442 		if (str)
443 		{
444 			ptr = str;
445 			while (*ptr)
446 			{
447 				/* Keep empty string */
448 				value = g_list_prepend (value, g_strdup (*ptr));
449 				ptr++;
450 			}
451 			g_strfreev (str);
452 		}
453 		g_free (val);
454 	}
455 
456 	return g_list_reverse (value);
457 }
458 
459 
460 /**
461  * anjuta_session_get_relative_uri_from_file:
462  * @session: an #AnjutaSession object
463  * @file: a GFile
464  * @fragment: an optional fragment
465  *
466  * Return an URI relative to the session directory file with an optional
467  * fragment.
468  * It is useful to keep only relative file paths in a session file to be able
469  * to copy the whole project without breaking references.
470  *
471  * Returns: (transfer full): A string that has to be freed with g_free().
472  */
473 gchar *
anjuta_session_get_relative_uri_from_file(AnjutaSession * session,GFile * file,const gchar * fragment)474 anjuta_session_get_relative_uri_from_file (AnjutaSession *session,
475                                            GFile *file,
476                                            const gchar *fragment)
477 {
478 	GFile *parent;
479 	gchar *uri;
480 	gint level;
481 
482 	parent = g_file_new_for_path (session->priv->dir_path);
483 	for (level = 0; (parent != NULL) && !g_file_equal (file, parent) && !g_file_has_prefix (file, parent); level++)
484 	{
485 		GFile *next = g_file_get_parent (parent);
486 		g_object_unref (parent);
487 		parent = next;
488 	}
489 
490 	if (parent == NULL)
491 	{
492 		uri = g_file_get_uri (file);
493 	}
494 	else
495 	{
496 		gchar *path;
497 
498 		if (g_file_equal (file, parent))
499 		{
500 			uri = g_strdup(".");
501 		}
502 		else
503 		{
504 			path = g_file_get_relative_path (parent, file);
505 			uri = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
506 			g_free (path);
507 		}
508 		if (level != 0)
509 		{
510 			gsize len;
511 			gchar *buffer;
512 			gchar *ptr;
513 
514 			len = strlen (uri);
515 			buffer = g_new (gchar, len + level * 3 + 1);
516 			for (ptr = buffer; level; level--)
517 			{
518 				memcpy (ptr, ".." G_DIR_SEPARATOR_S, 3);
519 				ptr += 3;
520 			}
521 			memcpy (ptr, uri, len + 1);
522 			g_free (uri);
523 
524 			uri = buffer;
525 		}
526 	}
527 
528 	if (fragment != NULL)
529 	{
530 		gchar *with_fragment;
531 
532 		with_fragment = g_strconcat (uri, "#", fragment, NULL);
533 		g_free (uri);
534 		uri = with_fragment;
535 	}
536 
537 	return uri;
538 }
539 
540 
541 /**
542  * anjuta_session_get_file_from_relative_uri:
543  * @session: an #AnjutaSession object
544  * @uri: a relative URI from a key
545  * @fragment: (allow-none): fragment part of the URI if existing, can be %NULL
546  *
547  * Return a GFile corresponding to the URI and and optional fragment,
548  * normally read from a session key.
549  * The path is expected to be relative to the session directory but it works
550  * with an absolute URI, in this case it returns the same file than
551  * g_file_new_for_uri.
552  * It is useful to keep only relative file paths in a session file to be able
553  * to copy the whole project without breaking references.
554  *
555  * Returns: (transfer full): A new GFile that has to be freed with g_object_unref().
556  */
557 GFile*
anjuta_session_get_file_from_relative_uri(AnjutaSession * session,const gchar * uri,const gchar ** fragment)558 anjuta_session_get_file_from_relative_uri (AnjutaSession *session,
559                                            const gchar *uri,
560                                            const gchar **fragment)
561 {
562 	GFile *file;
563 	gchar *scheme;
564 
565 	scheme =g_uri_parse_scheme (uri);
566 	if (scheme != NULL)
567 	{
568 		free (scheme);
569 		file = g_file_new_for_uri (uri);
570 	}
571 	else
572 	{
573 		gchar *parent_uri = g_filename_to_uri (session->priv->dir_path, NULL, NULL);
574 		gchar *full_uri;
575 
576 		full_uri = g_strconcat (parent_uri, G_DIR_SEPARATOR_S, uri, NULL);
577 		file = g_file_new_for_uri (full_uri);
578 		g_free (full_uri);
579 		g_free (parent_uri);
580 	}
581 	if (fragment != NULL)
582 	{
583 		*fragment = strchr (uri, '#');
584 		if (*fragment != NULL) (*fragment)++;
585 	}
586 
587 	return file;
588 }
589 
590