libgeda
|
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 }