libgeda: Remove some exit() calls and assertions.
[geda-gaf/peter-b.git] / libgeda / src / f_basic.c
blob5ee61c2552a9e8717311bf56e23bf1a17f24f5a4
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
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.
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.
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
21 /*! \file f_basic.c
22 * \brief file related functions
25 #include <config.h>
27 #include <stdio.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
33 #include <sys/param.h>
34 #include <limits.h>
35 #include <stdlib.h>
36 #include <time.h>
37 #ifdef HAVE_ERRNO_H
38 #include <errno.h>
39 #endif
40 #include <sys/types.h>
41 #include <sys/stat.h>
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
47 # if defined (_WIN32)
48 # define WIN32_LEAN_AND_MEAN
49 # include <windows.h> /* for GetFullPathName */
50 # endif
52 #include "libgeda_priv.h"
54 #ifdef HAVE_LIBDMALLOC
55 #include <dmalloc.h>
56 #endif
58 /*! \brief Get the autosave filename for a file
59 * \par Function description
60 * Returns the expected autosave filename for the \a filename passed.
62 * \warning The result should be freed when no longer needed.
64 * \param [in] filename The filename to create an autosave filename for.
65 * \return A newly allocated string buffer.
67 gchar *f_get_autosave_filename (const gchar *filename)
69 gchar *result, *basename, *new_basename, *dirname;
70 basename = g_path_get_basename(filename);
71 dirname = g_path_get_dirname(filename);
72 new_basename = g_strdup_printf(AUTOSAVE_BACKUP_FILENAME_STRING,
73 basename);
74 result = g_build_filename(dirname, new_basename, NULL);
76 g_free(basename);
77 g_free(new_basename);
78 g_free(dirname);
80 return result;
83 /*! \brief Check if a file has an active autosave file
84 * \par Function Description
85 * Checks whether an autosave file exists for the \a filename passed
86 * which has a modification time newer than the file itself. If the
87 * check fails, sets \a err with the reason. N.b. if the autosave
88 * file exists but it was not possible to get its modification time,
89 * returns TRUE.
91 * \param [in] filename File to check
92 * \param [in,out] err #GError structure for error reporting, or
93 * NULL to disable error reporting
95 * \returns TRUE if autosave active, FALSE otherwise
97 gboolean f_has_active_autosave (const gchar *filename, GError **err)
99 gboolean result = FALSE;
100 gchar *auto_filename;
101 gint file_err = 0;
102 gint auto_err = 0;
103 GFileError g_errcode = 0;
104 struct stat file_stat, auto_stat;
106 auto_filename = f_get_autosave_filename (filename);
107 if (stat (filename, &file_stat) != 0) {
108 file_err = errno;
110 if (stat (auto_filename, &auto_stat) != 0) {
111 auto_err = errno;
114 /* A valid use of goto! (checks for raptors) */
115 if (auto_err & ENOENT) {
116 /* The autosave file does not exist. */
117 result = FALSE;
118 goto check_autosave_finish;
120 if (auto_err) {
121 g_errcode = g_file_error_from_errno (auto_err);
122 g_set_error (err, G_FILE_ERROR, g_errcode,
123 _("Failed to stat [%s]: %s"),
124 auto_filename, g_strerror (auto_err));
125 result = TRUE;
126 goto check_autosave_finish;
128 if (file_err & ENOENT) {
129 /* The autosave file exists, but the actual file does not. */
130 result = TRUE;
131 goto check_autosave_finish;
133 if (file_err) {
134 g_errcode = g_file_error_from_errno (file_err);
135 g_set_error (err, G_FILE_ERROR, g_errcode,
136 _("Failed to stat [%s]: %s"),
137 auto_filename, g_strerror (file_err));
138 result = TRUE;
139 goto check_autosave_finish;
141 /* If we got this far, both files exist and we have managed to get
142 * their stat info. */
143 if (difftime (file_stat.st_mtime, auto_stat.st_mtime) < 0) {
144 result = TRUE;
147 check_autosave_finish:
148 g_free (auto_filename);
149 return result;
152 /*! \brief Opens the schematic file.
153 * \par Function Description
154 * Opens the schematic file by calling f_open_flags() with the
155 * F_OPEN_RC and F_OPEN_CHECK_BACKUP flags.
157 * \param [in,out] toplevel The TOPLEVEL object to load the schematic into.
158 * \param [in] filename A character string containing the file name
159 * to open.
160 * \param [in,out] err #GError structure for error reporting, or
161 * NULL to disable error reporting
163 * \return 0 on failure, 1 on success.
165 int f_open(TOPLEVEL *toplevel, PAGE *page,
166 const gchar *filename, GError **err)
168 return f_open_flags (toplevel, page, filename,
169 F_OPEN_RC | F_OPEN_CHECK_BACKUP, err);
172 /*! \brief Opens the schematic file with fine-grained control over behaviour.
173 * \par Function Description
174 * Opens the schematic file and carries out a number of actions
175 * depending on the \a flags set. If #F_OPEN_RC is set, executes
176 * configuration files found in the target directory. If
177 * #F_OPEN_CHECK_BACKUP is set, warns user if a backup is found for
178 * the file being loaded, and possibly prompts user for whether to
179 * load the backup instead. If #F_OPEN_RESTORE_CWD is set, does not
180 * change the working directory to that of the file being loaded.
182 * \param [in,out] toplevel The TOPLEVEL object to load the schematic into.
183 * \param [in] filename A character string containing the file name
184 * to open.
185 * \param [in] flags Combination of #FOpenFlags values.
186 * \param [in,out] err #GError structure for error reporting, or
187 * NULL to disable error reporting
189 * \return 0 on failure, 1 on success.
191 int f_open_flags(TOPLEVEL *toplevel, PAGE *page,
192 const gchar *filename,
193 const gint flags, GError **err)
195 int opened=FALSE;
196 char *full_filename = NULL;
197 char *full_rcfilename = NULL;
198 char *file_directory = NULL;
199 char *saved_cwd = NULL;
200 char *backup_filename = NULL;
201 char load_backup_file = 0;
202 GError *tmp_err = NULL;
204 /* has the head been freed yet? */
205 /* probably not hack PAGE */
207 set_window(toplevel, page,
208 toplevel->init_left, toplevel->init_right,
209 toplevel->init_top, toplevel->init_bottom);
212 /* Cache the cwd so we can restore it later. */
213 if (flags & F_OPEN_RESTORE_CWD) {
214 saved_cwd = g_get_current_dir();
217 /* get full, absolute path to file */
218 full_filename = f_normalize_filename (filename, &tmp_err);
219 if (full_filename == NULL) {
220 g_set_error (err, G_FILE_ERROR, tmp_err->code,
221 _("Cannot find file %s: %s"),
222 filename, tmp_err->message);
223 g_error_free(tmp_err);
224 return 0;
227 /* write full, absolute filename into page->page_filename */
228 g_free(page->page_filename);
229 page->page_filename = g_strdup(full_filename);
231 /* Before we open the page, let's load the corresponding gafrc. */
232 /* First cd into file's directory. */
233 file_directory = g_dirname (full_filename);
235 if (file_directory) {
236 if (chdir (file_directory)) {
237 /* Error occurred with chdir */
238 #warning FIXME: What do we do?
242 /* Now open RC and process file */
243 if (flags & F_OPEN_RC) {
244 full_rcfilename = g_build_filename (file_directory, "gafrc", NULL);
245 g_rc_parse_file (toplevel, full_rcfilename, &tmp_err);
246 if (tmp_err != NULL) {
247 /* Config files are allowed to be missing or skipped; check for
248 * this. */
249 if (!g_error_matches (tmp_err, G_FILE_ERROR, G_FILE_ERROR_NOENT) &&
250 !g_error_matches (tmp_err, EDA_ERROR, EDA_ERROR_RC_TWICE)) {
251 s_log_message ("%s\n", tmp_err->message);
253 g_error_free (tmp_err);
254 tmp_err = NULL;
258 g_free (file_directory);
260 if (flags & F_OPEN_CHECK_BACKUP) {
261 /* Check if there is a newer autosave backup file */
262 GString *message;
263 gboolean active_backup = f_has_active_autosave (full_filename, &tmp_err);
264 backup_filename = f_get_autosave_filename (full_filename);
266 if (tmp_err != NULL) g_warning ("%s\n", tmp_err->message);
267 if (active_backup) {
268 message = g_string_new ("");
269 g_string_append_printf(message, _("\nWARNING: Found an autosave backup file:\n %s.\n\n"), backup_filename);
270 if (tmp_err != NULL) {
271 g_string_append(message, _("I could not guess if it is newer, so you have to do it manually.\n"));
272 } else {
273 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"));
275 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"));
276 if (toplevel->load_newer_backup_func == NULL) {
277 g_warning ("%s", message->str);
278 g_warning (_("\nRun gschem and correct the situation.\n\n"));
279 } else {
280 /* Ask the user if load the backup or the original file */
281 if (toplevel->load_newer_backup_func
282 (toplevel->load_newer_backup_data, message)) {
283 /* Load the backup file */
284 load_backup_file = 1;
287 g_string_free (message, TRUE);
289 if (tmp_err != NULL) g_error_free (tmp_err);
292 /* Now that we have set the current directory and read
293 * the RC file, it's time to read in the file. */
294 if (load_backup_file == 1) {
295 /* Load the backup file */
296 s_page_append_list (toplevel, page,
297 o_read (toplevel, NULL, backup_filename, &tmp_err));
298 } else {
299 /* Load the original file */
300 s_page_append_list (toplevel, page,
301 o_read (toplevel, NULL, full_filename, &tmp_err));
304 if (tmp_err == NULL)
305 opened = TRUE;
306 else
307 g_propagate_error (err, tmp_err);
309 if (load_backup_file == 0) {
310 /* If it's not the backup file */
311 page->CHANGED=0; /* added 4/7/98 */
312 } else {
313 /* We are loading the backup file, so gschem should ask
314 the user if save it or not when closing the page. */
315 page->CHANGED=1;
318 g_free(full_filename);
319 g_free(full_rcfilename);
320 g_free (backup_filename);
322 /* Reset the directory to the value it had when f_open was
323 * called. */
324 if (flags & F_OPEN_RESTORE_CWD) {
325 if (chdir (saved_cwd)) {
326 /* Error occurred with chdir */
327 #warning FIXME: What do we do?
329 g_free(saved_cwd);
332 return opened;
335 /*! \brief Closes the schematic file
336 * \par Function Description
337 * Does nothing
339 * \param [in,out] toplevel The TOPLEVEL object with schematic to be closed.
341 void f_close(TOPLEVEL *toplevel)
346 /*! \brief Save the schematic file
347 * \par Function Description
348 * This function saves the current schematic file in the toplevel object.
350 * \bug g_access introduces a race condition in certain cases, but
351 * solves bug #698565 in the normal use-case
353 * \param [in,out] toplevel The TOPLEVEL object containing the schematic.
354 * \param [in] filename The file name to save the schematic to.
355 * \param [in,out] err #GError structure for error reporting, or
356 * NULL to disable error reporting
357 * \return 1 on success, 0 on failure.
359 int f_save(TOPLEVEL *toplevel, PAGE *page, const char *filename, GError **err)
361 gchar *backup_filename;
362 gchar *real_filename;
363 gchar *only_filename;
364 gchar *dirname;
365 mode_t saved_umask, mask;
366 struct stat st;
367 GError *tmp_err = NULL;
369 /* Get the real filename and file permissions */
370 real_filename = follow_symlinks (filename, &tmp_err);
372 if (real_filename == NULL) {
373 g_set_error (err, tmp_err->domain, tmp_err->code,
374 _("Can't get the real filename of %s: %s"),
375 filename,
376 tmp_err->message);
377 return 0;
380 /* Check to see if filename is writable */
381 if (g_file_test(filename, G_FILE_TEST_EXISTS) &&
382 g_access(filename, W_OK) != 0) {
383 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_PERM,
384 _("File %s is read-only"), filename);
385 return 0;
388 /* Get the directory in which the real filename lives */
389 dirname = g_path_get_dirname (real_filename);
390 only_filename = g_path_get_basename(real_filename);
392 /* Do a backup if it's not an undo file backup and it was never saved. */
393 if (page->saved_since_first_loaded == 0) {
394 if ( (g_file_test (real_filename, G_FILE_TEST_EXISTS)) &&
395 (!g_file_test(real_filename, G_FILE_TEST_IS_DIR)) )
397 backup_filename = g_strdup_printf("%s%c%s~", dirname,
398 G_DIR_SEPARATOR, only_filename);
400 /* Make the backup file read-write before saving a new one */
401 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
402 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
403 if (chmod(backup_filename, S_IREAD|S_IWRITE) != 0) {
404 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
405 backup_filename);
409 if (rename(real_filename, backup_filename) != 0) {
410 s_log_message (_("Can't save backup file: %s."), backup_filename);
412 else {
413 /* Make the backup file readonly so a 'rm *' command will ask
414 the user before deleting it */
415 saved_umask = umask(0);
416 mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
417 mask = (~mask)&0777;
418 mask &= ((~saved_umask) & 0777);
419 if (chmod(backup_filename, mask) != 0) {
420 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
421 backup_filename);
423 umask(saved_umask);
426 g_free(backup_filename);
429 /* If there is not an existing file with that name, compute the
430 * permissions and uid/gid that we will use for the newly-created file.
433 if (stat (real_filename, &st) != 0)
435 struct stat dir_st;
436 int result;
438 /* Use default permissions */
439 saved_umask = umask(0);
440 st.st_mode = 0666 & ~saved_umask;
441 umask(saved_umask);
442 #ifdef HAVE_CHOWN
443 st.st_uid = getuid ();
445 result = stat (dirname, &dir_st);
447 if (result == 0 && (dir_st.st_mode & S_ISGID))
448 st.st_gid = dir_st.st_gid;
449 else
450 st.st_gid = getgid ();
451 #endif /* HAVE_CHOWN */
453 g_free (dirname);
454 g_free (only_filename);
456 if (o_save (toplevel, s_page_objects (page), real_filename, &tmp_err)) {
458 page->saved_since_first_loaded = 1;
460 /* Reset the last saved timer */
461 g_get_current_time (&page->last_load_or_save_time);
462 page->ops_since_last_backup = 0;
463 page->do_autosave_backup = 0;
465 /* Restore permissions. */
466 chmod (real_filename, st.st_mode);
467 #ifdef HAVE_CHOWN
468 if (chown (real_filename, st.st_uid, st.st_gid)) {
469 /* Error occured with chown */
470 #warning FIXME: What do we do?
472 #endif
474 g_free (real_filename);
475 return 1;
477 else {
478 g_set_error (err, tmp_err->domain, tmp_err->code,
479 _("Could NOT save file: %s"), tmp_err->message);
480 g_clear_error (&tmp_err);
481 g_free (real_filename);
482 return 0;
486 /*! \brief Builds an absolute pathname.
487 * \par Function Description
488 * This function derives an absolute pathname for the pathname
489 * pointed to by \a name with '.' and '..' resolved. It does not
490 * resolve symbolic links.
492 * It returns NULL and sets the \a error (if not NULL) if it failed
493 * to build the pathname or the pathname does not exists.
495 * \note
496 * The code for this function is derived from the realpath() of
497 * the GNU C Library (Copyright (C) 1996-2002, 2004, 2005, 2006 Free
498 * Software Foundation, Inc. / LGPL 2.1 or later).
500 * The part for the resolution of symbolic links has been discarded
501 * and it has been adapted for glib and for use on Windows.
503 * \param [in] name A character string containing the pathname
504 * to resolve.
505 * \param [in,out] error Return location for a GError, or NULL.
506 * \return A newly-allocated string with the resolved absolute
507 * pathname on success, NULL otherwise.
509 gchar *f_normalize_filename (const gchar *name, GError **error)
511 #if defined (_WIN32)
512 char buf[MAX_PATH];
513 #else
514 GString *rpath;
515 const char *start, *end;
516 #endif
518 if (name == NULL) {
519 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
520 "%s", g_strerror (EINVAL));
521 return NULL;
524 if (*name == '\0') {
525 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
526 "%s", g_strerror (ENOENT));
527 return NULL;
530 #if defined (_WIN32)
531 /* Windows method (modified) from libiberty's lrealpath.c, GPL V2+
533 * We assume we don't have symlinks and just canonicalize to a
534 * Windows absolute path. GetFullPathName converts ../ and ./ in
535 * relative paths to absolute paths, filling in current drive if
536 * one is not given or using the current directory of a specified
537 * drive (eg, "E:foo"). It also converts all forward slashes to
538 * back slashes.
540 DWORD len = GetFullPathName (name, MAX_PATH, buf, NULL);
541 gchar *result;
543 if (len == 0 || len > MAX_PATH - 1) {
544 result = g_strdup (name);
545 } else {
546 /* The file system is case-preserving but case-insensitive,
547 * canonicalize to lowercase, using the codepage associated
548 * with the process locale. */
549 CharLowerBuff (buf, len);
550 result = g_strdup (buf);
553 /* Test that the file actually exists, and fail if it doesn't. We
554 * do this to be consistent with the behaviour on POSIX
555 * platforms. */
556 if (!g_file_test (result, G_FILE_TEST_EXISTS)) {
557 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
558 "%s", g_strerror (ENOENT));
559 g_free (result);
560 return NULL;
562 return result;
564 #else
565 #define ROOT_MARKER_LEN 1
567 rpath = g_string_sized_new (strlen (name));
569 /* if relative path, prepend current dir */
570 if (!g_path_is_absolute (name)) {
571 gchar *cwd = g_get_current_dir ();
572 g_string_append (rpath, cwd);
573 g_free (cwd);
574 if (!G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1])) {
575 g_string_append_c (rpath, G_DIR_SEPARATOR);
577 } else {
578 g_string_append_len (rpath, name, ROOT_MARKER_LEN);
579 /* move to first path separator */
580 name += ROOT_MARKER_LEN - 1;
583 for (start = end = name; *start != '\0'; start = end) {
584 /* skip sequence of multiple path-separators */
585 while (G_IS_DIR_SEPARATOR (*start)) {
586 ++start;
589 /* find end of path component */
590 for (end = start; *end != '\0' && !G_IS_DIR_SEPARATOR (*end); ++end);
592 if (end - start == 0) {
593 break;
594 } else if (end - start == 1 && start[0] == '.') {
595 /* nothing */;
596 } else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
597 /* back up to previous component, ignore if at root already. */
598 if (rpath->len > ROOT_MARKER_LEN) {
599 while (!G_IS_DIR_SEPARATOR (rpath->str[(--rpath->len) - 1]));
600 g_string_set_size (rpath, rpath->len);
602 } else {
603 /* path component, copy to new path */
604 if (!G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1])) {
605 g_string_append_c (rpath, G_DIR_SEPARATOR);
608 g_string_append_len (rpath, start, end - start);
610 if (!g_file_test (rpath->str, G_FILE_TEST_EXISTS)) {
611 g_set_error (error,G_FILE_ERROR, G_FILE_ERROR_NOENT,
612 "%s", g_strerror (ENOENT));
613 goto error;
614 } else if (!g_file_test (rpath->str, G_FILE_TEST_IS_DIR) &&
615 *end != '\0') {
616 g_set_error (error,G_FILE_ERROR, G_FILE_ERROR_NOTDIR,
617 "%s", g_strerror (ENOTDIR));
618 goto error;
623 if (G_IS_DIR_SEPARATOR (rpath->str[rpath->len - 1]) &&
624 rpath->len > ROOT_MARKER_LEN) {
625 g_string_set_size (rpath, rpath->len - 1);
628 return g_string_free (rpath, FALSE);
630 error:
631 g_string_free (rpath, TRUE);
632 return NULL;
633 #undef ROOT_MARKER_LEN
634 #endif
637 /*! \brief Follow symlinks until a real file is found
638 * \par Function Description
639 * Does readlink() recursively until we find a real filename.
641 * \param [in] filename The filename to search for.
642 * \param [in,out] err #GError structure for error reporting,
643 * or NULL to disable error reporting.
644 * \return The newly-allocated path to real file on success, NULL
645 * otherwise.
647 * \note Originally taken from gedit's source code.
649 char *follow_symlinks (const gchar *filename, GError **err)
651 gchar *followed_filename = NULL;
652 gint link_count = 0;
653 GError *tmp_err = NULL;
655 if (filename == NULL) {
656 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_INVAL,
657 "%s", g_strerror (EINVAL));
658 return NULL;
661 if (strlen (filename) + 1 > MAXPATHLEN) {
662 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NAMETOOLONG,
663 "%s", g_strerror (ENAMETOOLONG));
664 return NULL;
667 followed_filename = g_strdup (filename);
669 #ifdef __MINGW32__
670 /* MinGW does not have symlinks */
671 return followed_filename;
672 #else
674 while (link_count < MAX_LINK_LEVEL) {
675 struct stat st;
676 gchar *linkname = NULL;
678 if (lstat (followed_filename, &st) != 0) {
679 /* We could not access the file, so perhaps it does not
680 * exist. Return this as a real name so that we can
681 * attempt to create the file.
683 return followed_filename;
686 if (!S_ISLNK (st.st_mode)) {
687 /* It's not a link, so we've found what we're looking for! */
688 return followed_filename;
691 link_count++;
693 linkname = g_file_read_link (followed_filename, &tmp_err);
695 if (linkname == NULL) {
696 g_propagate_error(err, tmp_err);
697 g_free (followed_filename);
698 return NULL;
701 /* If the linkname is not an absolute path name, append
702 * it to the directory name of the followed filename. E.g.
703 * we may have /foo/bar/baz.lnk -> eek.txt, which really
704 * is /foo/bar/eek.txt.
707 if (!g_path_is_absolute(linkname)) {
708 gchar *dirname = NULL;
709 gchar *tmp = NULL;
711 dirname = g_path_get_dirname(followed_filename);
713 tmp = g_build_filename (dirname, linkname, NULL);
714 g_free (followed_filename);
715 g_free (dirname);
716 g_free (linkname);
717 followed_filename = tmp;
718 } else {
719 g_free (followed_filename);
720 followed_filename = linkname;
724 /* Too many symlinks */
725 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_LOOP,
726 _("%s: %s"), g_strerror (EMLINK), followed_filename);
727 g_free (followed_filename);
728 return NULL;
730 #endif /* __MINGW32__ */