libgeda

f_basic.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * libgeda - gEDA's library
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2010 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 
00025 #include <config.h>
00026 
00027 #include <stdio.h> 
00028 
00029 #ifdef HAVE_UNISTD_H
00030 #include <unistd.h> 
00031 #endif
00032 
00033 #include <sys/param.h>
00034 #include <limits.h>
00035 #include <stdlib.h>
00036 #include <time.h>
00037 #ifdef HAVE_ERRNO_H
00038 #include <errno.h>
00039 #endif
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 
00043 #ifdef HAVE_STRING_H
00044 #include <string.h>
00045 #endif
00046 
00047 # if defined (_WIN32)
00048 #  define WIN32_LEAN_AND_MEAN
00049 #  include <windows.h> /* for GetFullPathName */
00050 # endif
00051 
00052 #include "libgeda_priv.h"
00053 
00054 #ifdef HAVE_LIBDMALLOC
00055 #include <dmalloc.h>
00056 #endif
00057 
00067 gchar *f_get_autosave_filename (const gchar *filename)
00068 {
00069   gchar *result, *basename, *new_basename, *dirname;
00070   basename = g_path_get_basename(filename);
00071   dirname = g_path_get_dirname(filename);
00072   new_basename = g_strdup_printf(AUTOSAVE_BACKUP_FILENAME_STRING,
00073                                  basename);
00074   result = g_build_filename(dirname, new_basename, NULL);
00075 
00076   g_free(basename);
00077   g_free(new_basename);
00078   g_free(dirname);
00079 
00080   return result;
00081 }
00082 
00097 gboolean f_has_active_autosave (const gchar *filename, GError **err)
00098 {
00099   gboolean result = FALSE;
00100   gchar *auto_filename;
00101   gint file_err = 0;
00102   gint auto_err = 0;
00103   GFileError g_errcode = 0;
00104   struct stat file_stat, auto_stat;
00105 
00106   auto_filename = f_get_autosave_filename (filename);
00107   if (stat (filename, &file_stat) != 0) {
00108     file_err = errno;
00109   }
00110   if (stat (auto_filename, &auto_stat) != 0) {
00111     auto_err = errno;
00112   }
00113 
00114   /* A valid use of goto! (checks for raptors) */
00115   if (auto_err & ENOENT) {
00116     /* The autosave file does not exist. */
00117     result = FALSE;
00118     goto check_autosave_finish;
00119   }
00120   if (auto_err) {
00121     g_errcode = g_file_error_from_errno (auto_err);
00122     g_set_error (err, G_FILE_ERROR, g_errcode,
00123                  _("Failed to stat [%s]: %s"),
00124                  auto_filename, g_strerror (auto_err));
00125     result = TRUE;
00126     goto check_autosave_finish;
00127   }
00128   if (file_err & ENOENT) {
00129     /* The autosave file exists, but the actual file does not. */
00130     result = TRUE;
00131     goto check_autosave_finish;
00132   }
00133   if (file_err) {
00134     g_errcode = g_file_error_from_errno (file_err);
00135     g_set_error (err, G_FILE_ERROR, g_errcode,
00136                  _("Failed to stat [%s]: %s"),
00137                  auto_filename, g_strerror (file_err));
00138     result = TRUE;
00139     goto check_autosave_finish;
00140   }
00141   /* If we got this far, both files exist and we have managed to get
00142    * their stat info. */
00143   if (difftime (file_stat.st_mtime, auto_stat.st_mtime) < 0) {
00144     result = TRUE;
00145   }
00146 
00147  check_autosave_finish:
00148   g_free (auto_filename);
00149   return result;
00150 }
00151 
00165 int f_open(TOPLEVEL *toplevel, PAGE *page,
00166            const gchar *filename, GError **err)
00167 {
00168   return f_open_flags (toplevel, page, filename,
00169                        F_OPEN_RC | F_OPEN_CHECK_BACKUP, err);
00170 }
00171 
00191 int f_open_flags(TOPLEVEL *toplevel, PAGE *page,
00192                  const gchar *filename,
00193                  const gint flags, GError **err)
00194 {
00195   int opened=FALSE;
00196   char *full_filename = NULL;
00197   char *full_rcfilename = NULL;
00198   char *file_directory = NULL;
00199   char *saved_cwd = NULL;
00200   char *backup_filename = NULL;
00201   char load_backup_file = 0;
00202   GError *tmp_err = NULL;
00203 
00204   /* has the head been freed yet? */
00205   /* probably not hack PAGE */
00206 
00207   set_window(toplevel, page,
00208              toplevel->init_left, toplevel->init_right,
00209              toplevel->init_top,  toplevel->init_bottom);
00210 
00211 
00212   /* Cache the cwd so we can restore it later. */
00213   if (flags & F_OPEN_RESTORE_CWD) {
00214     saved_cwd = g_get_current_dir();
00215   }
00216 
00217   /* get full, absolute path to file */
00218   full_filename = f_normalize_filename (filename, &tmp_err);
00219   if (full_filename == NULL) {
00220     g_set_error (err, G_FILE_ERROR, tmp_err->code,
00221                  _("Cannot find file %s: %s"),
00222                  filename, tmp_err->message);
00223     g_error_free(tmp_err);
00224     return 0;
00225   }
00226 
00227   /* write full, absolute filename into page->page_filename */
00228   g_free(page->page_filename);
00229   page->page_filename = g_strdup(full_filename);
00230 
00231   /* Before we open the page, let's load the corresponding gafrc. */
00232   /* First cd into file's directory. */
00233   file_directory = g_dirname (full_filename);
00234 
00235   if (file_directory) { 
00236     if (chdir (file_directory)) {
00237       /* Error occurred with chdir */
00238 #warning FIXME: What do we do?
00239     }
00240   }
00241 
00242   /* Now open RC and process file */
00243   if (flags & F_OPEN_RC) {
00244     full_rcfilename = g_build_filename (file_directory, "gafrc", NULL);
00245     g_rc_parse_file (toplevel, full_rcfilename, &tmp_err);
00246     if (tmp_err != NULL) {
00247       /* Config files are allowed to be missing or skipped; check for
00248        * this. */
00249       if (!g_error_matches (tmp_err, G_FILE_ERROR, G_FILE_ERROR_NOENT) &&
00250           !g_error_matches (tmp_err, EDA_ERROR, EDA_ERROR_RC_TWICE)) {
00251         s_log_message ("%s\n", tmp_err->message);
00252       }
00253       g_error_free (tmp_err);
00254       tmp_err = NULL;
00255     }
00256   }
00257 
00258   g_free (file_directory);
00259 
00260   if (flags & F_OPEN_CHECK_BACKUP) {
00261     /* Check if there is a newer autosave backup file */
00262     GString *message;
00263     gboolean active_backup = f_has_active_autosave (full_filename, &tmp_err);
00264     backup_filename = f_get_autosave_filename (full_filename);
00265 
00266     if (tmp_err != NULL) g_warning ("%s\n", tmp_err->message);
00267     if (active_backup) {
00268       message = g_string_new ("");
00269       g_string_append_printf(message, _("\nWARNING: Found an autosave backup file:\n  %s.\n\n"), backup_filename);
00270       if (tmp_err != NULL) {
00271         g_string_append(message, _("I could not guess if it is newer, so you have to do it manually.\n"));
00272       } else {
00273         g_string_append(message, _("The backup copy is newer than the schematic, so it seems you should load it instead of the original file.\n"));
00274       }
00275       g_string_append (message, _("Gschem usually makes backup copies automatically, and this situation happens when it crashed or it was forced to exit abruptly.\n"));
00276       if (toplevel->load_newer_backup_func == NULL) {
00277         g_warning ("%s", message->str);
00278         g_warning (_("\nRun gschem and correct the situation.\n\n"));
00279       } else {
00280         /* Ask the user if load the backup or the original file */
00281         if (toplevel->load_newer_backup_func
00282             (toplevel->load_newer_backup_data, message)) {
00283           /* Load the backup file */
00284           load_backup_file = 1;
00285         }
00286       }
00287       g_string_free (message, TRUE);
00288     }
00289     if (tmp_err != NULL) g_error_free (tmp_err);
00290   }
00291 
00292   /* Now that we have set the current directory and read
00293    * the RC file, it's time to read in the file. */
00294   if (load_backup_file == 1) {
00295     /* Load the backup file */
00296     s_page_append_list (toplevel, page,
00297                         o_read (toplevel, NULL, backup_filename, &tmp_err));
00298   } else {
00299     /* Load the original file */
00300     s_page_append_list (toplevel, page,
00301                         o_read (toplevel, NULL, full_filename, &tmp_err));
00302   }
00303 
00304   if (tmp_err == NULL)
00305     opened = TRUE;
00306   else
00307     g_propagate_error (err, tmp_err);
00308 
00309   if (load_backup_file == 0) {
00310     /* If it's not the backup file */
00311     page->CHANGED=0; /* added 4/7/98 */
00312   } else {
00313     /* We are loading the backup file, so gschem should ask
00314        the user if save it or not when closing the page. */
00315     page->CHANGED=1;
00316   }
00317 
00318   g_free(full_filename);
00319   g_free(full_rcfilename);
00320   g_free (backup_filename);
00321 
00322   /* Reset the directory to the value it had when f_open was
00323    * called. */
00324   if (flags & F_OPEN_RESTORE_CWD) {
00325     if (chdir (saved_cwd)) {
00326       /* Error occurred with chdir */
00327 #warning FIXME: What do we do?
00328     }
00329     g_free(saved_cwd);
00330   }
00331 
00332   return opened;
00333 }
00334 
00341 void f_close(TOPLEVEL *toplevel)
00342 {
00343 
00344 }
00345 
00359 int f_save(TOPLEVEL *toplevel, PAGE *page, const char *filename, GError **err)
00360 {
00361   gchar *backup_filename;
00362   gchar *real_filename;
00363   gchar *only_filename;
00364   gchar *dirname;
00365   mode_t saved_umask, mask;
00366   struct stat st;
00367   GError *tmp_err = NULL;
00368 
00369   /* Get the real filename and file permissions */
00370   real_filename = follow_symlinks (filename, &tmp_err);
00371 
00372   if (real_filename == NULL) {
00373     g_set_error (err, tmp_err->domain, tmp_err->code,
00374                  _("Can't get the real filename of %s: %s"),
00375                  filename,
00376                  tmp_err->message);
00377     return 0;
00378   }
00379 
00380   /* Check to see if filename is writable */
00381   if (g_file_test(filename, G_FILE_TEST_EXISTS) && 
00382       g_access(filename, W_OK) != 0) {
00383     g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_PERM,
00384                  _("File %s is read-only"), filename);
00385     return 0;      
00386   }
00387   
00388   /* Get the directory in which the real filename lives */
00389   dirname = g_path_get_dirname (real_filename);
00390   only_filename = g_path_get_basename(real_filename);  
00391 
00392   /* Do a backup if it's not an undo file backup and it was never saved.
00393    * Only do a backup if backup files are enabled */
00394   if (page->saved_since_first_loaded == 0 && toplevel->make_backup_files == TRUE) {
00395     if ( (g_file_test (real_filename, G_FILE_TEST_EXISTS)) && 
00396      (!g_file_test(real_filename, G_FILE_TEST_IS_DIR)) )
00397     {
00398       backup_filename = g_strdup_printf("%s%c%s~", dirname, 
00399                     G_DIR_SEPARATOR, only_filename);
00400 
00401       /* Make the backup file read-write before saving a new one */
00402       if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) && 
00403        (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
00404     if (chmod(backup_filename, S_IREAD|S_IWRITE) != 0) {
00405       s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
00406              backup_filename);
00407     }
00408       }
00409     
00410       if (rename(real_filename, backup_filename) != 0) {
00411     s_log_message (_("Can't save backup file: %s."), backup_filename);
00412       }
00413       else {
00414     /* Make the backup file readonly so a 'rm *' command will ask 
00415        the user before deleting it */
00416     saved_umask = umask(0);
00417     mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
00418     mask = (~mask)&0777;
00419     mask &= ((~saved_umask) & 0777);
00420     if (chmod(backup_filename, mask) != 0) {
00421       s_log_message (_("Could NOT set backup file [%s] readonly\n"),
00422                          backup_filename);
00423     }
00424     umask(saved_umask);
00425       }
00426 
00427       g_free(backup_filename);
00428     }
00429   }
00430     /* If there is not an existing file with that name, compute the
00431      * permissions and uid/gid that we will use for the newly-created file.
00432      */
00433        
00434   if (stat (real_filename, &st) != 0)
00435   {
00436     struct stat dir_st;
00437     int result;
00438     
00439     /* Use default permissions */
00440     saved_umask = umask(0);
00441     st.st_mode = 0666 & ~saved_umask;
00442     umask(saved_umask);
00443 #ifdef HAVE_CHOWN
00444     st.st_uid = getuid ();
00445     
00446     result = stat (dirname, &dir_st);
00447     
00448     if (result == 0 && (dir_st.st_mode & S_ISGID))
00449       st.st_gid = dir_st.st_gid;
00450     else
00451     st.st_gid = getgid ();
00452 #endif /* HAVE_CHOWN */
00453   }
00454   g_free (dirname);
00455   g_free (only_filename);
00456   
00457   if (o_save (toplevel, s_page_objects (page), real_filename, &tmp_err)) {
00458 
00459     page->saved_since_first_loaded = 1;
00460 
00461     /* Reset the last saved timer */
00462     g_get_current_time (&page->last_load_or_save_time);
00463     page->ops_since_last_backup = 0;
00464     page->do_autosave_backup = 0;
00465 
00466     /* Restore permissions. */
00467     chmod (real_filename, st.st_mode);
00468 #ifdef HAVE_CHOWN
00469     if (chown (real_filename, st.st_uid, st.st_gid)) {
00470       /* Error occured with chown */
00471 #warning FIXME: What do we do?
00472     }
00473 #endif
00474 
00475     g_free (real_filename);
00476     return 1;
00477   }
00478   else {
00479     g_set_error (err, tmp_err->domain, tmp_err->code,
00480                  _("Could NOT save file: %s"), tmp_err->message);
00481     g_clear_error (&tmp_err);
00482     g_free (real_filename);
00483     return 0;
00484   }
00485 }
00486 
00510 gchar *f_normalize_filename (const gchar *name, GError **error)
00511 {
00512 #if defined (_WIN32)
00513     char buf[MAX_PATH];
00514 #else
00515     GString *rpath;
00516     const char *start, *end;
00517 #endif
00518 
00519   if (name == NULL) {
00520     g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
00521                  "%s", g_strerror (EINVAL));
00522     return NULL;
00523   }
00524 
00525   if (*name == '\0') {
00526     g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
00527                  "%s", g_strerror (ENOENT));
00528     return NULL;
00529   }
00530 
00531 #if defined (_WIN32)
00532   /* Windows method (modified) from libiberty's lrealpath.c, GPL V2+
00533    *
00534    * We assume we don't have symlinks and just canonicalize to a
00535    * Windows absolute path.  GetFullPathName converts ../ and ./ in
00536    * relative paths to absolute paths, filling in current drive if
00537    * one is not given or using the current directory of a specified
00538    * drive (eg, "E:foo"). It also converts all forward slashes to
00539    * back slashes.
00540    */
00541   DWORD len = GetFullPathName (name, MAX_PATH, buf, NULL);
00542   gchar *result;
00543 
00544   if (len == 0 || len > MAX_PATH - 1) {
00545     result = g_strdup (name);
00546   } else {
00547     /* The file system is case-preserving but case-insensitive,
00548      * canonicalize to lowercase, using the codepage associated
00549      * with the process locale.  */
00550     CharLowerBuff (buf, len);
00551     result = g_strdup (buf);
00552   }
00553 
00554   /* Test that the file actually exists, and fail if it doesn't.  We
00555    * do this to be consistent with the behaviour on POSIX
00556    * platforms. */
00557   if (!g_file_test (result, G_FILE_TEST_EXISTS)) {
00558     g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
00559                  "%s", g_strerror (ENOENT));
00560     g_free (result);
00561     return NULL;
00562   }
00563   return result;
00564 
00565 #else
00566 #define ROOT_MARKER_LEN 1
00567 
00568   rpath = g_string_sized_new (strlen (name));
00569 
00570   /* if relative path, prepend current dir */
00571   if (!g_path_is_absolute (name)) {
00572     gchar *cwd = g_get_current_dir ();
00573     g_string_append (rpath, cwd);
00574     g_free (cwd);
00575     if (!G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1])) {
00576       g_string_append_c (rpath, G_DIR_SEPARATOR);
00577     }
00578   } else {
00579     g_string_append_len (rpath, name, ROOT_MARKER_LEN);
00580     /* move to first path separator */
00581     name += ROOT_MARKER_LEN - 1;
00582   }
00583 
00584   for (start = end = name; *start != '\0'; start = end) {
00585     /* skip sequence of multiple path-separators */
00586     while (G_IS_DIR_SEPARATOR (*start)) {
00587       ++start;
00588     }
00589 
00590     /* find end of path component */
00591     for (end = start; *end != '\0' && !G_IS_DIR_SEPARATOR (*end); ++end);
00592 
00593     if (end - start == 0) {
00594       break;
00595     } else if (end - start == 1 && start[0] == '.') {
00596       /* nothing */;
00597     } else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
00598       /* back up to previous component, ignore if at root already.  */
00599       if (rpath->len > ROOT_MARKER_LEN) {
00600         while (!G_IS_DIR_SEPARATOR (rpath->str[(--rpath->len) - 1]));
00601         g_string_set_size (rpath, rpath->len);
00602       }
00603     } else {
00604       /* path component, copy to new path */
00605       if (!G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1])) {
00606         g_string_append_c (rpath, G_DIR_SEPARATOR);
00607       }
00608 
00609       g_string_append_len (rpath, start, end - start);
00610 
00611       if (!g_file_test (rpath->str, G_FILE_TEST_EXISTS)) {
00612         g_set_error (error,G_FILE_ERROR, G_FILE_ERROR_NOENT,
00613                      "%s", g_strerror (ENOENT));
00614         goto error;
00615       } else if (!g_file_test (rpath->str, G_FILE_TEST_IS_DIR) &&
00616                  *end != '\0') {
00617         g_set_error (error,G_FILE_ERROR, G_FILE_ERROR_NOTDIR,
00618                      "%s", g_strerror (ENOTDIR));
00619         goto error;
00620       }
00621     }
00622   }
00623 
00624   if (G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1]) &&
00625       rpath->len > ROOT_MARKER_LEN) {
00626     g_string_set_size (rpath, rpath->len - 1);
00627   }
00628 
00629   return g_string_free (rpath, FALSE);
00630 
00631 error:
00632   g_string_free (rpath, TRUE);
00633   return NULL;
00634 #undef ROOT_MARKER_LEN
00635 #endif
00636 }
00637 
00650 char *follow_symlinks (const gchar *filename, GError **err)
00651 {
00652   gchar *followed_filename = NULL;
00653   gint link_count = 0;
00654   GError *tmp_err = NULL;
00655 
00656   if (filename == NULL) {
00657     g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_INVAL,
00658                  "%s", g_strerror (EINVAL));
00659     return NULL;
00660   }
00661 
00662   if (strlen (filename) + 1 > MAXPATHLEN) {
00663     g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NAMETOOLONG,
00664                  "%s", g_strerror (ENAMETOOLONG));
00665     return NULL;
00666   }
00667 
00668   followed_filename = g_strdup (filename);
00669 
00670 #ifdef __MINGW32__
00671   /* MinGW does not have symlinks */
00672   return followed_filename;
00673 #else
00674 
00675   while (link_count < MAX_LINK_LEVEL) {
00676     struct stat st;
00677     gchar *linkname = NULL;
00678 
00679     if (lstat (followed_filename, &st) != 0) {
00680       /* We could not access the file, so perhaps it does not
00681        * exist.  Return this as a real name so that we can
00682        * attempt to create the file.
00683        */
00684       return followed_filename;
00685     }
00686 
00687     if (!S_ISLNK (st.st_mode)) {
00688       /* It's not a link, so we've found what we're looking for! */
00689       return followed_filename;
00690     }
00691 
00692     link_count++;
00693 
00694     linkname = g_file_read_link (followed_filename, &tmp_err);
00695 
00696     if (linkname == NULL) {
00697       g_propagate_error(err, tmp_err);
00698       g_free (followed_filename);
00699       return NULL;
00700     }
00701 
00702     /* If the linkname is not an absolute path name, append
00703      * it to the directory name of the followed filename.  E.g.
00704      * we may have /foo/bar/baz.lnk -> eek.txt, which really
00705      * is /foo/bar/eek.txt.
00706      */
00707 
00708     if (!g_path_is_absolute(linkname)) {
00709       gchar *dirname = NULL;
00710       gchar *tmp = NULL;
00711 
00712       dirname = g_path_get_dirname(followed_filename);
00713 
00714       tmp = g_build_filename (dirname, linkname, NULL);
00715       g_free (followed_filename);
00716       g_free (dirname);
00717       g_free (linkname);
00718       followed_filename = tmp;
00719     } else {
00720       g_free (followed_filename);
00721       followed_filename = linkname;
00722     }
00723   }
00724 
00725   /* Too many symlinks */
00726   g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_LOOP,
00727                _("%s: %s"), g_strerror (EMLINK), followed_filename);
00728   g_free (followed_filename);
00729   return NULL;
00730 
00731 #endif /* __MINGW32__ */
00732 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines