Fixed transpositions, typos, and random spelling errors.
[geda-gaf/peter-b.git] / libgeda / src / f_basic.c
blob7cd7a9c3a38f741b0b7cd371f20b9fbd1cb4801b
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 #include <config.h>
22 #include <stdio.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
28 #include <sys/param.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <time.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
35 #include <gtk/gtk.h>
36 #include <libguile.h>
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
41 #include "defines.h"
42 #include "struct.h"
43 #include "globals.h"
45 #include "../include/prototype.h"
47 #ifdef HAVE_LIBDMALLOC
48 #include <dmalloc.h>
49 #endif
51 /*! \brief Opens the schematic file.
52 * \par Function Description
53 * Opens the schematic file by calling f_open_flags() with the
54 * F_OPEN_RC and F_OPEN_CHECK_BACKUP flags.
56 * \param [in,out] w_current The TOPLEVEL object to load the schematic into.
57 * \param [in] filename A character string containing the file name
58 * to open.
59 * \return 0 on failure, 1 on success.
61 int f_open(TOPLEVEL *w_current, const gchar *filename)
63 return f_open_flags (w_current, filename,
64 F_OPEN_RC | F_OPEN_CHECK_BACKUP);
67 /*! \brief Opens the schematic file with fine-grained control over behaviour.
68 * \par Function Description
69 * Opens the schematic file and carries out a number of actions
70 * depending on the \a flags set. If #F_OPEN_RC is set, executes
71 * configuration files found in the target directory. If
72 * #F_OPEN_CHECK_BACKUP is set, warns user if a backup is found for
73 * the file being loaded, and possibly prompts user for whether to
74 * load the backup instead. If #F_OPEN_RESTORE_CWD is set, does not
75 * change the working directory to that of the file being loaded.
77 * \param [in,out] w_current The TOPLEVEL object to load the schematic into.
78 * \param [in] filename A character string containing the file name
79 * to open.
80 * \param [in] flags Combination of #FOpenFlags values.
81 * \return 0 on failure, 1 on success.
83 int f_open_flags(TOPLEVEL *w_current, const gchar *filename,
84 const gint flags)
86 int opened=FALSE;
87 char *full_filename = NULL;
88 char *full_rcfilename = NULL;
89 char *file_directory = NULL;
90 char *saved_cwd = NULL;
91 char *backup_filename = NULL;
92 char load_backup_file = 0;
94 /* has the head been freed yet? */
95 /* probably not hack PAGE */
97 set_window(w_current, w_current->page_current,
98 w_current->init_left, w_current->init_right,
99 w_current->init_top, w_current->init_bottom);
102 /* Cache the cwd so we can restore it later. */
103 /*! \bug Assumes cwd will be less than 1024 characters. */
104 if (flags & F_OPEN_RESTORE_CWD) {
105 saved_cwd = getcwd(NULL, 1024);
108 /* get full, absolute path to file */
109 full_filename = f_normalize_filename(filename);
111 /* write full, absolute filename into page_current->page_filename */
112 if (w_current->page_current->page_filename) {
113 g_free(w_current->page_current->page_filename);
115 w_current->page_current->page_filename = g_strdup(full_filename);
117 /* Before we open the page, let's load the corresponding gafrc. */
118 /* First cd into file's directory. */
119 file_directory = g_dirname (full_filename);
121 if (file_directory) {
122 chdir(file_directory);
123 /*! \bug Probably should do some checking of chdir return values */
126 /* Now open RC and process file */
127 if (flags & F_OPEN_RC) {
128 full_rcfilename = g_strconcat (file_directory,
129 G_DIR_SEPARATOR_S,
130 "gafrc",
131 NULL);
132 g_rc_parse_specified_rc(w_current, full_rcfilename);
135 if (flags & F_OPEN_CHECK_BACKUP) {
136 /* Check if there is a newer autosave backup file */
137 backup_filename
138 = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
139 file_directory, G_DIR_SEPARATOR,
140 g_path_get_basename(full_filename));
142 g_free (file_directory);
144 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
145 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
146 /* An autosave backup file exists. Check if it's newer */
147 struct stat stat_backup;
148 struct stat stat_file;
149 char error_stat = 0;
150 GString *message;
152 if (stat (backup_filename, &stat_backup) != 0) {
153 s_log_message ("f_open: Unable to get stat information of backup file %s.",
154 backup_filename);
155 error_stat = 1 ;
157 if (stat (full_filename, &stat_file) != 0) {
158 s_log_message ("f_open: Unable to get stat information of file %s.",
159 full_filename);
160 error_stat = 1;
162 if ((difftime (stat_file.st_ctime, stat_backup.st_ctime) < 0) ||
163 (error_stat == 1))
165 /* Found an autosave backup. It's newer if error_stat is 0 */
166 message = g_string_new ("");
167 g_string_append_printf(message, "\nWARNING: Found an autosave backup file:\n %s.\n\n", backup_filename);
168 if (error_stat == 1) {
169 g_string_append(message, "I could not guess if it is newer, so you have to"
170 "do it manually.\n");
172 else {
173 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");
175 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");
176 if (w_current->page_current->load_newer_backup_func == NULL) {
177 s_log_message(message->str);
178 s_log_message("\nRun gschem and correct the situation.\n\n");
179 fprintf(stderr, message->str);
180 fprintf(stderr, "\nRun gschem and correct the situation.\n\n");
182 else {
183 /* Ask the user if load the backup or the original file */
184 if (w_current->page_current->load_newer_backup_func
185 (w_current, message)) {
186 /* Load the backup file */
187 load_backup_file = 1;
190 g_string_free (message, TRUE);
195 /* Now that we have set the current directory and read
196 * the RC file, it's time to read in the file. */
197 if (load_backup_file == 1) {
198 /* Load the backup file */
199 w_current->page_current->object_tail = (OBJECT *)
200 o_read(w_current, w_current->page_current->object_tail,
201 backup_filename);
203 else {
204 /* Load the original file */
205 w_current->page_current->object_tail = (OBJECT *)
206 o_read(w_current, w_current->page_current->object_tail,
207 full_filename);
210 if (w_current->page_current->object_tail != NULL) {
211 s_log_message("Opened file [%s]\n", full_filename);
212 opened = TRUE;
213 } else {
214 /* Failed to open page */
215 opened = FALSE;
218 w_current->page_current->object_tail
219 = (OBJECT *) return_tail(w_current->page_current->object_head);
221 /* make sure you init net_consolide to false (default) in all */
222 /* programs */
223 if (w_current->net_consolidate == TRUE) {
224 o_net_consolidate(w_current);
227 if (load_backup_file == 0) {
228 /* If it's not the backup file */
229 w_current->page_current->CHANGED=0; /* added 4/7/98 */
231 else {
232 /* We are loading the backup file, so gschem should ask
233 the user if save it or not when closing the page. */
234 w_current->page_current->CHANGED=1;
237 g_free(full_filename);
238 g_free(full_rcfilename);
239 g_free (backup_filename);
241 /* Reset the directory to the value it had when f_open was
242 * called. */
243 if (flags & F_OPEN_RESTORE_CWD) {
244 chdir(saved_cwd);
245 g_free(saved_cwd);
248 if (!opened) {
249 return (FALSE);
250 } else {
251 return (TRUE);
255 /*! \brief Closes the schematic file
256 * \par Function Description
257 * Does nothing
259 * \param [in,out] w_current The TOPLEVEL object with schematic to be closed.
261 void f_close(TOPLEVEL *w_current)
266 /*! \brief Save schematic file and close
267 * \par Function Description
268 * This function will save the current schematic file before closing it.
269 * It also deletes the page_current item in the TOPLEVEL structure.
271 * \param [in,out] w_current The TOPLEVEL object containing the schematic.
272 * \param [in] filename The file name to save the schematic to.
274 void f_save_close(TOPLEVEL *w_current, char *filename)
276 o_save(w_current, filename);
277 s_page_delete (w_current, w_current->page_current);
280 /*! \brief Save the schematic file
281 * \par Function Description
282 * This function saves the current schematic file in the w_current object.
284 * \param [in,out] w_current The TOPLEVEL object containing the schematic.
285 * \param [in] filename The file name to save the schematic to.
286 * \return 1 on success, 0 on failure.
288 int f_save(TOPLEVEL *w_current, const char *filename)
290 gchar *backup_filename;
291 gchar *real_filename;
292 gchar *only_filename;
293 gchar *dirname;
294 mode_t saved_umask, mask;
295 struct stat st;
297 /* Get the real filename and file permissions */
298 real_filename = follow_symlinks (filename, NULL);
300 if (real_filename == NULL) {
301 s_log_message ("Can't get the real filename of %s.", filename);
302 fprintf (stderr, "Can't get the real filename of %s.\n", filename);
303 return 0;
306 /* Get the directory in which the real filename lives */
307 dirname = g_path_get_dirname (real_filename);
308 only_filename = g_path_get_basename(real_filename);
310 /* Do a backup if it's not an undo file backup and it was never saved. */
311 if (w_current->page_current->saved_since_first_loaded == 0) {
312 if ( (g_file_test (real_filename, G_FILE_TEST_EXISTS)) &&
313 (!g_file_test(real_filename, G_FILE_TEST_IS_DIR)) )
315 backup_filename = g_strdup_printf("%s%c%s~", dirname,
316 G_DIR_SEPARATOR, only_filename);
318 /* Make the backup file read-write before saving a new one */
319 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
320 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
321 if (chmod(backup_filename, S_IREAD|S_IWRITE) != 0) {
322 s_log_message ("Could NOT set previous backup file [%s] read-write\n",
323 backup_filename);
327 if (rename(real_filename, backup_filename) != 0) {
328 s_log_message ("Can't save backup file: %s.", backup_filename);
329 fprintf (stderr, "Can't save backup file: %s.", backup_filename);
331 else {
332 /* Make the backup file readonly so a 'rm *' command will ask
333 the user before deleting it */
334 saved_umask = umask(0);
335 mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
336 mask = (~mask)&0777;
337 mask &= ((~saved_umask) & 0777);
338 if (chmod(backup_filename, mask) != 0) {
339 s_log_message ("Could NOT set backup file [%s] readonly\n",
340 backup_filename);
342 umask(saved_umask);
345 g_free(backup_filename);
348 /* If there is not an existing file with that name, compute the
349 * permissions and uid/gid that we will use for the newly-created file.
352 if (stat (real_filename, &st) != 0)
354 struct stat dir_st;
355 int result;
357 /* Use default permissions */
358 saved_umask = umask(0);
359 st.st_mode = 0666 & ~saved_umask;
360 umask(saved_umask);
361 #ifdef HAVE_CHOWN
362 st.st_uid = getuid ();
364 result = stat (dirname, &dir_st);
366 if (result == 0 && (dir_st.st_mode & S_ISGID))
367 st.st_gid = dir_st.st_gid;
368 else
369 st.st_gid = getgid ();
370 #endif /* HAVE_CHOWN */
372 g_free (dirname);
373 g_free (only_filename);
375 if (o_save(w_current, real_filename)) {
377 w_current->page_current->saved_since_first_loaded = 1;
379 /* Reset the last saved timer */
380 g_get_current_time (&w_current->page_current->last_load_or_save_time);
381 w_current->page_current->ops_since_last_backup = 0;
382 w_current->page_current->do_autosave_backup = 0;
384 /* Restore permissions. */
385 chmod (real_filename, st.st_mode);
386 #ifdef HAVE_CHOWN
387 chown (real_filename, st.st_uid, st.st_gid);
388 #endif
390 g_free (real_filename);
391 return 1;
393 else {
394 g_free (real_filename);
395 return 0;
399 /*! \brief Reformats a filename as an absolute resolved filename
400 * \par Function Description
401 * Given a filename in any format, this returns the full, absolute
402 * resolved filename.
404 * \param [in] filename A character string containing the file
405 * name to resolve.
406 * \return A character string with the resolved filename.
408 char* f_normalize_filename(const gchar *filename)
410 char filename_buffer[MAXPATHLEN]; /* nasty hack for realpath */
411 char *full_filename;
413 /* Check for pathological case */
414 if (filename == NULL) {
415 return NULL;
418 realpath(filename, filename_buffer); /* places reult in filename_buffer */
419 full_filename = g_strdup (filename_buffer);
421 #ifdef DEBUG
422 printf("In f_normalize_filename, returning full_filename= %s \n", full_filename);
423 #endif
425 return full_filename;
428 /*! \brief Follow symlinks until a real file is found
429 * \par Function Description
430 * Does readlink() recursively until we find a real filename.
432 * \param [in] filename The filename to search for.
433 * \param [out] error Unused, set to NULL
434 * \return The path to real file on success, NULL otherwise.
436 * \note Taken from gedit's source code.
438 char *follow_symlinks (const gchar *filename, GError **error)
440 gchar *followed_filename;
441 gint link_count;
443 g_return_val_if_fail (filename != NULL, NULL);
445 g_return_val_if_fail (strlen (filename) + 1 <= MAXPATHLEN, NULL);
447 followed_filename = g_strdup (filename);
448 link_count = 0;
450 while (link_count < MAX_LINK_LEVEL) {
451 struct stat st;
453 if (lstat (followed_filename, &st) != 0)
454 /* We could not access the file, so perhaps it does not
455 * exist. Return this as a real name so that we can
456 * attempt to create the file.
458 return followed_filename;
460 if (S_ISLNK (st.st_mode)) {
461 gint len;
462 gchar linkname[MAXPATHLEN];
464 link_count++;
466 len = readlink (followed_filename, linkname, MAXPATHLEN - 1);
468 if (len == -1) {
469 s_log_message("Could not read symbolic link information for %s", followed_filename);
470 fprintf(stderr, "Could not read symbolic link information for %s", followed_filename);
471 g_free (followed_filename);
472 return NULL;
475 linkname[len] = '\0';
477 /* If the linkname is not an absolute path name, append
478 * it to the directory name of the followed filename. E.g.
479 * we may have /foo/bar/baz.lnk -> eek.txt, which really
480 * is /foo/bar/eek.txt.
483 if (linkname[0] != G_DIR_SEPARATOR) {
484 gchar *slashpos;
485 gchar *tmp;
487 slashpos = strrchr (followed_filename, G_DIR_SEPARATOR);
489 if (slashpos)
490 *slashpos = '\0';
491 else {
492 tmp = g_strconcat ("./", followed_filename, NULL);
493 g_free (followed_filename);
494 followed_filename = tmp;
497 tmp = g_build_filename (followed_filename, linkname, NULL);
498 g_free (followed_filename);
499 followed_filename = tmp;
500 } else {
501 g_free (followed_filename);
502 followed_filename = g_strdup (linkname);
504 } else
505 return followed_filename;
508 /* Too many symlinks */
510 s_log_message("The file has too many symbolic links.");
511 fprintf(stderr, "The file has too many symbolic links.");
513 return NULL;