Esempio n. 1
0
/*! \brief Save the schematic file
 *  \par Function Description
 *  This function saves the current schematic file in the toplevel object.
 *
 *  \param [in,out] toplevel  The TOPLEVEL object containing the schematic.
 *  \param [in]      filename  The file name to save the schematic to.
 *  \param [in,out] err       #GError structure for error reporting, or
 *                            NULL to disable error reporting
 *  \return 1 on success, 0 on failure.
 */
int f_save(TOPLEVEL *toplevel, PAGE *page, const char *filename, GError **err)
{
  gchar *backup_filename;
  gchar *real_filename;
  gchar *only_filename;
  gchar *dirname;
  mode_t saved_umask, mask;
  struct stat st;
  GError *tmp_err = NULL;

  /* Get the real filename and file permissions */
  real_filename = follow_symlinks (filename, &tmp_err);

  if (real_filename == NULL) {
    g_set_error (err, tmp_err->domain, tmp_err->code,
                 _("Can't get the real filename of %s: %s"),
                 filename,
                 tmp_err->message);
    return 0;
  }
  
  /* Get the directory in which the real filename lives */
  dirname = g_path_get_dirname (real_filename);
  only_filename = g_path_get_basename(real_filename);  

  /* Do a backup if it's not an undo file backup and it was never saved. */
  if (page->saved_since_first_loaded == 0) {
    if ( (g_file_test (real_filename, G_FILE_TEST_EXISTS)) && 
	 (!g_file_test(real_filename, G_FILE_TEST_IS_DIR)) )
    {
      backup_filename = g_strdup_printf("%s%c%s~", dirname, 
					G_DIR_SEPARATOR, only_filename);

      /* Make the backup file read-write before saving a new one */
      if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) && 
	   (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
	if (chmod(backup_filename, S_IREAD|S_IWRITE) != 0) {
	  s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
			 backup_filename);
	}
      }
	
      if (rename(real_filename, backup_filename) != 0) {
	s_log_message (_("Can't save backup file: %s."), backup_filename);
      }
      else {
	/* Make the backup file readonly so a 'rm *' command will ask 
	   the user before deleting it */
	saved_umask = umask(0);
	mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
	mask = (~mask)&0777;
	mask &= ((~saved_umask) & 0777);
	if (chmod(backup_filename, mask) != 0) {
	  s_log_message (_("Could NOT set backup file [%s] readonly\n"),
                         backup_filename);
	}
	umask(saved_umask);
      }

      g_free(backup_filename);
    }
  }
    /* If there is not an existing file with that name, compute the
     * permissions and uid/gid that we will use for the newly-created file.
     */
       
  if (stat (real_filename, &st) != 0)
  {
    struct stat dir_st;
    int result;
    
    /* Use default permissions */
    saved_umask = umask(0);
    st.st_mode = 0666 & ~saved_umask;
    umask(saved_umask);
#ifdef HAVE_CHOWN
    st.st_uid = getuid ();
    
    result = stat (dirname, &dir_st);
    
    if (result == 0 && (dir_st.st_mode & S_ISGID))
	  st.st_gid = dir_st.st_gid;
    else
    st.st_gid = getgid ();
#endif /* HAVE_CHOWN */
  }
  g_free (dirname);
  g_free (only_filename);
  
  if (o_save (toplevel, s_page_objects (page), real_filename, &tmp_err)) {

    page->saved_since_first_loaded = 1;

    /* Reset the last saved timer */
    g_get_current_time (&page->last_load_or_save_time);
    page->ops_since_last_backup = 0;
    page->do_autosave_backup = 0;

    /* Restore permissions. */
    chmod (real_filename, st.st_mode);
#ifdef HAVE_CHOWN
    if (chown (real_filename, st.st_uid, st.st_gid)) {
      /* Error occured with chown */
#warning FIXME: What do we do?
    }
#endif

    g_free (real_filename);
    return 1;
  }
  else {
    g_set_error (err, tmp_err->domain, tmp_err->code,
                 _("Could NOT save file: %s"), tmp_err->message);
    g_clear_error (&tmp_err);
    g_free (real_filename);
    return 0;
  }
}
Esempio n. 2
0
/*! \brief Do autosave on all pages that are marked.
 *  \par Function Description
 *  Looks for pages with the do_autosave_backup flag activated and
 *  autosaves them.
 *
 *  \param [in] w_current  The GSCHEM_TOPLEVEL object to search for autosave's.
 */
void o_autosave_backups(GSCHEM_TOPLEVEL *w_current)
{
  TOPLEVEL *toplevel = w_current->toplevel;
  GList *iter;
  PAGE *p_save, *p_current;
  gchar *backup_filename;
  gchar *real_filename;
  gchar *only_filename;
  gchar *dirname;
  mode_t saved_umask;
  mode_t mask;
  struct stat st;

  /* save current page */
  p_save = toplevel->page_current;

  for ( iter = geda_list_get_glist( toplevel->pages );
        iter != NULL;
        iter = g_list_next( iter ) ) {

    p_current = (PAGE *)iter->data;

    if (p_current->do_autosave_backup == 0) {
      continue;
    }
    if (p_current->ops_since_last_backup != 0) {
      /* make p_current the current page of toplevel */
      s_page_goto (toplevel, p_current);

      /* Get the real filename and file permissions */
      real_filename = follow_symlinks (p_current->page_filename, NULL);

      if (real_filename == NULL) {
        s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
      } else {
        /* Get the directory in which the real filename lives */
        dirname = g_path_get_dirname (real_filename);
        only_filename = g_path_get_basename(real_filename);

        backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
                                          dirname, G_DIR_SEPARATOR, only_filename);

        /* If there is not an existing file with that name, compute the
         * permissions and uid/gid that we will use for the newly-created file.
         */

        if (stat (real_filename, &st) != 0) {
#if defined(HAVE_GETUID) && defined(HAVE_GETGID)
            struct stat dir_st;
            int result;
#endif

            /* Use default permissions */
            saved_umask = umask(0);
            st.st_mode = 0666 & ~saved_umask;
            umask(saved_umask);
#if defined(HAVE_GETUID) && defined(HAVE_GETGID)
            st.st_uid = getuid ();

            result = stat (dirname, &dir_st);

            if (result == 0 && (dir_st.st_mode & S_ISGID))
              st.st_gid = dir_st.st_gid;
            else
              st.st_gid = getgid ();
#endif
          }
        g_free (dirname);
        g_free (only_filename);
        g_free (real_filename);

        /* Make the backup file writable before saving a new one */
        if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
             (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
          saved_umask = umask(0);
          if (chmod(backup_filename, (S_IWRITE|S_IWGRP|S_IWOTH) &
                    ((~saved_umask) & 0777)) != 0) {
            s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
                           backup_filename);
          }
          umask(saved_umask);
        }

        if (o_save (toplevel,
                    s_page_objects (toplevel->page_current),
                    backup_filename, NULL)) {

          p_current->ops_since_last_backup = 0;
                p_current->do_autosave_backup = 0;

          /* Make the backup file readonly so a 'rm *' command will ask
             the user before deleting it */
          saved_umask = umask(0);
          mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
          mask = (~mask)&0777;
          mask &= ((~saved_umask) & 0777);
          if (chmod(backup_filename,mask) != 0) {
            s_log_message (_("Could NOT set backup file [%s] readonly\n"),
                           backup_filename);
          }
          umask(saved_umask);
        } else {
          s_log_message (_("Could NOT save backup file [%s]\n"),
                         backup_filename);
        }
        g_free (backup_filename);
      }
    }
  }
  /* restore current page */
  s_page_goto (toplevel, p_save);
}
Esempio n. 3
0
/*! \todo Finish function documentation!!!
 *  \brief
 *  \par Function Description
 *
 *  
 *  <B>flag</B> can be one of the following values:
 *  <DL>
 *    <DT>*</DT><DD>UNDO_ALL
 *    <DT>*</DT><DD>UNDO_VIEWPORT_ONLY
 *  </DL>
 */
void o_undo_savestate(GSCHEM_TOPLEVEL *w_current, int flag)
{
  TOPLEVEL *toplevel = w_current->toplevel;
  char *filename = NULL;
  GList *object_list = NULL;
  int levels;
  UNDO *u_current;
  UNDO *u_current_next;

  /* save autosave backups if necessary */
  o_autosave_backups(w_current);

  if (w_current->undo_control == FALSE) {
    return;
  }

  if (flag == UNDO_ALL) {

    /* Increment the number of operations since last backup if 
       auto-save is enabled */
    if (toplevel->auto_save_interval != 0) {
      toplevel->page_current->ops_since_last_backup++;
    }

    /* HACK */
    /* Before we save the undo state, consolidate nets as necessary */

    /* This is where the net consolidation call would have been
     * triggered before it was removed from o_save_buffer().
     */
    if (toplevel->net_consolidate == TRUE)
      o_net_consolidate (toplevel, toplevel->page_current);
  }

  if (w_current->undo_type == UNDO_DISK && flag == UNDO_ALL) {

    filename = g_strdup_printf("%s%cgschem.save%d_%d.sch",
                               tmp_path, G_DIR_SEPARATOR,
                               prog_pid, undo_file_index++);

    /* Changed from f_save to o_save when adding backup copy creation. */
    /* f_save manages the creaton of backup copies. 
       This way, f_save is called only when saving a file, and not when
       saving an undo backup copy */
    o_save (toplevel, s_page_objects (toplevel->page_current), filename, NULL);

  } else if (w_current->undo_type == UNDO_MEMORY && flag == UNDO_ALL) {
    object_list = o_glist_copy_all (toplevel,
                                    s_page_objects (toplevel->page_current),
                                    object_list);
  }

  /* Clear Anything above current */
  if (toplevel->page_current->undo_current) {
    s_undo_remove_rest(toplevel,
                       toplevel->page_current->undo_current->next);
    toplevel->page_current->undo_current->next = NULL;
  } else { /* undo current is NULL */
    s_undo_remove_rest(toplevel,
                       toplevel->page_current->undo_bottom);
    toplevel->page_current->undo_bottom = NULL;
  }

  toplevel->page_current->undo_tos = toplevel->page_current->undo_current;

  toplevel->page_current->undo_tos =
  s_undo_add(toplevel->page_current->undo_tos,
             flag, filename, object_list,
             toplevel->page_current->left,
             toplevel->page_current->top,
             toplevel->page_current->right,
             toplevel->page_current->bottom,
             toplevel->page_current->page_control,
             toplevel->page_current->up);

  toplevel->page_current->undo_current =
      toplevel->page_current->undo_tos;

  if (toplevel->page_current->undo_bottom == NULL) {
    toplevel->page_current->undo_bottom =
        toplevel->page_current->undo_tos;
  }

#if DEBUG
  printf("\n\n---Undo----\n");
  s_undo_print_all(toplevel->page_current->undo_bottom);
  printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
  printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
  printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
  printf("----\n");
#endif

  g_free(filename);

  /* Now go through and see if we need to free/remove some undo levels */ 
  /* so we stay within the limits */

  /* only check history every 10 undo savestates */
  if (undo_file_index % 10) {
    return;
  }

  levels = s_undo_levels(toplevel->page_current->undo_bottom);

#if DEBUG
  printf("levels: %d\n", levels);
#endif

  if (levels >= w_current->undo_levels + UNDO_PADDING) {
    levels = levels - w_current->undo_levels;

#if DEBUG
    printf("Trimming: %d levels\n", levels);
#endif

    u_current = toplevel->page_current->undo_bottom;
    while(u_current && levels > 0) {
      u_current_next = u_current->next;

      if (u_current->filename) {
#if DEBUG
        printf("Freeing: %s\n", u_current->filename);
#endif
        unlink(u_current->filename);
        g_free(u_current->filename);
      }

      if (u_current->object_list) {
        s_delete_object_glist (toplevel, u_current->object_list);
        u_current->object_list = NULL;
      }

      u_current->next = NULL;
      u_current->prev = NULL;
      g_free(u_current);

      u_current = u_current_next;
      levels--;
    }

    /* Because we use a pad you are always garanteed to never */
    /* exhaust the list */
    u_current->prev = NULL;
    toplevel->page_current->undo_bottom = u_current;

#if DEBUG
    printf("New current is: %s\n", u_current->filename);
#endif
  }

#if DEBUG
  printf("\n\n---Undo----\n");
  s_undo_print_all(toplevel->page_current->undo_bottom);
  printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
  printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
  printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
  printf("----\n");
#endif

}
Esempio n. 4
0
/*! \todo Finish function documentation!!!
 *  \brief
 *  \par Function Description
 *
 *
 *  <B>flag</B> can be one of the following values:
 *  <DL>
 *    <DT>*</DT><DD>UNDO_ALL
 *    <DT>*</DT><DD>UNDO_VIEWPORT_ONLY
 *  </DL>
 */
void
o_undo_savestate (GschemToplevel *w_current, PAGE *page, int flag)
{
  TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
  char *filename = NULL;
  GList *object_list = NULL;
  int levels;
  UNDO *u_current;
  UNDO *u_current_next;

  GschemPageView *view = gschem_toplevel_get_current_page_view (w_current);
  g_return_if_fail (view != NULL);

  g_return_if_fail (page != NULL);

  GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);

  /* save autosave backups if necessary */
  o_autosave_backups(w_current);

  if (w_current->undo_control == FALSE) {
    return;
  }

  if (flag == UNDO_ALL) {

    /* Increment the number of operations since last backup if
       auto-save is enabled */
    if (toplevel->auto_save_interval != 0) {
      page->ops_since_last_backup++;
    }

    /* HACK */
    /* Before we save the undo state, consolidate nets as necessary */

    /* This is where the net consolidation call would have been
     * triggered before it was removed from o_save_buffer().
     */
    if (toplevel->net_consolidate == TRUE)
      geda_net_object_consolidate (toplevel, page);
  }

  if (w_current->undo_type == UNDO_DISK && flag == UNDO_ALL) {

    filename = g_strdup_printf("%s%cgschem.save%d_%d.sch",
                               tmp_path, G_DIR_SEPARATOR,
                               prog_pid, undo_file_index++);

    /* Changed from f_save to o_save when adding backup copy creation. */
    /* f_save manages the creaton of backup copies.
       This way, f_save is called only when saving a file, and not when
       saving an undo backup copy */
    o_save (toplevel, s_page_objects (page), filename, NULL);

  } else if (w_current->undo_type == UNDO_MEMORY && flag == UNDO_ALL) {
    object_list = o_glist_copy_all (toplevel,
                                    s_page_objects (page),
                                    object_list);
  }

  /* Clear Anything above current */
  if (page->undo_current) {
    s_undo_remove_rest(toplevel, page->undo_current->next);
    page->undo_current->next = NULL;
  } else { /* undo current is NULL */
    s_undo_remove_rest(toplevel, page->undo_bottom);
    page->undo_bottom = NULL;
  }

  page->undo_tos = page->undo_current;

  if (geometry != NULL) {
    page->undo_tos = s_undo_add(page->undo_tos,
                                flag, filename, object_list,
                                (geometry->viewport_left + geometry->viewport_right) / 2,
                                (geometry->viewport_top + geometry->viewport_bottom) / 2,
                                /* scale */
                                max (((double) abs (geometry->viewport_right - geometry->viewport_left) / geometry->screen_width),
                                  ((double) abs (geometry->viewport_top - geometry->viewport_bottom) / geometry->screen_height)),
                                page->page_control,
                                page->up);
  } else {
    page->undo_tos = s_undo_add(page->undo_tos,
                                flag, filename, object_list,
                                0, /* center x */
                                0, /* center y */
                                0, /* scale */
                                page->page_control,
                                page->up);
  }

  page->undo_current =
      page->undo_tos;

  if (page->undo_bottom == NULL) {
    page->undo_bottom =
        page->undo_tos;
  }

#if DEBUG
  printf("\n\n---Undo----\n");
  s_undo_print_all(page->undo_bottom);
  printf("BOTTOM: %s\n", page->undo_bottom->filename);
  printf("TOS: %s\n", page->undo_tos->filename);
  printf("CURRENT: %s\n", page->undo_current->filename);
  printf("----\n");
#endif

  g_free(filename);

  /* Now go through and see if we need to free/remove some undo levels */
  /* so we stay within the limits */

  /* only check history every 10 undo savestates */
  if (undo_file_index % 10) {
    return;
  }

  levels = s_undo_levels(page->undo_bottom);

#if DEBUG
  printf("levels: %d\n", levels);
#endif

  if (levels >= w_current->undo_levels + UNDO_PADDING) {
    levels = levels - w_current->undo_levels;

#if DEBUG
    printf("Trimming: %d levels\n", levels);
#endif

    u_current = page->undo_bottom;

    while (levels > 0) {
      /* Because we use a pad you are always guaranteed to never */
      /* exhaust the list */
      g_assert (u_current != NULL);

      u_current_next = u_current->next;

      if (u_current->filename) {
#if DEBUG
        printf("Freeing: %s\n", u_current->filename);
#endif
        unlink(u_current->filename);
        g_free(u_current->filename);
      }

      if (u_current->object_list) {
        geda_object_list_delete (toplevel, u_current->object_list);
        u_current->object_list = NULL;
      }

      u_current->next = NULL;
      u_current->prev = NULL;
      g_free(u_current);

      u_current = u_current_next;
      levels--;
    }

    g_assert (u_current != NULL);
    u_current->prev = NULL;
    page->undo_bottom = u_current;

#if DEBUG
    printf("New current is: %s\n", u_current->filename);
#endif
  }

#if DEBUG
  printf("\n\n---Undo----\n");
  s_undo_print_all(page->undo_bottom);
  printf("BOTTOM: %s\n", page->undo_bottom->filename);
  printf("TOS: %s\n", page->undo_tos->filename);
  printf("CURRENT: %s\n", page->undo_current->filename);
  printf("----\n");
#endif

}
Esempio n. 5
0
/*! \brief Save schematic file and close
 *  \par Function Description
 *  This function will save the current schematic file before closing it.
 *  It also deletes the page_current item in the TOPLEVEL structure.
 *
 *  \param [in,out] toplevel  The TOPLEVEL object containing the schematic.
 *  \param [in]      filename  The file name to save the schematic to.
 */
void f_save_close(TOPLEVEL *toplevel, char *filename)
{
  o_save(toplevel, filename);
  s_page_delete (toplevel, toplevel->page_current);
}