python-loader: Fix build with python3 (bgo#633786)
[anjuta.git] / libanjuta / anjuta-token-file.c
blob3b3a1f683b2e6274a0ad9a4c16ebf1605c1e60a7
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-token-file.c
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "anjuta-token-file.h"
22 #include "anjuta-debug.h"
24 #include <glib-object.h>
26 #include <stdio.h>
27 #include <string.h>
29 /* Types declarations
30 *---------------------------------------------------------------------------*/
32 struct _AnjutaTokenFile
34 GObject parent;
36 GFile* file; /* Corresponding GFile */
38 AnjutaToken *content; /* Current file content */
40 AnjutaToken *save; /* List of memory block used */
42 gboolean dirty; /* Set when the file has been modified */
45 struct _AnjutaTokenFileClass
47 GObjectClass parent_class;
50 static GObjectClass *parent_class = NULL;
52 /* Helpers functions
53 *---------------------------------------------------------------------------*/
55 /* Private functions
56 *---------------------------------------------------------------------------*/
58 static AnjutaToken*
59 anjuta_token_file_find_position (AnjutaTokenFile *file, AnjutaToken *token)
61 AnjutaToken *start;
62 const gchar *pos;
63 const gchar *ptr;
64 const gchar *end;
66 if (token == NULL) return NULL;
68 if (anjuta_token_get_length (token) == 0)
70 AnjutaToken *last = anjuta_token_last (token);
72 for (; (token != NULL) && (token != last); token = anjuta_token_next (token))
74 if (anjuta_token_get_length (token) != 0) break;
77 if (anjuta_token_get_length (token) == 0) return NULL;
80 pos = anjuta_token_get_string (token);
81 for (start = file->content; start != NULL; start = anjuta_token_next (start))
83 guint len = anjuta_token_get_length (start);
85 if (len)
87 ptr = anjuta_token_get_string (start);
88 end = ptr + len;
90 if ((pos >= ptr) && (pos < end)) break;
93 if ((start != NULL) && (ptr != pos))
95 start = anjuta_token_split (start, pos - ptr);
96 start = anjuta_token_next (start);
99 return start;
102 /* Public functions
103 *---------------------------------------------------------------------------*/
105 AnjutaToken*
106 anjuta_token_file_load (AnjutaTokenFile *file, GError **error)
108 gchar *content;
109 gsize length;
111 anjuta_token_file_unload (file);
113 file->save = anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL);
114 file->content = anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL);
116 if (g_file_load_contents (file->file, NULL, &content, &length, NULL, error))
118 AnjutaToken *token;
120 token = anjuta_token_new_string_len (ANJUTA_TOKEN_FILE, content, length);
121 anjuta_token_prepend_child (file->save, token);
123 token = anjuta_token_new_static (ANJUTA_TOKEN_FILE, content);
124 anjuta_token_prepend_child (file->content, token);
125 file->dirty = FALSE;
128 return file->content;
131 gboolean
132 anjuta_token_file_unload (AnjutaTokenFile *file)
134 if (file->content != NULL) anjuta_token_free (file->content);
135 file->content = NULL;
137 if (file->save != NULL) anjuta_token_free (file->save);
138 file->save = NULL;
140 return TRUE;
143 gboolean
144 anjuta_token_file_save (AnjutaTokenFile *file, GError **error)
146 GFileOutputStream *stream;
147 gboolean ok = TRUE;
148 GError *err = NULL;
149 AnjutaToken *token;
151 stream = g_file_replace (file->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &err);
152 if (stream == NULL)
154 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
156 /* Perhaps parent directory is missing, try to create it */
157 GFile *parent = g_file_get_parent (file->file);
159 if (g_file_make_directory_with_parents (parent, NULL, NULL))
161 g_object_unref (parent);
162 g_clear_error (&err);
163 stream = g_file_replace (file->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
164 if (stream == NULL) return FALSE;
166 else
168 g_object_unref (parent);
169 g_propagate_error (error, err);
171 return FALSE;
174 else
176 g_propagate_error (error, err);
177 return FALSE;
181 for (token = file->content; token != NULL; token = anjuta_token_next (token))
183 if (!(anjuta_token_get_flags (token) & ANJUTA_TOKEN_REMOVED) && (anjuta_token_get_length (token)))
185 if (g_output_stream_write (G_OUTPUT_STREAM (stream), anjuta_token_get_string (token), anjuta_token_get_length (token) * sizeof (char), NULL, error) < 0)
187 ok = FALSE;
188 break;
193 ok = ok && g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL);
194 g_object_unref (stream);
195 file->dirty = FALSE;
197 return ok;
200 void
201 anjuta_token_file_move (AnjutaTokenFile *file, GFile *new_file)
203 if (file->file) g_object_unref (file->file);
204 file->file = new_file != NULL ? g_object_ref (new_file) : NULL;
205 file->dirty = new_file != NULL;
208 static void
209 remove_raw_token (AnjutaToken *token, gpointer user_data)
211 AnjutaTokenFile *file = (AnjutaTokenFile *)user_data;
213 if ((anjuta_token_get_length (token) > 0))
215 AnjutaToken *pos = anjuta_token_file_find_position (file, token);
216 guint len = anjuta_token_get_length (token);
218 if (pos != NULL)
220 while (len != 0)
222 guint flen = anjuta_token_get_length (pos);
223 if (len < flen)
225 pos = anjuta_token_split (pos, len);
226 flen = len;
228 pos = anjuta_token_free (pos);
229 len -= flen;
235 static AnjutaToken *
236 anjuta_token_file_remove_token (AnjutaTokenFile *file, AnjutaToken *token)
238 AnjutaToken *next = NULL;
240 if (token != NULL) next = anjuta_token_foreach_post_order (token, remove_raw_token, file);
242 next = anjuta_token_free (token);
243 file->dirty = TRUE;
245 return next;
249 * anjuta_token_file_update:
250 * @file: a #AnjutaTokenFile derived class object.
251 * @token: Token to update.
253 * Update the file with all changed token starting from @token. The function can
254 * return an error if the token is not in the file.
256 * Return value: TRUE is the update is done without error.
258 gboolean
259 anjuta_token_file_update (AnjutaTokenFile *file, AnjutaToken *token)
261 AnjutaToken *prev;
262 AnjutaToken *next;
263 AnjutaToken *last;
264 guint added;
266 /* Find all token needing an update */
268 /* Find following tokens */
269 for (last = token; last != NULL; last = anjuta_token_next (last))
271 /* Get all tokens in group */
272 last = anjuta_token_last (last);
274 gint flags = anjuta_token_get_flags (last);
275 if (!(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
278 /* Find first modified token */
279 for (;;)
281 gint flags = anjuta_token_get_flags (token);
282 if (flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))
284 /* Check previous token */
285 for (prev = token; prev != NULL; prev = anjuta_token_previous (prev))
287 gint flags = anjuta_token_get_flags (prev);
288 if (!(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
289 token = prev;
292 break;
294 if (token == last)
296 /* No changed */
297 return TRUE;
299 token = anjuta_token_next (token);
302 /* Find previous token */
303 for (prev = token; prev != NULL; prev = anjuta_token_previous (prev))
305 gint flags = anjuta_token_get_flags (prev);
306 if ((anjuta_token_get_length (prev) != 0) && !(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
309 /* Delete removed token and compute length of added token */
310 added = 0;
311 for (next = token; (next != NULL) && (next != last);)
313 gint flags = anjuta_token_get_flags (next);
315 if (flags & ANJUTA_TOKEN_REMOVED)
317 next = anjuta_token_file_remove_token (file, next);
318 continue;
320 else if (flags & ANJUTA_TOKEN_ADDED)
322 added += anjuta_token_get_length (next);
324 next = anjuta_token_next (next);
327 /* Add new token */
328 if (added != 0)
330 gchar *value;
331 AnjutaToken *add;
332 AnjutaToken *start = NULL;
334 value = g_new (gchar, added);
335 add = anjuta_token_prepend_child (file->save, anjuta_token_new_string_len (ANJUTA_TOKEN_NAME, value, added));
337 /* Find token position */
338 if (prev != NULL)
340 start = anjuta_token_file_find_position (file, prev);
341 if (start != NULL) start = anjuta_token_split (start, anjuta_token_get_length (prev));
344 /* Insert token */
345 add = anjuta_token_new_static_len (ANJUTA_TOKEN_NAME, value, added);
346 if (start == NULL)
348 anjuta_token_prepend_child (file->content, add);
350 else
352 anjuta_token_insert_after (start, add);
355 for (next = token; (next != NULL) && (next != last); next = anjuta_token_next (next))
357 gint flags = anjuta_token_get_flags (next);
360 if (flags & ANJUTA_TOKEN_ADDED)
362 guint len = anjuta_token_get_length (next);
364 if (len > 0)
366 memcpy(value, anjuta_token_get_string (next), len);
367 anjuta_token_set_string (next, value, len);
368 value += len;
370 anjuta_token_clear_flags (next, ANJUTA_TOKEN_ADDED);
374 file->dirty = TRUE;
376 return TRUE;
380 * anjuta_token_file_get_token_position:
381 * @file: #AnjutaTokenFile object
382 * @token: token
384 * Returns the position of the token in the file. This position is a number
385 * which doesn't correspond to a line number or a character but respect the
386 * order of token in the file.
388 * Returns: The position of the token or 0 if the token is not in the file.
390 gsize
391 anjuta_token_file_get_token_position (AnjutaTokenFile *file, AnjutaToken *token)
393 AnjutaToken *content;
394 const gchar *string;
395 gsize pos = 1;
400 string = anjuta_token_get_string (token);
401 if (string != NULL) break;
403 /* token is a group or an empty token, looks for group members or
404 * following token */
405 token = anjuta_token_next_after_children (token);
406 } while (token != NULL);
408 for (content = file->content; content != NULL; content = anjuta_token_next (content))
410 const gchar *start;
411 gsize len;
413 start = anjuta_token_get_string (content);
414 len = anjuta_token_get_length (content);
416 if ((string >= start) && ((string - start) < len))
418 pos += string - start;
419 break;
421 else
423 pos += len;
427 return content == NULL ? 0 : pos;
431 gboolean
432 anjuta_token_file_get_token_location (AnjutaTokenFile *file, AnjutaTokenFileLocation *location, AnjutaToken *token)
434 AnjutaTokenFileLocation loc = {NULL, 1, 1};
435 AnjutaToken *pos;
436 const gchar *target;
438 anjuta_token_dump (token);
441 target = anjuta_token_get_string (token);
442 if (target != NULL) break;
444 /* token is a group or an empty token, looks for group members or
445 * following token */
446 token = anjuta_token_next_after_children (token);
447 } while (token != NULL);
449 for (pos = file->content; pos != NULL; pos = anjuta_token_next (pos))
451 if (!(anjuta_token_get_flags (pos) & ANJUTA_TOKEN_REMOVED) && (anjuta_token_get_length (pos) > 0))
453 const gchar *ptr;
454 const gchar *end;
456 ptr = anjuta_token_get_string (pos);
457 end = ptr + anjuta_token_get_length (pos);
459 for (; ptr != end; ptr++)
461 if (*ptr == '\n')
463 /* New line */
464 loc.line++;
465 loc.column = 1;
467 else
469 loc.column++;
472 if (ptr == target)
474 if (location != NULL)
476 location->filename = file->file == NULL ? NULL : g_file_get_parse_name (file->file);
477 location->line = loc.line;
478 location->column = loc.column;
481 return TRUE;
487 return FALSE;
490 GFile*
491 anjuta_token_file_get_file (AnjutaTokenFile *file)
493 return file->file;
496 AnjutaToken*
497 anjuta_token_file_get_content (AnjutaTokenFile *file)
499 if (file->content == NULL)
501 anjuta_token_file_load (file, NULL);
504 return file->content;
507 gboolean
508 anjuta_token_file_is_dirty (AnjutaTokenFile *file)
510 return file->dirty;
513 /* GObject functions
514 *---------------------------------------------------------------------------*/
516 /* dispose is the first destruction step. It is used to unref object created
517 * with instance_init in order to break reference counting cycles. This
518 * function could be called several times. All function should still work
519 * after this call. It has to called its parents.*/
521 static void
522 anjuta_token_file_dispose (GObject *object)
524 AnjutaTokenFile *file = ANJUTA_TOKEN_FILE (object);
526 anjuta_token_file_unload (file);
528 if (file->file) g_object_unref (file->file);
529 file->file = NULL;
531 G_OBJECT_CLASS (parent_class)->dispose (object);
534 /* instance_init is the constructor. All functions should work after this
535 * call. */
537 static void
538 anjuta_token_file_instance_init (AnjutaTokenFile *file)
540 file->file = NULL;
541 file->content = NULL;
542 file->save = NULL;
545 /* class_init intialize the class itself not the instance */
547 static void
548 anjuta_token_file_class_init (AnjutaTokenFileClass * klass)
550 GObjectClass *gobject_class;
552 g_return_if_fail (klass != NULL);
554 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
555 gobject_class = G_OBJECT_CLASS (klass);
557 gobject_class->dispose = anjuta_token_file_dispose;
560 GType
561 anjuta_token_file_get_type (void)
563 static GType type = 0;
565 if (!type)
567 static const GTypeInfo type_info =
569 sizeof (AnjutaTokenFileClass),
570 (GBaseInitFunc) NULL,
571 (GBaseFinalizeFunc) NULL,
572 (GClassInitFunc) anjuta_token_file_class_init,
573 (GClassFinalizeFunc) NULL,
574 NULL, /* class_data */
575 sizeof (AnjutaTokenFile),
576 0, /* n_preallocs */
577 (GInstanceInitFunc) anjuta_token_file_instance_init,
578 NULL /* value_table */
581 type = g_type_register_static (G_TYPE_OBJECT,
582 "AnjutaTokenFile", &type_info, 0);
585 return type;
589 /* Constructor & Destructor
590 *---------------------------------------------------------------------------*/
592 AnjutaTokenFile *
593 anjuta_token_file_new (GFile *gfile)
595 AnjutaTokenFile *file = g_object_new (ANJUTA_TOKEN_FILE_TYPE, NULL);
597 if (gfile) file->file = g_object_ref (gfile);
598 file->dirty = gfile != NULL;
600 return file;
603 void
604 anjuta_token_file_free (AnjutaTokenFile *tfile)
606 g_object_unref (tfile);