Contents of /trunk/src/diff.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (hide annotations)
Wed Dec 10 19:50:17 2008 UTC (15 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 19377 byte(s)
Asynchronous network io
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go 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 3 of the License, or
9     * (at your option) any later version.
10     *
11     * OSM2Go 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 General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     /*
21     * diff.c - generate and restore changes on the current data set
22     */
23    
24     #include "appdata.h"
25    
26     #include <libxml/parser.h>
27     #include <libxml/tree.h>
28    
29     #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
30     #error "libxml doesn't support required tree or output"
31     #endif
32    
33     static void diff_save_tags(tag_t *tag, xmlNodePtr node) {
34     while(tag) {
35     xmlNodePtr tag_node = xmlNewChild(node, NULL,
36     BAD_CAST "tag", NULL);
37     xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key);
38     xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value);
39     tag = tag->next;
40     }
41     }
42    
43     static void diff_save_state_n_id(int flags, xmlNodePtr node, item_id_t id) {
44     if(flags & OSM_FLAG_DELETED)
45     xmlNewProp(node, BAD_CAST "state", BAD_CAST "deleted");
46     else if(flags & OSM_FLAG_NEW)
47     xmlNewProp(node, BAD_CAST "state", BAD_CAST "new");
48    
49     /* all items need an id */
50     char *id_str = g_strdup_printf("%ld", id);
51     xmlNewProp(node, BAD_CAST "id", BAD_CAST id_str);
52     g_free(id_str);
53     }
54    
55     static void diff_save_nodes(node_t *node, xmlNodePtr root_node) {
56     /* store all modfied nodes */
57     while(node) {
58     if(node->flags) {
59     xmlNodePtr node_node = xmlNewChild(root_node, NULL,
60     BAD_CAST "node", NULL);
61    
62     diff_save_state_n_id(node->flags, node_node, node->id);
63    
64     if(!(node->flags & OSM_FLAG_DELETED)) {
65     char str[32];
66    
67     /* additional info is only required if the node hasn't been deleted */
68     g_ascii_dtostr(str, sizeof(str), node->pos.lat);
69     xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str);
70     g_ascii_dtostr(str, sizeof(str), node->pos.lon);
71     xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str);
72     snprintf(str, sizeof(str), "%ld", node->time);
73     xmlNewProp(node_node, BAD_CAST "time", BAD_CAST str);
74    
75     diff_save_tags(node->tag, node_node);
76     }
77     }
78     node = node->next;
79     }
80     }
81    
82     static void diff_save_ways(way_t *way, xmlNodePtr root_node) {
83    
84     /* store all modfied ways */
85     while(way) {
86     if(way->flags) {
87     xmlNodePtr node_way = xmlNewChild(root_node, NULL,
88     BAD_CAST "way", NULL);
89    
90     diff_save_state_n_id(way->flags, node_way, way->id);
91    
92     if(way->flags & OSM_FLAG_HIDDEN)
93     xmlNewProp(node_way, BAD_CAST "hidden", BAD_CAST "true");
94    
95     /* additional info is only required if the way hasn't been deleted */
96     /* and of the dirty or new flags are set. (otherwise e.g. only */
97     /* the hidden flag may be set) */
98     if((!(way->flags & OSM_FLAG_DELETED)) &&
99     (way->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW))) {
100     node_chain_t *node_chain = way->node_chain;
101     while(node_chain) {
102     xmlNodePtr node_node = xmlNewChild(node_way, NULL,
103     BAD_CAST "nd", NULL);
104     char *id = g_strdup_printf("%ld", node_chain->node->id);
105     xmlNewProp(node_node, BAD_CAST "ref", BAD_CAST id);
106     g_free(id);
107     node_chain = node_chain->next;
108     }
109     diff_save_tags(way->tag, node_way);
110     }
111     }
112     way = way->next;
113     }
114     }
115    
116     static void diff_save_relations(relation_t *relation, xmlNodePtr root_node) {
117    
118     /* store all modfied relations */
119     while(relation) {
120     if(relation->flags) {
121     xmlNodePtr node_rel = xmlNewChild(root_node, NULL,
122     BAD_CAST "relation", NULL);
123    
124     diff_save_state_n_id(relation->flags, node_rel, relation->id);
125    
126     if(!(relation->flags & OSM_FLAG_DELETED)) {
127     /* additional info is only required if the relation */
128     /* hasn't been deleted */
129     member_t *member = relation->member;
130     while(member) {
131     xmlNodePtr node_member = xmlNewChild(node_rel, NULL,
132     BAD_CAST "member", NULL);
133    
134     char *ref = NULL;
135     switch(member->type) {
136     case NODE:
137     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "node");
138     ref = g_strdup_printf("%ld", member->node->id);
139     break;
140     case WAY:
141     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "way");
142     ref = g_strdup_printf("%ld", member->way->id);
143     break;
144     case RELATION:
145     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "relation");
146     ref = g_strdup_printf("%ld", member->relation->id);
147     break;
148    
149     /* XXX_ID's are used if this is a reference to an item not */
150     /* stored in this xml data set */
151     case NODE_ID:
152     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "node");
153     ref = g_strdup_printf("%ld", member->id);
154     break;
155     case WAY_ID:
156     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "way");
157     ref = g_strdup_printf("%ld", member->id);
158     break;
159     case RELATION_ID:
160     xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "relation");
161     ref = g_strdup_printf("%ld", member->id);
162     break;
163    
164     default:
165     printf("unexpected member type %d\n", member->type);
166     break;
167     }
168    
169     g_assert(ref);
170     xmlNewProp(node_member, BAD_CAST "ref", BAD_CAST ref);
171     g_free(ref);
172    
173     if(member->role)
174     xmlNewProp(node_member, BAD_CAST "role", BAD_CAST member->role);
175    
176     member = member->next;
177     }
178     diff_save_tags(relation->tag, node_rel);
179     }
180     }
181     relation = relation->next;
182     }
183     }
184    
185    
186     /* return true if no diff needs to be saved */
187     gboolean diff_is_clean(osm_t *osm, gboolean honor_hidden_flags) {
188     gboolean clean = TRUE;
189    
190     /* check if a diff is necessary */
191     node_t *node = osm->node;
192     while(node && clean) {
193     if(node->flags) clean = FALSE;
194     node = node->next;
195     }
196    
197     way_t *way = osm->way;
198     while(way && clean) {
199     if(honor_hidden_flags) {
200     if(way->flags) clean = FALSE;
201     } else
202     if(way->flags & ~OSM_FLAG_HIDDEN)
203     clean = FALSE;
204    
205     way = way->next;
206     }
207    
208     relation_t *relation = osm->relation;
209     while(relation && clean) {
210     if(relation->flags) clean = FALSE;
211     relation = relation->next;
212     }
213    
214     return clean;
215     }
216    
217     void diff_save(project_t *project, osm_t *osm) {
218 harbaum 4 if(!project || !osm) return;
219 harbaum 1
220     char *diff_name =
221     g_strdup_printf("%s/%s.diff", project->path, project->name);
222    
223     if(diff_is_clean(osm, TRUE)) {
224     printf("data set is clean, removing diff if present\n");
225     g_remove(diff_name);
226     g_free(diff_name);
227     return;
228     }
229    
230     printf("data set is dirty, generating diff\n");
231    
232     LIBXML_TEST_VERSION;
233    
234     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
235     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "diff");
236     xmlNewProp(root_node, BAD_CAST "name", BAD_CAST project->name);
237     xmlDocSetRootElement(doc, root_node);
238    
239     diff_save_nodes(osm->node, root_node);
240     diff_save_ways(osm->way, root_node);
241     diff_save_relations(osm->relation, root_node);
242    
243     xmlSaveFormatFileEnc(diff_name, doc, "UTF-8", 1);
244     xmlFreeDoc(doc);
245     xmlCleanupParser();
246    
247     g_free(diff_name);
248     }
249    
250     static int xml_get_prop_int(xmlNode *node, char *prop, int def) {
251     char *str = (char*)xmlGetProp(node, BAD_CAST prop);
252     int value = def;
253    
254     if(str) {
255     value = strtoul(str, NULL, 10);
256     xmlFree(str);
257     }
258    
259     return value;
260     }
261    
262     static int xml_get_prop_state(xmlNode *node, char *prop) {
263     char *str = (char*)xmlGetProp(node, BAD_CAST prop);
264    
265     if(str) {
266     if(strcasecmp(str, "new") == 0) {
267     xmlFree(str);
268     return OSM_FLAG_NEW;
269     }
270    
271     if(strcasecmp(str, "deleted") == 0) {
272     xmlFree(str);
273     return OSM_FLAG_DELETED;
274     }
275    
276     g_assert(0);
277     }
278    
279     return OSM_FLAG_DIRTY;
280     }
281    
282     static pos_t *xml_get_prop_pos(xmlNode *node) {
283     char *str_lat = (char*)xmlGetProp(node, BAD_CAST "lat");
284     char *str_lon = (char*)xmlGetProp(node, BAD_CAST "lon");
285    
286     if(!str_lon || !str_lat) {
287     if(!str_lon) xmlFree(str_lon);
288     if(!str_lat) xmlFree(str_lat);
289     return NULL;
290     }
291    
292     pos_t *pos = g_new0(pos_t, 1);
293     pos->lat = g_ascii_strtod(str_lat, NULL);
294     pos->lon = g_ascii_strtod(str_lon, NULL);
295    
296     xmlFree(str_lon);
297     xmlFree(str_lat);
298    
299     return pos;
300     }
301    
302     static tag_t *xml_scan_tags(xmlDoc *doc, xmlNodePtr node, osm_t *osm) {
303     /* scan for tags */
304     tag_t *first_tag = NULL;
305     tag_t **tag = &first_tag;
306    
307     while(node) {
308     if(node->type == XML_ELEMENT_NODE) {
309     if(strcasecmp((char*)node->name, "tag") == 0) {
310     /* attach tag to node/way */
311     *tag = osm_parse_osm_tag(osm, doc, node);
312     if(*tag) tag = &((*tag)->next);
313     }
314     }
315     node = node->next;
316     }
317     return first_tag;
318     }
319    
320     void diff_restore_node(xmlDoc *doc, xmlNodePtr node_node, osm_t *osm) {
321     printf("Restoring node\n");
322    
323     /* read properties */
324     item_id_t id = xml_get_prop_int(node_node, "id", ID_ILLEGAL);
325     if(id == ID_ILLEGAL) {
326     printf(" Node entry missing id\n");
327     return;
328     }
329    
330     int state = xml_get_prop_state(node_node, "state");
331     pos_t *pos = xml_get_prop_pos(node_node);
332    
333     if(!(state & OSM_FLAG_DELETED) && !pos) {
334     printf(" Node not deleted, but no valid position\n");
335     return;
336     }
337    
338     /* evaluate properties */
339     node_t *node = NULL;
340    
341     switch(state) {
342     case OSM_FLAG_NEW:
343     printf(" Restoring NEW node\n");
344    
345     node = g_new0(node_t, 1);
346     node->visible = TRUE;
347     node->flags = OSM_FLAG_NEW;
348     node->time = xml_get_prop_int(node_node, "time", 0);
349     if(!node->time) node->time = time(NULL);
350    
351     /* attach to end of node list */
352     node_t **lnode = &osm->node;
353     while(*lnode) lnode = &(*lnode)->next;
354     *lnode = node;
355     break;
356    
357     case OSM_FLAG_DELETED:
358     printf(" Restoring DELETE flag\n");
359    
360     if((node = osm_get_node_by_id(osm, id)))
361     node->flags |= OSM_FLAG_DELETED;
362     else
363     printf(" WARNING: no node with that id found\n");
364     break;
365    
366     case OSM_FLAG_DIRTY:
367     printf(" Valid id/position (DIRTY)\n");
368    
369     if((node = osm_get_node_by_id(osm, id)))
370     node->flags |= OSM_FLAG_DIRTY;
371     else
372     printf(" WARNING: no node with that id found\n");
373     break;
374    
375     default:
376     printf(" Illegal node entry\n");
377     return;
378     break;
379     }
380    
381     if(!node) {
382     printf(" no valid node\n");
383     return;
384     }
385    
386     /* update id and position from diff */
387     node->id = id;
388     if(pos) {
389     node->pos.lat = pos->lat;
390     node->pos.lon = pos->lon;
391    
392     pos2lpos(osm->bounds, &node->pos, &node->lpos);
393    
394     g_free(pos);
395     }
396    
397     /* node may be an existing node, so remove tags to */
398     /* make space for new ones */
399     if(node->tag) {
400     printf(" removing existing tags for diff tags\n");
401     osm_tag_free(node->tag);
402     node->tag = NULL;
403     }
404    
405     node->tag = xml_scan_tags(doc, node_node->children, osm);
406     }
407    
408     void diff_restore_way(xmlDoc *doc, xmlNodePtr node_node, osm_t *osm) {
409     printf("Restoring way\n");
410    
411     item_id_t id = xml_get_prop_int(node_node, "id", ID_ILLEGAL);
412     if(id == ID_ILLEGAL) {
413     printf(" entry missing id\n");
414     return;
415     }
416    
417     int state = xml_get_prop_state(node_node, "state");
418    
419     /* handle hidden flag */
420     gboolean hidden = FALSE;
421     char *str = (char*)xmlGetProp(node_node, BAD_CAST "hidden");
422     if(str) {
423     if(strcasecmp(str, "true") == 0)
424     hidden = TRUE;
425    
426     xmlFree(str);
427     }
428    
429    
430     /* evaluate properties */
431     way_t *way = NULL;
432     switch(state) {
433     case OSM_FLAG_NEW:
434     printf(" Restoring NEW way\n");
435    
436     way = g_new0(way_t, 1);
437     way->visible = TRUE;
438     way->flags = OSM_FLAG_NEW;
439     way->time = xml_get_prop_int(node_node, "time", 0);
440     if(!way->time) way->time = time(NULL);
441    
442     /* attach to end of way list */
443     way_t **lway = &osm->way;
444     while(*lway) lway = &(*lway)->next;
445     *lway = way;
446     break;
447    
448     case OSM_FLAG_DELETED:
449     printf(" Restoring DELETE flag\n");
450    
451     if((way = osm_get_way_by_id(osm, id)))
452     way->flags |= OSM_FLAG_DELETED;
453     else
454     printf(" WARNING: no way with that id found\n");
455     break;
456    
457     case OSM_FLAG_DIRTY:
458     printf(" Valid id (DIRTY)\n");
459    
460     if((way = osm_get_way_by_id(osm, id)))
461     way->flags |= OSM_FLAG_DIRTY;
462     else
463     printf(" WARNING: no way with that id found\n");
464     break;
465    
466     default:
467     printf(" Illegal way entry\n");
468     return;
469     }
470    
471     if(!way) {
472     printf(" no valid way\n");
473     return;
474     }
475    
476     /* update id from diff */
477     way->id = id;
478    
479     /* update node_chain */
480     if(hidden)
481     way->flags |= OSM_FLAG_HIDDEN;
482    
483     gboolean installed_new_nodes = FALSE;
484    
485     /* scan for nodes */
486     node_chain_t **node_chain = &way->node_chain;
487     xmlNode *nd_node = NULL;
488     for(nd_node = node_node->children; nd_node; nd_node = nd_node->next) {
489     if(nd_node->type == XML_ELEMENT_NODE) {
490     if(strcasecmp((char*)nd_node->name, "nd") == 0) {
491    
492     /* only replace the original nodes if new nodes have actually been */
493     /* found. */
494     if(!installed_new_nodes) {
495     /* way may be an existing way, so remove nodes to */
496     /* make space for new ones */
497     if(way->node_chain) {
498     printf(" removing existing nodes for diff nodes\n");
499     osm_node_chain_free(way->node_chain);
500     way->node_chain = NULL;
501     }
502    
503     installed_new_nodes = TRUE;
504     }
505    
506     /* attach node to node_chain */
507     *node_chain = osm_parse_osm_way_nd(osm, doc, nd_node);
508     if(*node_chain)
509     node_chain = &((*node_chain)->next);
510     }
511     }
512     }
513    
514     /* only replace tags if nodes have been found before. if no nodes */
515     /* were found this wasn't a dirty entry but e.g. only the hidden */
516     /* flag had been set */
517     if(installed_new_nodes) {
518    
519     /* node may be an existing node, so remove tags to */
520     /* make space for new ones */
521     if(way->tag) {
522     printf(" removing existing tags for diff tags\n");
523     osm_tag_free(way->tag);
524     way->tag = NULL;
525     }
526    
527     way->tag = xml_scan_tags(doc, node_node->children, osm);
528     } else {
529     printf(" no nodes restored, way isn't dirty!\n");
530     way->flags &= ~OSM_FLAG_DIRTY;
531     }
532     }
533    
534     void diff_restore_relation(xmlDoc *doc, xmlNodePtr node_rel, osm_t *osm) {
535     printf("Restoring relation\n");
536    
537     item_id_t id = xml_get_prop_int(node_rel, "id", ID_ILLEGAL);
538     if(id == ID_ILLEGAL) {
539     printf(" entry missing id\n");
540     return;
541     }
542    
543     int state = xml_get_prop_state(node_rel, "state");
544    
545     /* evaluate properties */
546     relation_t *relation = NULL;
547     switch(state) {
548     case OSM_FLAG_NEW:
549     printf(" Restoring NEW relation\n");
550    
551     relation = g_new0(relation_t, 1);
552     relation->visible = TRUE;
553     relation->flags = OSM_FLAG_NEW;
554     relation->time = xml_get_prop_int(node_rel, "time", 0);
555     if(!relation->time) relation->time = time(NULL);
556    
557     /* attach to end of relation list */
558     relation_t **lrelation = &osm->relation;
559     while(*lrelation) lrelation = &(*lrelation)->next;
560     *lrelation = relation;
561     break;
562    
563     case OSM_FLAG_DELETED:
564     printf(" Restoring DELETE flag\n");
565    
566     if((relation = osm_get_relation_by_id(osm, id)))
567     relation->flags |= OSM_FLAG_DELETED;
568     else
569     printf(" WARNING: no relation with that id found\n");
570     break;
571    
572     case OSM_FLAG_DIRTY:
573     printf(" Valid id (DIRTY)\n");
574    
575     if((relation = osm_get_relation_by_id(osm, id)))
576     relation->flags |= OSM_FLAG_DIRTY;
577     else
578     printf(" WARNING: no relation with that id found\n");
579     break;
580    
581     default:
582     printf(" Illegal relation entry\n");
583     return;
584     }
585    
586     if(!relation) {
587     printf(" no valid relation\n");
588     return;
589     }
590    
591     /* update id from diff */
592     relation->id = id;
593    
594     /* update members */
595    
596     /* this may be an existing relation, so remove members to */
597     /* make space for new ones */
598     if(relation->member) {
599     printf(" removing existing members for diff members\n");
600     osm_members_free(relation->member);
601     relation->member = NULL;
602     }
603    
604     /* scan for members */
605     member_t **member = &relation->member;
606     xmlNode *member_node = NULL;
607     for(member_node = node_rel->children; member_node;
608     member_node = member_node->next) {
609     if(member_node->type == XML_ELEMENT_NODE) {
610     if(strcasecmp((char*)member_node->name, "member") == 0) {
611     /* attach member to member_chain */
612     *member = osm_parse_osm_relation_member(osm, doc, member_node);
613     if(*member)
614     member = &((*member)->next);
615     }
616     }
617     }
618    
619     /* node may be an existing node, so remove tags to */
620     /* make space for new ones */
621     if(relation->tag) {
622     printf(" removing existing tags for diff tags\n");
623     osm_tag_free(relation->tag);
624     relation->tag = NULL;
625     }
626    
627     relation->tag = xml_scan_tags(doc, node_rel->children, osm);
628     }
629    
630     void diff_restore(appdata_t *appdata, project_t *project, osm_t *osm) {
631 harbaum 4 if(!project || !osm) return;
632    
633 harbaum 1 char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name);
634 harbaum 4
635 harbaum 1 if(!g_file_test(diff_name, G_FILE_TEST_EXISTS)) {
636     printf("no diff present!\n");
637     g_free(diff_name);
638     return;
639     }
640    
641     printf("diff found, applying ...\n");
642    
643     xmlDoc *doc = NULL;
644     xmlNode *root_element = NULL;
645    
646     /* parse the file and get the DOM */
647     if((doc = xmlReadFile(diff_name, NULL, 0)) == NULL) {
648     errorf(GTK_WIDGET(appdata->window),
649     "Error: could not parse file %s\n", diff_name);
650     g_free(diff_name);
651     return;
652     }
653    
654     /* Get the root element node */
655     root_element = xmlDocGetRootElement(doc);
656    
657     xmlNode *cur_node = NULL;
658     for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
659     if (cur_node->type == XML_ELEMENT_NODE) {
660     if(strcasecmp((char*)cur_node->name, "diff") == 0) {
661     char *str = (char*)xmlGetProp(cur_node, BAD_CAST "name");
662     if(str) {
663     printf("diff for project %s\n", str);
664     if(strcmp(project->name, str) != 0) {
665     messagef(GTK_WIDGET(appdata->window), _("Warning"),
666     "Diff name (%s) does not match project name (%s)",
667     str, project->name);
668     }
669     xmlFree(str);
670     }
671    
672     xmlNodePtr node_node = cur_node->children;
673     while(node_node) {
674     if(node_node->type == XML_ELEMENT_NODE) {
675    
676     if(strcasecmp((char*)node_node->name, "node") == 0)
677     diff_restore_node(doc, node_node, osm);
678    
679     else if(strcasecmp((char*)node_node->name, "way") == 0)
680     diff_restore_way(doc, node_node, osm);
681    
682     else if(strcasecmp((char*)node_node->name, "relation") == 0)
683     diff_restore_relation(doc, node_node, osm);
684    
685     else
686     printf("WARNING: item %s not restored\n", node_node->name);
687     }
688     node_node = node_node->next;
689     }
690     }
691     }
692     }
693    
694     g_free(diff_name);
695    
696     xmlFreeDoc(doc);
697     xmlCleanupParser();
698    
699     /* check for hidden ways and update menu accordingly */
700     gboolean something_is_hidden = FALSE;
701     way_t *way = osm->way;
702     while(!something_is_hidden && way) {
703     if(way->flags & OSM_FLAG_HIDDEN)
704     something_is_hidden = TRUE;
705    
706     way = way->next;
707     }
708    
709     if(something_is_hidden) {
710     printf("hidden flags have been restored, enable show_add menu\n");
711    
712     statusbar_set(appdata, _("Some objects have been hidden"), TRUE);
713     gtk_widget_set_sensitive(appdata->menu_item_map_show_all, TRUE);
714     }
715     }
716    
717     gboolean diff_present(project_t *project) {
718     char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name);
719    
720     if(!g_file_test(diff_name, G_FILE_TEST_EXISTS)) {
721     printf("no diff present!\n");
722     g_free(diff_name);
723     return FALSE;
724     }
725    
726     g_free(diff_name);
727     return TRUE;
728     }
729    
730     void diff_remove(project_t *project) {
731     char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name);
732     g_remove(diff_name);
733     g_free(diff_name);
734     }