GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-filepath-utils.c
1 /********************************************************************\
2  * gnc-filepath-utils.c -- file path resolutin utilitie *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA [email protected] *
20 \********************************************************************/
21 
22 /*
23  * @file gnc-filepath-utils.c
24  * @brief file path resolution utilities
25  * @author Copyright (c) 1998-2004 Linas Vepstas <[email protected]>
26  * @author Copyright (c) 2000 Dave Peticolas
27  */
28 
29 #include "config.h"
30 
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include <glib/gprintf.h>
34 #include <glib/gstdio.h>
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <errno.h>
45 
46 #include "gnc-path.h"
47 #include "gnc-filepath-utils.h"
48 #include "libqof/qof/qof.h"
49 
50 #ifdef _MSC_VER
51 #include <glib/gwin32.h>
52 #define PATH_MAX MAXPATHLEN
53 #endif
54 
55 /* This static indicates the debugging module that this .o belongs to. */
56 static QofLogModule log_module = G_LOG_DOMAIN;
57 
58 
65 static void
66 scrub_filename(char* filename)
67 {
68  char* p;
69 
70 #define STRANGE_CHARS "/:"
71  p = strpbrk(filename, STRANGE_CHARS);
72  while (p)
73  {
74  *p = '_';
75  p = strpbrk(filename, STRANGE_CHARS);
76  }
77 }
78 
85 static gchar *
86 check_path_return_if_valid(gchar *path)
87 {
88  if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
89  {
90  return path;
91  }
92  g_free (path);
93  return NULL;
94 }
95 
123 gchar *
124 gnc_resolve_file_path (const gchar * filefrag)
125 {
126  gchar *fullpath = NULL, *tmp_path = NULL;
127 
128  /* seriously invalid */
129  if (!filefrag)
130  {
131  g_critical("filefrag is NULL");
132  return NULL;
133  }
134 
135  /* ---------------------------------------------------- */
136  /* OK, now we try to find or build an absolute file path */
137 
138  /* check for an absolute file path */
139  if (g_path_is_absolute(filefrag))
140  return g_strdup (filefrag);
141 
142  /* Look in the current working directory */
143  tmp_path = g_get_current_dir();
144  fullpath = g_build_filename(tmp_path, filefrag, (gchar *)NULL);
145  g_free(tmp_path);
146  fullpath = check_path_return_if_valid(fullpath);
147  if (fullpath != NULL)
148  return fullpath;
149 
150  /* Look in the data dir (e.g. $PREFIX/share/gnucash) */
151  tmp_path = gnc_path_get_pkgdatadir();
152  fullpath = g_build_filename(tmp_path, filefrag, (gchar *)NULL);
153  g_free(tmp_path);
154  fullpath = check_path_return_if_valid(fullpath);
155  if (fullpath != NULL)
156  return fullpath;
157 
158  /* Look in the config dir (e.g. $PREFIX/share/gnucash/accounts) */
159  tmp_path = gnc_path_get_accountsdir();
160  fullpath = g_build_filename(tmp_path, filefrag, (gchar *)NULL);
161  g_free(tmp_path);
162  fullpath = check_path_return_if_valid(fullpath);
163  if (fullpath != NULL)
164  return fullpath;
165 
166  /* Look in the users config dir (e.g. $HOME/.gnucash/data) */
167  fullpath = gnc_build_data_path(filefrag);
168  if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR))
169  return fullpath;
170 
171  /* OK, it's not there. Note that it needs to be created and pass it
172  * back anyway */
173  g_warning("create new file %s", fullpath);
174  return fullpath;
175 
176 }
177 
178 /* Searches for a file fragment paths set via GNC_DOC_PATH environment
179  * variable. If this variable is not set, fall back to search in
180  * - a html directory in the local user's gnucash settings directory
181  * (typically $HOME/.gnucash/html)
182  * - the gnucash documentation directory
183  * (typically /usr/share/doc/gnucash)
184  * - the gnucash data directory
185  * (typically /usr/share/gnucash)
186  * It searches in this order.
187  *
188  * This is used by gnc_path_find_localized_file to search for
189  * localized versions of files if they exist.
190  */
191 static gchar *
192 gnc_path_find_localized_html_file_internal (const gchar * file_name)
193 {
194  gchar *full_path = NULL;
195  int i;
196  const gchar *env_doc_path = g_getenv("GNC_DOC_PATH");
197  const gchar *default_dirs[] =
198  {
199  gnc_build_dotgnucash_path ("html"),
200  gnc_path_get_pkgdocdir (),
201  gnc_path_get_pkgdatadir (),
202  NULL
203  };
204  gchar **dirs;
205 
206  if (!file_name || *file_name == '\0')
207  return NULL;
208 
209  /* Allow search path override via GNC_DOC_PATH environment variable */
210  if (env_doc_path)
211  dirs = g_strsplit (env_doc_path, G_SEARCHPATH_SEPARATOR_S, -1);
212  else
213  dirs = (gchar **)default_dirs;
214 
215  for (i = 0; dirs[i]; i++)
216  {
217  full_path = g_build_filename (dirs[i], file_name, (gchar *)NULL);
218  DEBUG ("Checking for existence of %s", full_path);
219  full_path = check_path_return_if_valid (full_path);
220  if (full_path != NULL)
221  return full_path;
222  }
223 
224  return NULL;
225 }
226 
261 gchar *
262 gnc_path_find_localized_html_file (const gchar *file_name)
263 {
264  gchar *loc_file_name = NULL;
265  gchar *full_path = NULL;
266  const gchar * const *lang;
267  int i;
268 
269  if (!file_name || *file_name == '\0')
270  return NULL;
271 
272  /* An absolute path is returned unmodified. */
273  if (g_path_is_absolute (file_name))
274  return g_strdup (file_name);
275 
276  /* First try to find the file in any of the localized directories
277  * the user has set up on his system
278  */
279  for (lang = g_get_language_names (); *lang; lang++)
280  {
281  loc_file_name = g_build_filename (*lang, file_name, (gchar *)NULL);
282  full_path = gnc_path_find_localized_html_file_internal (loc_file_name);
283  g_free (loc_file_name);
284  if (full_path != NULL)
285  return full_path;
286  }
287 
288  /* If not found in a localized directory, try to find the file
289  * in any of the base directories
290  */
291  return gnc_path_find_localized_html_file_internal (file_name);
292 
293 }
294 
295 /* ====================================================================== */
296 
302 static gboolean
303 gnc_validate_directory (const gchar *dirname, gboolean create, gchar **msg)
304 {
305  struct stat statbuf;
306  gint rc;
307 
308  *msg = NULL;
309  rc = g_stat (dirname, &statbuf);
310  if (rc)
311  {
312  switch (errno)
313  {
314  case ENOENT:
315  if (create)
316  {
317  rc = g_mkdir (dirname,
318 #ifdef G_OS_WIN32
319  0 /* The mode argument is ignored on windows */
320 #else
321  S_IRWXU /* perms = S_IRWXU = 0700 */
322 #endif
323  );
324  if (rc)
325  {
326  *msg = g_strdup_printf(
327  _("An error occurred while creating the directory:\n"
328  " %s\n"
329  "Please correct the problem and restart GnuCash.\n"
330  "The reported error was '%s' (errno %d).\n"),
331  dirname, g_strerror(errno) ? g_strerror(errno) : "", errno);
332  return FALSE;
333  }
334  }
335  else
336  {
337  *msg = g_strdup_printf(
338  _("Note: the directory\n"
339  " %s\n"
340  "doesn't exist. This is however not fatal.\n"),
341  dirname);
342  return FALSE;
343  }
344  g_stat (dirname, &statbuf);
345  break;
346 
347  case EACCES:
348  *msg = g_strdup_printf(
349  _("The directory\n"
350  " %s\n"
351  "exists but cannot be accessed. This program \n"
352  "must have full access (read/write/execute) to \n"
353  "the directory in order to function properly.\n"),
354  dirname);
355  return FALSE;
356 
357  case ENOTDIR:
358  *msg = g_strdup_printf(
359  _("The path\n"
360  " %s\n"
361  "exists but it is not a directory. Please delete\n"
362  "the file and start GnuCash again.\n"),
363  dirname);
364  return FALSE;
365 
366  default:
367  *msg = g_strdup_printf(
368  _("An unknown error occurred when validating that the\n"
369  " %s\n"
370  "directory exists and is usable. Please correct the\n"
371  "problem and restart GnuCash. The reported error \n"
372  "was '%s' (errno %d)."),
373  dirname, g_strerror(errno) ? g_strerror(errno) : "", errno);
374  return FALSE;
375  }
376  }
377 
378  if ((statbuf.st_mode & S_IFDIR) != S_IFDIR)
379  {
380  *msg = g_strdup_printf(
381  _("The path\n"
382  " %s\n"
383  "exists but it is not a directory. Please delete\n"
384  "the file and start GnuCash again.\n"),
385  dirname);
386  return FALSE;
387  }
388 #ifndef G_OS_WIN32
389  /* The mode argument is ignored on windows anyway */
390  if ((statbuf.st_mode & S_IRWXU) != S_IRWXU)
391  {
392  *msg = g_strdup_printf(
393  _("The permissions are wrong on the directory\n"
394  " %s\n"
395  "They must be at least 'rwx' for the user.\n"),
396  dirname);
397  return FALSE;
398  }
399 #endif
400 
401  return TRUE;
402 }
403 
412 const gchar *
414 {
415  static gchar *dotgnucash = NULL;
416  gchar *tmp_dir;
417  gchar *errmsg = NULL;
418 
419  if (dotgnucash)
420  return dotgnucash;
421 
422  dotgnucash = g_strdup(g_getenv("GNC_DOT_DIR"));
423 
424  if (!dotgnucash)
425  {
426  const gchar *home = g_get_home_dir();
427  if (!home || !gnc_validate_directory(home, FALSE, &errmsg))
428  {
429  g_free(errmsg);
430  g_warning("Cannot find suitable home directory. Using tmp directory instead.");
431  home = g_get_tmp_dir();
432  }
433  g_assert(home);
434 
435  dotgnucash = g_build_filename(home, ".gnucash", (gchar *)NULL);
436  }
437  if (!gnc_validate_directory(dotgnucash, TRUE, &errmsg))
438  {
439  const gchar *tmp = g_get_tmp_dir();
440  g_free(errmsg);
441  g_free(dotgnucash);
442  g_warning("Cannot find suitable .gnucash directory in home directory. Using tmp directory instead.");
443  g_assert(tmp);
444 
445  dotgnucash = g_build_filename(tmp, ".gnucash", (gchar *)NULL);
446 
447  if (!gnc_validate_directory(dotgnucash, TRUE, &errmsg))
448  exit(1);
449  }
450 
451  /* Since we're in code that is only executed once.... */
452  tmp_dir = g_build_filename(dotgnucash, "books", (gchar *)NULL);
453  if (!gnc_validate_directory(tmp_dir, TRUE, &errmsg))
454  exit(1);
455  g_free(tmp_dir);
456  tmp_dir = g_build_filename(dotgnucash, "checks", (gchar *)NULL);
457  if (!gnc_validate_directory(tmp_dir, TRUE, &errmsg))
458  exit(1);
459  g_free(tmp_dir);
460  tmp_dir = g_build_filename(tmp_dir, "translog", (gchar *)NULL);
461  if (!gnc_validate_directory(dotgnucash, TRUE, &errmsg))
462  exit(1);
463  g_free(tmp_dir);
464 
465  return dotgnucash;
466 }
467 
476 gchar *
477 gnc_build_dotgnucash_path (const gchar *filename)
478 {
479  return g_build_filename(gnc_dotgnucash_dir(), filename, (gchar *)NULL);
480 }
481 
490 gchar *
491 gnc_build_book_path (const gchar *filename)
492 {
493  gchar* filename_dup = g_strdup(filename);
494  gchar* result = NULL;
495 
496  scrub_filename(filename_dup);
497  result = g_build_filename(gnc_dotgnucash_dir(), "books",
498  filename_dup, (gchar *)NULL);
499  g_free(filename_dup);
500  return result;
501 }
502 
511 gchar *
512 gnc_build_translog_path (const gchar *filename)
513 {
514  gchar* filename_dup = g_strdup(filename);
515  gchar* result = NULL;
516 
517  scrub_filename(filename_dup);
518  result = g_build_filename(gnc_dotgnucash_dir(), "translog",
519  filename_dup, (gchar *)NULL);
520  g_free(filename_dup);
521  return result;
522 }
523 
532 gchar *
533 gnc_build_data_path (const gchar *filename)
534 {
535  gchar* filename_dup = g_strdup(filename);
536  gchar* result;
537 
538  scrub_filename(filename_dup);
539  result = g_build_filename(gnc_dotgnucash_dir(), "data", filename_dup, (gchar *)NULL);
540  g_free(filename_dup);
541  return result;
542 }
543 
552 gchar *
553 gnc_build_report_path (const gchar *filename)
554 {
555  gchar *result = g_build_filename(gnc_path_get_reportdir(), filename, (gchar *)NULL);
556  return result;
557 }
558 
567 gchar *
568 gnc_build_stdreports_path (const gchar *filename)
569 {
570  gchar *result = g_build_filename(gnc_path_get_stdreportsdir(), filename, (gchar *)NULL);
571  return result;
572 }
573 
574 static gchar *
575 gnc_filepath_locate_file (const gchar *default_path, const gchar *name)
576 {
577  gchar *fullname;
578 
579  g_return_val_if_fail (name != NULL, NULL);
580 
581  if (g_path_is_absolute (name))
582  fullname = g_strdup (name);
583  else if (default_path)
584  fullname = g_build_filename (default_path, name, NULL);
585  else
586  fullname = gnc_resolve_file_path (name);
587 
588  if (!g_file_test (fullname, G_FILE_TEST_IS_REGULAR))
589  {
590  g_warning ("Could not locate file %s", name);
591  g_free (fullname);
592  return NULL;
593  }
594 
595  return fullname;
596 }
597 
598 gchar *
599 gnc_filepath_locate_data_file (const gchar *name)
600 {
601  return gnc_filepath_locate_file (gnc_path_get_pkgdatadir(), name);
602 }
603 
604 gchar *
605 gnc_filepath_locate_pixmap (const gchar *name)
606 {
607  gchar *default_path;
608  gchar *fullname;
609 
610  default_path = g_build_filename (gnc_path_get_pkgdatadir (), "pixmaps", NULL);
611  fullname = gnc_filepath_locate_file (default_path, name);
612  g_free(default_path);
613 
614  return fullname;
615 }
616 
617 gchar *
618 gnc_filepath_locate_ui_file (const gchar *name)
619 {
620  gchar *default_path;
621  gchar *fullname;
622 
623  default_path = g_build_filename (gnc_path_get_pkgdatadir (), "ui", NULL);
624  fullname = gnc_filepath_locate_file (default_path, name);
625  g_free(default_path);
626 
627  return fullname;
628 }
629 
630 gchar *
631 gnc_filepath_locate_doc_file (const gchar *name)
632 {
633  return gnc_filepath_locate_file (gnc_path_get_pkgdocdir(), name);
634 }
635 
636 
637 /* =============================== END OF FILE ========================== */
gchar * gnc_filepath_locate_data_file(const gchar *name)
gchar * gnc_build_book_path(const gchar *filename)
Make a path to filename in the book subdirectory of the user's configuration directory.
gchar * gnc_build_data_path(const gchar *filename)
Make a path to filename in the data subdirectory of the user's configuration directory.
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define DEBUG(format, args...)
Definition: qoflog.h:255
gchar * gnc_filepath_locate_ui_file(const gchar *name)
gchar * gnc_build_dotgnucash_path(const gchar *filename)
Make a path to filename in the user's configuration directory.
gchar * gnc_resolve_file_path(const gchar *filefrag)
Create an absolute path when given a relative path; otherwise return the argument.
gchar * gnc_build_stdreports_path(const gchar *filename)
Make a path to filename in the standard reports directory.
gchar * gnc_filepath_locate_doc_file(const gchar *name)
gchar * gnc_build_report_path(const gchar *filename)
Make a path to filename in the report directory.
gchar * gnc_filepath_locate_pixmap(const gchar *name)
gchar * gnc_build_translog_path(const gchar *filename)
Make a path to filename in the translog subdirectory of the user's configuration directory.
gchar * gnc_path_find_localized_html_file(const gchar *file_name)
Find an absolute path to a localized version of a given relative path to a html or html related file...
const gchar * gnc_dotgnucash_dir(void)
Ensure that the user's configuration directory exists and is minimally populated. ...
File path resolution utility functions.
const gchar * QofLogModule
Definition: qofid.h:89