1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details)
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 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 #include <config.h>
21
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #endif
27 #include <libgen.h>
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32
33 #include "gschem.h"
34
35 #ifdef HAVE_LIBDMALLOC
36 #include <dmalloc.h>
37 #endif
38
39 /* break with the tradition here and input a list */
40 /*! \todo probably should go back and do the same for o_copy o_move
41 * o_delete...
42 */
43 /*! \todo Finish function documentation!!!
44 * \brief
45 * \par Function Description
46 *
47 */
o_edit(GSCHEM_TOPLEVEL * w_current,GList * list)48 void o_edit(GSCHEM_TOPLEVEL *w_current, GList *list)
49 {
50 OBJECT *o_current;
51 const gchar *str = NULL;
52
53 if (list == NULL) {
54 w_current->inside_action = 0;
55 i_set_state(w_current, SELECT);
56 return;
57 }
58
59 o_current = (OBJECT *) list->data;
60 if (o_current == NULL) {
61 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
62 exit(-1);
63 }
64
65 /* for now deal with only the first item */
66 switch(o_current->type) {
67
68 /* also add the ability to multi attrib edit: nets, busses, pins */
69 case(OBJ_COMPLEX):
70 case(OBJ_PLACEHOLDER):
71 case(OBJ_NET):
72 case(OBJ_PIN):
73 case(OBJ_BUS):
74 x_multiattrib_open (w_current);
75 break;
76
77 case(OBJ_PICTURE):
78 picture_change_filename_dialog(w_current);
79 break;
80 case(OBJ_ARC):
81 arc_angle_dialog(w_current, o_current);
82 break;
83 case(OBJ_TEXT):
84 str = o_text_get_string (w_current->toplevel, o_current);
85 if (o_attrib_get_name_value (o_current, NULL, NULL) &&
86 /* attribute editor only accept 1-line values for attribute */
87 o_text_num_lines (str) == 1) {
88 attrib_edit_dialog(w_current,o_current, FROM_MENU);
89 } else {
90 o_text_edit(w_current, o_current);
91 }
92 break;
93 }
94
95 /* has to be more extensive in the future */
96 /* some sort of redrawing? */
97 }
98
99 /*! \todo Finish function documentation!!!
100 * \brief
101 * \par Function Description
102 *
103 */
104 /* This locks the entire selected list. It does lock components, but does NOT
105 * change the color (of primatives of the components) though
106 * this cannot be called recursively */
o_lock(GSCHEM_TOPLEVEL * w_current)107 void o_lock(GSCHEM_TOPLEVEL *w_current)
108 {
109 OBJECT *object = NULL;
110 GList *s_current = NULL;
111
112 /* skip over head */
113 s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
114
115 while(s_current != NULL) {
116 object = (OBJECT *) s_current->data;
117 if (object) {
118 /* check to see if locked_color is already being used */
119 if (object->locked_color == -1) {
120 object->selectable = FALSE;
121 object->locked_color = object->color;
122 object->color = LOCK_COLOR;
123 w_current->toplevel->page_current->CHANGED=1;
124 } else {
125 s_log_message(_("Object already locked\n"));
126 }
127 }
128
129 s_current = g_list_next(s_current);
130 }
131
132 if (!w_current->SHIFTKEY) o_select_unselect_all(w_current);
133 o_undo_savestate(w_current, UNDO_ALL);
134 i_update_menus(w_current);
135 }
136
137 /*! \todo Finish function documentation!!!
138 * \brief
139 * \par Function Description
140 *
141 */
142 /* You can unlock something by selecting it with a bounding box... */
143 /* this will probably change in the future, but for now it's a
144 something.. :-) */
145 /* this cannot be called recursively */
o_unlock(GSCHEM_TOPLEVEL * w_current)146 void o_unlock(GSCHEM_TOPLEVEL *w_current)
147 {
148 OBJECT *object = NULL;
149 GList *s_current = NULL;
150
151 s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
152
153 while(s_current != NULL) {
154 object = (OBJECT *) s_current->data;
155 if (object) {
156 /* only unlock if the object is locked */
157 if (object->selectable == FALSE) {
158 object->selectable = TRUE;
159 object->color = object->locked_color;
160 object->locked_color = -1;
161 w_current->toplevel->page_current->CHANGED = 1;
162 } else {
163 s_log_message(_("Object already unlocked\n"));
164 }
165 }
166
167 s_current = g_list_next(s_current);
168 }
169 o_undo_savestate(w_current, UNDO_ALL);
170 }
171
172 /*! \brief Rotate all objects in list.
173 * \par Function Description
174 * Given an object <B>list</B>, and the center of rotation
175 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
176 * list, rotating each object through angle <B>angle</B>.
177 * The list contains a given object and all its attributes
178 * (refdes, pinname, pinlabel, ...).
179 * There is a second pass to run the rotate hooks of non-simple objects,
180 * like pin or complex objects, for example.
181 *
182 * \param [in] w_current The GSCHEM_TOPLEVEL object.
183 * \param [in] centerx Center x coordinate of rotation.
184 * \param [in] centery Center y coordinate of rotation.
185 * \param [in] angle Angle to rotate the objects through.
186 * \param [in] list The list of objects to rotate.
187 */
o_rotate_world_update(GSCHEM_TOPLEVEL * w_current,int centerx,int centery,int angle,GList * list)188 void o_rotate_world_update(GSCHEM_TOPLEVEL *w_current,
189 int centerx, int centery, int angle, GList *list)
190 {
191 TOPLEVEL *toplevel = w_current->toplevel;
192 OBJECT *o_current;
193 GList *o_iter;
194
195 /* this is okay if you just hit rotate and have nothing selected */
196 if (list == NULL) {
197 w_current->inside_action = 0;
198 i_set_state(w_current, SELECT);
199 return;
200 }
201
202 o_invalidate_glist (w_current, list);
203
204 /* Find connected objects, removing each object in turn from the
205 * connection list. We only _really_ want those objects connected
206 * to the selection, not those within in it.
207 */
208 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
209 o_current = o_iter->data;
210
211 s_conn_remove_object (toplevel, o_current);
212 }
213
214 o_glist_rotate_world( toplevel, centerx, centery, angle, list );
215
216 /* Find connected objects, adding each object in turn back to the
217 * connection list. We only _really_ want those objects connected
218 * to the selection, not those within in it.
219 */
220 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
221 o_current = o_iter->data;
222
223 s_conn_update_object (toplevel, o_current);
224 }
225
226 o_invalidate_glist (w_current, list);
227
228 /* Run rotate-objects-hook */
229 g_run_hook_object_list (w_current, "%rotate-objects-hook", list);
230
231 /* Don't save the undo state if we are inside an action */
232 /* This is useful when rotating the selection while moving, for example */
233 toplevel->page_current->CHANGED = 1;
234 if (!w_current->inside_action) {
235 o_undo_savestate(w_current, UNDO_ALL);
236 }
237 }
238
239 /*! \todo Finish function documentation!!!
240 * \brief
241 * \par Function Description
242 *
243 */
o_mirror_world_update(GSCHEM_TOPLEVEL * w_current,int centerx,int centery,GList * list)244 void o_mirror_world_update(GSCHEM_TOPLEVEL *w_current, int centerx, int centery, GList *list)
245 {
246 TOPLEVEL *toplevel = w_current->toplevel;
247 OBJECT *o_current;
248 GList *o_iter;
249
250 if (list == NULL) {
251 w_current->inside_action = 0;
252 i_set_state(w_current, SELECT);
253 return;
254 }
255
256 o_invalidate_glist (w_current, list);
257
258 /* Find connected objects, removing each object in turn from the
259 * connection list. We only _really_ want those objects connected
260 * to the selection, not those within in it.
261 */
262 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
263 o_current = o_iter->data;
264
265 s_conn_remove_object (toplevel, o_current);
266 }
267
268 o_glist_mirror_world( toplevel, centerx, centery, list );
269
270 /* Find connected objects, adding each object in turn back to the
271 * connection list. We only _really_ want those objects connected
272 * to the selection, not those within in it.
273 */
274 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
275 o_current = o_iter->data;
276
277 s_conn_update_object (toplevel, o_current);
278 }
279
280 o_invalidate_glist (w_current, list);
281
282 /* Run mirror-objects-hook */
283 g_run_hook_object_list (w_current, "%mirror-objects-hook", list);
284
285 toplevel->page_current->CHANGED=1;
286 o_undo_savestate(w_current, UNDO_ALL);
287 }
288
289 /*! \todo Finish function documentation!!!
290 * \brief
291 * \par Function Description
292 *
293 */
o_edit_show_hidden_lowlevel(GSCHEM_TOPLEVEL * w_current,const GList * o_list)294 void o_edit_show_hidden_lowlevel (GSCHEM_TOPLEVEL *w_current,
295 const GList *o_list)
296 {
297 TOPLEVEL *toplevel = w_current->toplevel;
298 OBJECT *o_current;
299 const GList *iter;
300
301 iter = o_list;
302 while (iter != NULL) {
303 o_current = (OBJECT *)iter->data;
304 if (o_current->type == OBJ_TEXT && !o_is_visible (toplevel, o_current)) {
305
306 /* don't toggle the visibility flag */
307 o_text_recreate (toplevel, o_current);
308 }
309
310 if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
311 o_edit_show_hidden_lowlevel(w_current, o_current->complex->prim_objs);
312 o_recalc_single_object(toplevel, o_current);
313 }
314
315 iter = g_list_next (iter);
316 }
317 }
318
319 /*! \todo Finish function documentation!!!
320 * \brief
321 * \par Function Description
322 *
323 */
o_edit_show_hidden(GSCHEM_TOPLEVEL * w_current,const GList * o_list)324 void o_edit_show_hidden (GSCHEM_TOPLEVEL *w_current, const GList *o_list)
325 {
326 /* this function just shows the hidden text, but doesn't toggle it */
327 /* this function does not change the CHANGED bit, no real changes are */
328 /* made to the schematic */
329
330 /* toggle show_hidden_text variable, which when it is true */
331 /* means that hidden text IS drawn */
332 w_current->toplevel->show_hidden_text = !w_current->toplevel->show_hidden_text;
333 i_show_state(w_current, NULL); /* update screen status */
334
335 o_edit_show_hidden_lowlevel(w_current, o_list);
336 o_invalidate_all (w_current);
337
338 if (w_current->toplevel->show_hidden_text) {
339 s_log_message(_("Hidden text is now visible\n"));
340 } else {
341 s_log_message(_("Hidden text is now invisible\n"));
342 }
343 }
344
345 #define FIND_WINDOW_HALF_SIZE (5000)
346
347 OBJECT *last_o = NULL;
348 int skiplast;
349
350 /*! \todo Finish function documentation!!!
351 * \brief
352 * \par Function Description
353 *
354 * \todo Only descends into the first source schematic
355 *
356 */
o_edit_find_text(GSCHEM_TOPLEVEL * w_current,const GList * o_list,char * stext,int descend,int skip)357 int o_edit_find_text (GSCHEM_TOPLEVEL *w_current, const GList *o_list,
358 char *stext, int descend, int skip)
359 {
360 TOPLEVEL *toplevel = w_current->toplevel;
361 char *attrib = NULL;
362 int count = 0;
363 PAGE *parent = NULL;
364 char *current_filename = NULL;
365 int page_control = 0;
366 int pcount = 0;
367 int rv;
368 int text_screen_height;
369 const GList *iter;
370
371 OBJECT *o_current;
372
373 skiplast = skip;
374
375 iter = o_list;
376 while (iter != NULL) {
377 o_current = (OBJECT *)iter->data;
378
379 if (descend) {
380 if (o_current->type == OBJ_COMPLEX) {
381 parent = toplevel->page_current;
382 attrib = o_attrib_search_attached_attribs_by_name (o_current,
383 "source", count);
384
385 /* if above is null, then look inside symbol */
386 if (attrib == NULL) {
387 attrib = o_attrib_search_inherited_attribs_by_name (o_current,
388 "source", count);
389 /* looking_inside = TRUE; */
390 }
391
392 if (attrib) {
393 pcount = 0;
394 current_filename = u_basic_breakup_string(attrib, ',', pcount);
395 if (current_filename != NULL) {
396 PAGE *child_page =
397 s_hierarchy_down_schematic_single(toplevel,
398 current_filename,
399 parent,
400 page_control,
401 HIERARCHY_NORMAL_LOAD);
402
403 if (child_page != NULL) {
404 page_control = child_page->page_control;
405 rv = o_edit_find_text (w_current,
406 s_page_objects (child_page),
407 stext, descend, skiplast);
408 if (!rv) {
409 s_page_goto( toplevel, child_page );
410 return 0;
411 }
412 }
413 }
414 }
415 }
416 }
417
418 if (o_current->type == OBJ_TEXT &&
419 (o_is_visible (toplevel, o_current) || toplevel->show_hidden_text)) {
420
421 const gchar *str = o_text_get_string (toplevel, o_current);
422 /* replaced strcmp with strstr to simplify the search */
423 if (strstr (str,stext)) {
424 if (!skiplast) {
425 int x1, y1, x2, y2;
426
427 a_zoom(w_current, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
428 g_assert( world_get_single_object_bounds (toplevel, o_current, &x1, &y1, &x2, &y2) );
429 text_screen_height = SCREENabs (w_current, y2 - y1);
430 /* this code will zoom/pan till the text screen height is about */
431 /* 50 pixels high, perhaps a future enhancement will be to make */
432 /* this number configurable */
433 while (text_screen_height < 50) {
434 a_zoom(w_current, ZOOM_IN, DONTCARE, A_PAN_DONT_REDRAW);
435 text_screen_height = SCREENabs (w_current, y2 - y1);
436 }
437 a_pan_general(w_current,
438 o_current->text->x, o_current->text->y,
439 1, 0);
440
441 /* Make sure the titlebar and scrollbars are up-to-date */
442 x_window_set_current_page(w_current,
443 w_current->toplevel->page_current );
444
445 last_o = o_current;
446 break;
447 }
448 if (last_o == o_current) {
449 skiplast = 0;
450 }
451
452 } /* if (strstr(o_current->text->string,stext)) */
453 } /* if (o_current->type == OBJ_TEXT) */
454 iter = g_list_next (iter);
455
456 if (iter == NULL) {
457 return 1;
458 }
459 }
460 return (iter == NULL);
461 }
462
463
464 /*! \todo Finish function documentation!!!
465 * \brief
466 * \par Function Description
467 *
468 */
o_edit_hide_specific_text(GSCHEM_TOPLEVEL * w_current,const GList * o_list,char * stext)469 void o_edit_hide_specific_text (GSCHEM_TOPLEVEL *w_current,
470 const GList *o_list,
471 char *stext)
472 {
473 TOPLEVEL *toplevel = w_current->toplevel;
474 OBJECT *o_current;
475 const GList *iter;
476
477 iter = o_list;
478 while (iter != NULL) {
479 o_current = (OBJECT *)iter->data;
480
481 if (o_current->type == OBJ_TEXT) {
482 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
483 if (!strncmp (stext, str, strlen (stext))) {
484 if (o_is_visible (toplevel, o_current)) {
485 o_set_visibility (toplevel, o_current, INVISIBLE);
486 o_text_recreate(toplevel, o_current);
487
488 toplevel->page_current->CHANGED = 1;
489 }
490 }
491 }
492 iter = g_list_next (iter);
493 }
494 o_undo_savestate(w_current, UNDO_ALL);
495 o_invalidate_all (w_current);
496 }
497
498 /*! \todo Finish function documentation!!!
499 * \brief
500 * \par Function Description
501 *
502 */
o_edit_show_specific_text(GSCHEM_TOPLEVEL * w_current,const GList * o_list,char * stext)503 void o_edit_show_specific_text (GSCHEM_TOPLEVEL *w_current,
504 const GList *o_list,
505 char *stext)
506 {
507 TOPLEVEL *toplevel = w_current->toplevel;
508 OBJECT *o_current;
509 const GList *iter;
510
511 iter = o_list;
512 while (iter != NULL) {
513 o_current = (OBJECT *)iter->data;
514
515 if (o_current->type == OBJ_TEXT) {
516 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
517 if (!strncmp (stext, str, strlen (stext))) {
518 if (!o_is_visible (toplevel, o_current)) {
519 o_set_visibility (toplevel, o_current, VISIBLE);
520 o_text_recreate(toplevel, o_current);
521
522 toplevel->page_current->CHANGED = 1;
523 }
524 }
525 }
526 iter = g_list_next (iter);
527 }
528 o_undo_savestate(w_current, UNDO_ALL);
529 }
530
531
532 /*! \brief Update a component.
533 *
534 * \par Function Description
535 * Updates \a o_current to the latest version of the symbol available
536 * in the symbol library, while preserving any attributes set in the
537 * current schematic. On success, returns the new OBJECT which
538 * replaces \a o_current on the page; \a o_current is deleted. On
539 * failure, returns NULL, and \a o_current is left unchanged.
540 *
541 * \param [in] w_current The GSCHEM_TOPLEVEL object.
542 * \param [in,out] o_current The OBJECT to be updated.
543 *
544 * \return the new OBJECT that replaces \a o_current.
545 */
546 OBJECT *
o_update_component(GSCHEM_TOPLEVEL * w_current,OBJECT * o_current)547 o_update_component (GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
548 {
549 TOPLEVEL *toplevel = w_current->toplevel;
550 OBJECT *o_new;
551 PAGE *page;
552 GList *new_attribs;
553 GList *old_attribs;
554 GList *iter;
555 const CLibSymbol *clib;
556
557 g_return_val_if_fail (o_current != NULL, NULL);
558 g_return_val_if_fail (o_current->type == OBJ_COMPLEX, NULL);
559 g_return_val_if_fail (o_current->complex_basename != NULL, NULL);
560
561 page = o_get_page (toplevel, o_current);
562
563 /* Force symbol data to be reloaded from source */
564 clib = s_clib_get_symbol_by_name (o_current->complex_basename);
565 s_clib_symbol_invalidate_data (clib);
566
567 if (clib == NULL) {
568 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
569 o_current->complex_basename);
570 return NULL;
571 }
572
573 /* Unselect the old object. */
574 o_selection_remove (toplevel, page->selection_list, o_current);
575
576 /* Create new object and set embedded */
577 o_new = o_complex_new (toplevel, OBJ_COMPLEX, DEFAULT_COLOR,
578 o_current->complex->x,
579 o_current->complex->y,
580 o_current->complex->angle,
581 o_current->complex->mirror,
582 clib, o_current->complex_basename,
583 1);
584 if (o_complex_is_embedded (o_current)) {
585 o_embed (toplevel, o_new);
586 }
587
588 new_attribs = o_complex_promote_attribs (toplevel, o_new);
589
590 /* Cull any attributes from new COMPLEX that are already attached to
591 * old COMPLEX. Note that the new_attribs list is kept consistent by
592 * setting GList data pointers to NULL if their OBJECTs are
593 * culled. At the end, the new_attribs list is updated by removing
594 * all list items with NULL data. This is slightly magic, but
595 * works. */
596 for (iter = new_attribs; iter != NULL; iter = g_list_next (iter)) {
597 OBJECT *attr_new = iter->data;
598 gchar *name;
599 gchar *value;
600
601 g_assert (attr_new->type == OBJ_TEXT);
602
603 o_attrib_get_name_value (attr_new, &name, NULL);
604
605 value = o_attrib_search_attached_attribs_by_name (o_current, name, 0);
606 if (value != NULL) {
607 o_attrib_remove (toplevel, &o_new->attribs, attr_new);
608 s_delete_object (toplevel, attr_new);
609 iter->data = NULL;
610 }
611
612 g_free (name);
613 g_free (value);
614 }
615 new_attribs = g_list_remove_all (new_attribs, NULL);
616
617 /* Detach attributes from old OBJECT and attach to new OBJECT */
618 old_attribs = g_list_copy (o_current->attribs);
619 o_attrib_detach_all (toplevel, o_current);
620 o_attrib_attach_list (toplevel, old_attribs, o_new, 1);
621 g_list_free (old_attribs);
622
623 /* Add new attributes to page */
624 s_page_append_list (toplevel, page, new_attribs);
625
626 /* Update pinnumbers for current slot */
627 s_slot_update_object (toplevel, o_new);
628
629 /* Replace old OBJECT with new OBJECT */
630 s_page_replace (toplevel, page, o_current, o_new);
631 s_delete_object (toplevel, o_current);
632
633 /* Select new OBJECT */
634 o_selection_add (toplevel, page->selection_list, o_new);
635
636 /* mark the page as modified */
637 toplevel->page_current->CHANGED = 1;
638 o_undo_savestate (w_current, UNDO_ALL);
639
640 return o_new;
641 }
642
643 /*! \brief Do autosave on all pages that are marked.
644 * \par Function Description
645 * Looks for pages with the do_autosave_backup flag activated and
646 * autosaves them.
647 *
648 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
649 */
o_autosave_backups(GSCHEM_TOPLEVEL * w_current)650 void o_autosave_backups(GSCHEM_TOPLEVEL *w_current)
651 {
652 TOPLEVEL *toplevel = w_current->toplevel;
653 GList *iter;
654 PAGE *p_save, *p_current;
655 gchar *backup_filename;
656 gchar *real_filename;
657 gchar *only_filename;
658 gchar *dirname;
659 mode_t saved_umask;
660 mode_t mask;
661 struct stat st;
662
663 /* save current page */
664 p_save = toplevel->page_current;
665
666 for ( iter = geda_list_get_glist( toplevel->pages );
667 iter != NULL;
668 iter = g_list_next( iter ) ) {
669
670 p_current = (PAGE *)iter->data;
671
672 if (p_current->do_autosave_backup == 0) {
673 continue;
674 }
675 if (p_current->ops_since_last_backup != 0) {
676 /* make p_current the current page of toplevel */
677 s_page_goto (toplevel, p_current);
678
679 /* Get the real filename and file permissions */
680 real_filename = follow_symlinks (p_current->page_filename, NULL);
681
682 if (real_filename == NULL) {
683 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
684 } else {
685 /* Get the directory in which the real filename lives */
686 dirname = g_path_get_dirname (real_filename);
687 only_filename = g_path_get_basename(real_filename);
688
689 backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
690 dirname, G_DIR_SEPARATOR, only_filename);
691
692 /* If there is not an existing file with that name, compute the
693 * permissions and uid/gid that we will use for the newly-created file.
694 */
695
696 if (stat (real_filename, &st) != 0) {
697 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
698 struct stat dir_st;
699 int result;
700 #endif
701
702 /* Use default permissions */
703 saved_umask = umask(0);
704 st.st_mode = 0666 & ~saved_umask;
705 umask(saved_umask);
706 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
707 st.st_uid = getuid ();
708
709 result = stat (dirname, &dir_st);
710
711 if (result == 0 && (dir_st.st_mode & S_ISGID))
712 st.st_gid = dir_st.st_gid;
713 else
714 st.st_gid = getgid ();
715 #endif
716 }
717 g_free (dirname);
718 g_free (only_filename);
719 g_free (real_filename);
720
721 /* Make the backup file writable before saving a new one */
722 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
723 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
724 saved_umask = umask(0);
725 if (chmod(backup_filename, (S_IWRITE|S_IWGRP|S_IWOTH) &
726 ((~saved_umask) & 0777)) != 0) {
727 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
728 backup_filename);
729 }
730 umask(saved_umask);
731 }
732
733 if (o_save (toplevel,
734 s_page_objects (toplevel->page_current),
735 backup_filename, NULL)) {
736
737 p_current->ops_since_last_backup = 0;
738 p_current->do_autosave_backup = 0;
739
740 /* Make the backup file readonly so a 'rm *' command will ask
741 the user before deleting it */
742 saved_umask = umask(0);
743 mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
744 mask = (~mask)&0777;
745 mask &= ((~saved_umask) & 0777);
746 if (chmod(backup_filename,mask) != 0) {
747 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
748 backup_filename);
749 }
750 umask(saved_umask);
751 } else {
752 s_log_message (_("Could NOT save backup file [%s]\n"),
753 backup_filename);
754 }
755 g_free (backup_filename);
756 }
757 }
758 }
759 /* restore current page */
760 s_page_goto (toplevel, p_save);
761 }
762