gschem

o_misc.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * gschem - gEDA Schematic Capture
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details)
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 #include <config.h>
00021 
00022 #include <stdio.h>
00023 #include <ctype.h>
00024 #ifdef HAVE_STRING_H
00025 #include <string.h>
00026 #endif
00027 #include <libgen.h>
00028 
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <unistd.h>
00032 
00033 #include "gschem.h"
00034 
00035 #ifdef HAVE_LIBDMALLOC
00036 #include <dmalloc.h>
00037 #endif
00038 
00039 /* break with the tradition here and input a list */
00048 void o_edit(GSCHEM_TOPLEVEL *w_current, GList *list)
00049 {
00050   OBJECT *o_current;
00051   const gchar *str = NULL;
00052 
00053   if (list == NULL) {
00054     w_current->inside_action = 0;
00055     i_set_state(w_current, SELECT);
00056     return;
00057   }
00058 
00059   o_current = (OBJECT *) list->data;
00060   if (o_current == NULL) {
00061     fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
00062     exit(-1);
00063   }
00064 
00065   /* for now deal with only the first item */
00066   switch(o_current->type) {
00067 
00068     /* also add the ability to multi attrib edit: nets, busses, pins */
00069     case(OBJ_COMPLEX):
00070     case(OBJ_PLACEHOLDER):
00071     case(OBJ_NET):
00072     case(OBJ_PIN):
00073     case(OBJ_BUS):
00074     x_multiattrib_open (w_current);
00075     break;
00076 
00077     case(OBJ_PICTURE):
00078     picture_change_filename_dialog(w_current);
00079     break;
00080     case(OBJ_ARC):
00081     arc_angle_dialog(w_current, o_current);
00082     break;
00083     case(OBJ_TEXT):
00084       str = o_text_get_string (w_current->toplevel, o_current);
00085       if (o_attrib_get_name_value (o_current, NULL, NULL) &&
00086         /* attribute editor only accept 1-line values for attribute */
00087         o_text_num_lines (str) == 1) {
00088         attrib_edit_dialog(w_current,o_current, FROM_MENU);
00089     } else {
00090       o_text_edit(w_current, o_current);
00091     }
00092     break;
00093   }
00094 
00095   /* has to be more extensive in the future */
00096   /* some sort of redrawing? */
00097 }
00098 
00104 /* This locks the entire selected list.  It does lock components, but does NOT
00105  * change the color (of primatives of the components) though
00106  * this cannot be called recursively */
00107 void o_lock(GSCHEM_TOPLEVEL *w_current)
00108 {
00109   OBJECT *object = NULL;
00110   GList *s_current = NULL;
00111 
00112   /* skip over head */
00113   s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
00114 
00115   while(s_current != NULL) {
00116     object = (OBJECT *) s_current->data;
00117     if (object) {
00118       /* check to see if locked_color is already being used */
00119       if (object->locked_color == -1) {
00120         object->selectable = FALSE;
00121         object->locked_color = object->color;
00122         object->color = LOCK_COLOR;
00123         w_current->toplevel->page_current->CHANGED=1;
00124       } else {
00125         s_log_message(_("Object already locked\n"));
00126       }
00127     }
00128 
00129     s_current = g_list_next(s_current);
00130   }
00131 
00132   if (!w_current->SHIFTKEY) o_select_unselect_all(w_current);
00133   o_undo_savestate(w_current, UNDO_ALL);
00134   i_update_menus(w_current);
00135 }
00136 
00142 /* You can unlock something by selecting it with a bounding box... */
00143 /* this will probably change in the future, but for now it's a
00144    something.. :-) */
00145 /* this cannot be called recursively */
00146 void o_unlock(GSCHEM_TOPLEVEL *w_current)
00147 {
00148   OBJECT *object = NULL;
00149   GList *s_current = NULL;
00150 
00151   s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
00152 
00153   while(s_current != NULL) {
00154     object = (OBJECT *) s_current->data;
00155     if (object) {
00156       /* only unlock if the object is locked */
00157       if (object->selectable == FALSE) {
00158         object->selectable = TRUE;
00159         object->color = object->locked_color;
00160         object->locked_color = -1;
00161         w_current->toplevel->page_current->CHANGED = 1;
00162       } else {
00163         s_log_message(_("Object already unlocked\n"));
00164       }
00165     }
00166 
00167     s_current = g_list_next(s_current);
00168   }
00169   o_undo_savestate(w_current, UNDO_ALL);
00170 }
00171 
00188 void o_rotate_world_update(GSCHEM_TOPLEVEL *w_current,
00189                            int centerx, int centery, int angle, GList *list)
00190 {
00191   TOPLEVEL *toplevel = w_current->toplevel;
00192   OBJECT *o_current;
00193   GList *o_iter;
00194 
00195   /* this is okay if you just hit rotate and have nothing selected */
00196   if (list == NULL) {
00197     w_current->inside_action = 0;
00198     i_set_state(w_current, SELECT);
00199     return;
00200   }
00201 
00202   o_invalidate_glist (w_current, list);
00203 
00204   /* Find connected objects, removing each object in turn from the
00205    * connection list. We only _really_ want those objects connected
00206    * to the selection, not those within in it.
00207    */
00208   for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
00209     o_current = o_iter->data;
00210 
00211     s_conn_remove_object (toplevel, o_current);
00212   }
00213 
00214   o_glist_rotate_world( toplevel, centerx, centery, angle, list );
00215 
00216   /* Find connected objects, adding each object in turn back to the
00217    * connection list. We only _really_ want those objects connected
00218    * to the selection, not those within in it.
00219    */
00220   for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
00221     o_current = o_iter->data;
00222 
00223     s_conn_update_object (toplevel, o_current);
00224   }
00225 
00226   o_invalidate_glist (w_current, list);
00227 
00228   /* Run rotate-objects-hook */
00229   g_run_hook_object_list (w_current, "%rotate-objects-hook", list);
00230 
00231   /* Don't save the undo state if we are inside an action */
00232   /* This is useful when rotating the selection while moving, for example */
00233   toplevel->page_current->CHANGED = 1;
00234   if (!w_current->inside_action) {
00235     o_undo_savestate(w_current, UNDO_ALL);
00236   }
00237 }
00238 
00244 void o_mirror_world_update(GSCHEM_TOPLEVEL *w_current, int centerx, int centery, GList *list)
00245 {
00246   TOPLEVEL *toplevel = w_current->toplevel;
00247   OBJECT *o_current;
00248   GList *o_iter;
00249 
00250   if (list == NULL) {
00251     w_current->inside_action = 0;
00252     i_set_state(w_current, SELECT);
00253     return;
00254   }
00255 
00256   o_invalidate_glist (w_current, list);
00257 
00258   /* Find connected objects, removing each object in turn from the
00259    * connection list. We only _really_ want those objects connected
00260    * to the selection, not those within in it.
00261    */
00262   for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
00263     o_current = o_iter->data;
00264 
00265     s_conn_remove_object (toplevel, o_current);
00266   }
00267 
00268   o_glist_mirror_world( toplevel, centerx, centery, list );
00269 
00270   /* Find connected objects, adding each object in turn back to the
00271    * connection list. We only _really_ want those objects connected
00272    * to the selection, not those within in it.
00273    */
00274   for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
00275     o_current = o_iter->data;
00276 
00277     s_conn_update_object (toplevel, o_current);
00278   }
00279 
00280   o_invalidate_glist (w_current, list);
00281 
00282   /* Run mirror-objects-hook */
00283   g_run_hook_object_list (w_current, "%mirror-objects-hook", list);
00284 
00285   toplevel->page_current->CHANGED=1;
00286   o_undo_savestate(w_current, UNDO_ALL);
00287 }
00288 
00294 void o_edit_show_hidden_lowlevel (GSCHEM_TOPLEVEL *w_current,
00295                                   const GList *o_list)
00296 {
00297   TOPLEVEL *toplevel = w_current->toplevel;
00298   OBJECT *o_current;
00299   const GList *iter;
00300 
00301   iter = o_list;
00302   while (iter != NULL) {
00303     o_current = (OBJECT *)iter->data;
00304     if (o_current->type == OBJ_TEXT && !o_is_visible (toplevel, o_current)) {
00305 
00306       /* don't toggle the visibility flag */
00307       o_text_recreate (toplevel, o_current);
00308     }
00309 
00310     if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
00311       o_edit_show_hidden_lowlevel(w_current, o_current->complex->prim_objs);
00312       o_recalc_single_object(toplevel, o_current);
00313     }
00314 
00315     iter = g_list_next (iter);
00316   }
00317 }
00318 
00324 void o_edit_show_hidden (GSCHEM_TOPLEVEL *w_current, const GList *o_list)
00325 {
00326   /* this function just shows the hidden text, but doesn't toggle it */
00327   /* this function does not change the CHANGED bit, no real changes are */
00328   /* made to the schematic */
00329 
00330   /* toggle show_hidden_text variable, which when it is true */
00331   /* means that hidden text IS drawn */
00332   w_current->toplevel->show_hidden_text = !w_current->toplevel->show_hidden_text;
00333   i_show_state(w_current, NULL); /* update screen status */
00334 
00335   o_edit_show_hidden_lowlevel(w_current, o_list);
00336   o_invalidate_all (w_current);
00337 
00338   if (w_current->toplevel->show_hidden_text) {
00339     s_log_message(_("Hidden text is now visible\n"));
00340   } else {
00341     s_log_message(_("Hidden text is now invisible\n"));
00342   }
00343 }
00344 
00345 #define FIND_WINDOW_HALF_SIZE (5000)
00346 
00347 OBJECT *last_o = NULL;
00348 int skiplast;
00349 
00357 int o_edit_find_text (GSCHEM_TOPLEVEL *w_current, const GList *o_list,
00358                       char *stext, int descend, int skip)
00359 {
00360   TOPLEVEL *toplevel = w_current->toplevel;
00361   char *attrib = NULL;
00362   int count = 0;
00363   PAGE *parent = NULL;
00364   char *current_filename = NULL;
00365   int page_control = 0;
00366   int pcount = 0;
00367   int rv;
00368   int text_screen_height;
00369   const GList *iter;
00370 
00371   OBJECT *o_current;
00372 
00373   skiplast = skip;
00374 
00375   iter = o_list;
00376   while (iter != NULL) {
00377     o_current = (OBJECT *)iter->data;
00378 
00379     if (descend) {
00380       if (o_current->type == OBJ_COMPLEX) {
00381         parent = toplevel->page_current;
00382         attrib = o_attrib_search_attached_attribs_by_name (o_current,
00383                                                            "source", count);
00384 
00385         /* if above is null, then look inside symbol */
00386         if (attrib == NULL) {
00387           attrib = o_attrib_search_inherited_attribs_by_name (o_current,
00388                                                               "source", count);
00389           /*          looking_inside = TRUE; */
00390         }
00391 
00392         if (attrib) {
00393           pcount = 0;
00394           current_filename = u_basic_breakup_string(attrib, ',', pcount);
00395           if (current_filename != NULL) {
00396             PAGE *child_page =
00397               s_hierarchy_down_schematic_single(toplevel,
00398                                                 current_filename,
00399                                                 parent,
00400                                                 page_control,
00401                                                 HIERARCHY_NORMAL_LOAD);
00402 
00403             if (child_page != NULL) {
00404               page_control = child_page->page_control;
00405               rv = o_edit_find_text (w_current,
00406                                      s_page_objects (child_page),
00407                                      stext, descend, skiplast);
00408               if (!rv) {
00409                 s_page_goto( toplevel, child_page );
00410                 return 0;
00411               }
00412             }
00413           }
00414         }
00415       }
00416     }
00417 
00418     if (o_current->type == OBJ_TEXT &&
00419         (o_is_visible (toplevel, o_current) || toplevel->show_hidden_text)) {
00420 
00421       const gchar *str = o_text_get_string (toplevel, o_current);
00422      /* replaced strcmp with strstr to simplify the search */
00423       if (strstr (str,stext)) {
00424         if (!skiplast) {
00425           int x1, y1, x2, y2;
00426 
00427           a_zoom(w_current, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
00428           g_assert( world_get_single_object_bounds (toplevel, o_current, &x1, &y1, &x2, &y2) );
00429           text_screen_height = SCREENabs (w_current, y2 - y1);
00430           /* this code will zoom/pan till the text screen height is about */
00431           /* 50 pixels high, perhaps a future enhancement will be to make */
00432           /* this number configurable */
00433           while (text_screen_height < 50) {
00434             a_zoom(w_current, ZOOM_IN, DONTCARE, A_PAN_DONT_REDRAW);
00435             text_screen_height = SCREENabs (w_current, y2 - y1);
00436           }
00437           a_pan_general(w_current,
00438                         o_current->text->x, o_current->text->y,
00439                         1, 0);
00440 
00441       /* Make sure the titlebar and scrollbars are up-to-date */
00442       x_window_set_current_page(w_current, 
00443                                     w_current->toplevel->page_current );
00444 
00445           last_o = o_current;
00446           break;
00447         }
00448         if (last_o == o_current) {
00449           skiplast = 0;
00450         }
00451 
00452       } /* if (strstr(o_current->text->string,stext)) */
00453     } /* if (o_current->type == OBJ_TEXT) */
00454     iter = g_list_next (iter);
00455 
00456     if (iter == NULL) {
00457       return 1;
00458     }
00459   }
00460   return (iter == NULL);
00461 }
00462 
00463 
00469 void o_edit_hide_specific_text (GSCHEM_TOPLEVEL *w_current,
00470                                 const GList *o_list,
00471                                 char *stext)
00472 {
00473   TOPLEVEL *toplevel = w_current->toplevel;
00474   OBJECT *o_current;
00475   const GList *iter;
00476 
00477   iter = o_list;
00478   while (iter != NULL) {
00479     o_current = (OBJECT *)iter->data;
00480 
00481     if (o_current->type == OBJ_TEXT) {
00482       const gchar *str = o_text_get_string (w_current->toplevel, o_current);
00483       if (!strncmp (stext, str, strlen (stext))) {
00484         if (o_is_visible (toplevel, o_current)) {
00485           o_set_visibility (toplevel, o_current, INVISIBLE);
00486           o_text_recreate(toplevel, o_current);
00487 
00488           toplevel->page_current->CHANGED = 1;
00489         }
00490       }
00491     }
00492     iter = g_list_next (iter);
00493   }
00494   o_undo_savestate(w_current, UNDO_ALL);
00495   o_invalidate_all (w_current);
00496 }
00497 
00503 void o_edit_show_specific_text (GSCHEM_TOPLEVEL *w_current,
00504                                 const GList *o_list,
00505                                 char *stext)
00506 {
00507   TOPLEVEL *toplevel = w_current->toplevel;
00508   OBJECT *o_current;
00509   const GList *iter;
00510 
00511   iter = o_list;
00512   while (iter != NULL) {
00513     o_current = (OBJECT *)iter->data;
00514 
00515     if (o_current->type == OBJ_TEXT) {
00516       const gchar *str = o_text_get_string (w_current->toplevel, o_current);
00517       if (!strncmp (stext, str, strlen (stext))) {
00518         if (!o_is_visible (toplevel, o_current)) {
00519           o_set_visibility (toplevel, o_current, VISIBLE);
00520           o_text_recreate(toplevel, o_current);
00521 
00522           toplevel->page_current->CHANGED = 1;
00523         }
00524       }
00525     }
00526     iter = g_list_next (iter);
00527   }
00528   o_undo_savestate(w_current, UNDO_ALL);
00529 }
00530 
00531 
00546 OBJECT *
00547 o_update_component (GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
00548 {
00549   TOPLEVEL *toplevel = w_current->toplevel;
00550   OBJECT *o_new;
00551   PAGE *page;
00552   GList *new_attribs;
00553   GList *old_attribs;
00554   GList *iter;
00555   const CLibSymbol *clib;
00556 
00557   g_return_val_if_fail (o_current != NULL, NULL);
00558   g_return_val_if_fail (o_current->type == OBJ_COMPLEX, NULL);
00559   g_return_val_if_fail (o_current->complex_basename != NULL, NULL);
00560 
00561   page = o_get_page (toplevel, o_current);
00562 
00563   /* Force symbol data to be reloaded from source */
00564   clib = s_clib_get_symbol_by_name (o_current->complex_basename);
00565   s_clib_symbol_invalidate_data (clib);
00566 
00567   if (clib == NULL) {
00568     s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
00569                    o_current->complex_basename);
00570     return NULL;
00571   }
00572 
00573   /* Unselect the old object. */
00574   o_selection_remove (toplevel, page->selection_list, o_current);
00575 
00576   /* Create new object and set embedded */
00577   o_new = o_complex_new (toplevel, OBJ_COMPLEX, DEFAULT_COLOR,
00578                          o_current->complex->x,
00579                          o_current->complex->y,
00580                          o_current->complex->angle,
00581                          o_current->complex->mirror,
00582                          clib, o_current->complex_basename,
00583                          1);
00584   if (o_complex_is_embedded (o_current)) {
00585     o_embed (toplevel, o_new);
00586   }
00587 
00588   new_attribs = o_complex_promote_attribs (toplevel, o_new);
00589 
00590   /* Cull any attributes from new COMPLEX that are already attached to
00591    * old COMPLEX. Note that the new_attribs list is kept consistent by
00592    * setting GList data pointers to NULL if their OBJECTs are
00593    * culled. At the end, the new_attribs list is updated by removing
00594    * all list items with NULL data. This is slightly magic, but
00595    * works. */
00596   for (iter = new_attribs; iter != NULL; iter = g_list_next (iter)) {
00597     OBJECT *attr_new = iter->data;
00598     gchar *name;
00599     gchar *value;
00600 
00601     g_assert (attr_new->type == OBJ_TEXT);
00602 
00603     o_attrib_get_name_value (attr_new, &name, NULL);
00604 
00605     value = o_attrib_search_attached_attribs_by_name (o_current, name, 0);
00606     if (value != NULL) {
00607       o_attrib_remove (toplevel, &o_new->attribs, attr_new);
00608       s_delete_object (toplevel, attr_new);
00609       iter->data = NULL;
00610     }
00611 
00612     g_free (name);
00613     g_free (value);
00614   }
00615   new_attribs = g_list_remove_all (new_attribs, NULL);
00616 
00617   /* Detach attributes from old OBJECT and attach to new OBJECT */
00618   old_attribs = g_list_copy (o_current->attribs);
00619   o_attrib_detach_all (toplevel, o_current);
00620   o_attrib_attach_list (toplevel, old_attribs, o_new, 1);
00621   g_list_free (old_attribs);
00622 
00623   /* Add new attributes to page */
00624   s_page_append_list (toplevel, page, new_attribs);
00625 
00626   /* Update pinnumbers for current slot */
00627   s_slot_update_object (toplevel, o_new);
00628 
00629   /* Replace old OBJECT with new OBJECT */
00630   s_page_replace (toplevel, page, o_current, o_new);
00631   s_delete_object (toplevel, o_current);
00632 
00633   /* Select new OBJECT */
00634   o_selection_add (toplevel, page->selection_list, o_new);
00635 
00636   /* mark the page as modified */
00637   toplevel->page_current->CHANGED = 1;
00638   o_undo_savestate (w_current, UNDO_ALL);
00639 
00640   return o_new;
00641 }
00642 
00650 void o_autosave_backups(GSCHEM_TOPLEVEL *w_current)
00651 {
00652   TOPLEVEL *toplevel = w_current->toplevel;
00653   GList *iter;
00654   PAGE *p_save, *p_current;
00655   gchar *backup_filename;
00656   gchar *real_filename;
00657   gchar *only_filename;
00658   gchar *dirname;
00659   mode_t saved_umask;
00660   mode_t mask;
00661   struct stat st;
00662 
00663   /* save current page */
00664   p_save = toplevel->page_current;
00665 
00666   for ( iter = geda_list_get_glist( toplevel->pages );
00667         iter != NULL;
00668         iter = g_list_next( iter ) ) {
00669 
00670     p_current = (PAGE *)iter->data;
00671 
00672     if (p_current->do_autosave_backup == 0) {
00673       continue;
00674     }
00675     if (p_current->ops_since_last_backup != 0) {
00676       /* make p_current the current page of toplevel */
00677       s_page_goto (toplevel, p_current);
00678 
00679       /* Get the real filename and file permissions */
00680       real_filename = follow_symlinks (p_current->page_filename, NULL);
00681 
00682       if (real_filename == NULL) {
00683         s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
00684       } else {
00685         /* Get the directory in which the real filename lives */
00686         dirname = g_path_get_dirname (real_filename);
00687         only_filename = g_path_get_basename(real_filename);
00688 
00689         backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
00690                                           dirname, G_DIR_SEPARATOR, only_filename);
00691 
00692         /* If there is not an existing file with that name, compute the
00693          * permissions and uid/gid that we will use for the newly-created file.
00694          */
00695 
00696         if (stat (real_filename, &st) != 0) {
00697 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
00698             struct stat dir_st;
00699             int result;
00700 #endif
00701 
00702             /* Use default permissions */
00703             saved_umask = umask(0);
00704             st.st_mode = 0666 & ~saved_umask;
00705             umask(saved_umask);
00706 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
00707             st.st_uid = getuid ();
00708 
00709             result = stat (dirname, &dir_st);
00710 
00711             if (result == 0 && (dir_st.st_mode & S_ISGID))
00712               st.st_gid = dir_st.st_gid;
00713             else
00714               st.st_gid = getgid ();
00715 #endif
00716           }
00717         g_free (dirname);
00718         g_free (only_filename);
00719         g_free (real_filename);
00720 
00721         /* Make the backup file writable before saving a new one */
00722         if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
00723              (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
00724           saved_umask = umask(0);
00725           if (chmod(backup_filename, (S_IWRITE|S_IWGRP|S_IWOTH) &
00726                     ((~saved_umask) & 0777)) != 0) {
00727             s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
00728                            backup_filename);
00729           }
00730           umask(saved_umask);
00731         }
00732 
00733         if (o_save (toplevel,
00734                     s_page_objects (toplevel->page_current),
00735                     backup_filename, NULL)) {
00736 
00737           p_current->ops_since_last_backup = 0;
00738                 p_current->do_autosave_backup = 0;
00739 
00740           /* Make the backup file readonly so a 'rm *' command will ask
00741              the user before deleting it */
00742           saved_umask = umask(0);
00743           mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
00744           mask = (~mask)&0777;
00745           mask &= ((~saved_umask) & 0777);
00746           if (chmod(backup_filename,mask) != 0) {
00747             s_log_message (_("Could NOT set backup file [%s] readonly\n"),
00748                            backup_filename);
00749           }
00750           umask(saved_umask);
00751         } else {
00752           s_log_message (_("Could NOT save backup file [%s]\n"),
00753                          backup_filename);
00754         }
00755         g_free (backup_filename);
00756       }
00757     }
00758   }
00759   /* restore current page */
00760   s_page_goto (toplevel, p_save);
00761 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines