# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009 Gary Burton # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # """ File and folder related utility functions """ #------------------------------------------------------------------------- # # Python modules # #------------------------------------------------------------------------- import os import sys import shutil import hashlib import logging LOG = logging.getLogger(".gen.utils.file") #------------------------------------------------------------------------- # # Gramps modules # #------------------------------------------------------------------------- from ..constfunc import win, mac, get_env_var from ..const import TEMP_DIR, USER_HOME, ENV, GRAMPS_LOCALE as glocale #------------------------------------------------------------------------- # # Constants # #------------------------------------------------------------------------- _NEW_NAME_PATTERN = '%s%sUntitled_%d.%s' #------------------------------------------------------------------------- # # Functions # #------------------------------------------------------------------------- def find_file( filename): # try the filename we got try: if os.path.isfile(filename): return(filename) except UnicodeError: LOG.error("Filename %s raised a Unicode Error %s.", repr(filename), err) LOG.debug("Filename %s not found.", repr(filename)) return '' def find_folder( filename): # try the filename we got try: if os.path.isdir(filename): return(filename) except UnicodeError as err: LOG.error("Filename %s raised a Unicode Error %s", repr(filename), err) LOG.debug("Filename %s either not found or not a directory.", repr(filename)) return '' def get_new_filename(ext, folder='~/'): ix = 1 while os.path.isfile(os.path.expanduser(_NEW_NAME_PATTERN % (folder, os.path.sep, ix, ext))): ix = ix + 1 return os.path.expanduser(_NEW_NAME_PATTERN % (folder, os.path.sep, ix, ext)) def get_empty_tempdir(dirname): """ Return path to TEMP_DIR/dirname, a guaranteed empty directory makes intervening directories if required fails if _file_ by that name already exists, or for inadequate permissions to delete dir/files or create dir(s) """ dirpath = os.path.join(TEMP_DIR, str(dirname)) if os.path.isdir(dirpath): shutil.rmtree(dirpath) os.makedirs(dirpath) return dirpath def rm_tempdir(path): """Remove a tempdir created with get_empty_tempdir""" if path.startswith(TEMP_DIR) and os.path.isdir(path): shutil.rmtree(path) def relative_path(original, base): """ Calculate the relative path from base to original, with base a directory, and original an absolute path On problems, original is returned unchanged """ if not os.path.isdir(base): return original #original and base must be absolute paths if not os.path.isabs(base): return original if not os.path.isabs(original): return original original = os.path.normpath(original) base = os.path.normpath(base) # If the db_dir and obj_dir are on different drives (win only) # then there cannot be a relative path. Return original obj_path (base_drive, base) = os.path.splitdrive(base) (orig_drive, orig_name) = os.path.splitdrive(original) if base_drive.upper() != orig_drive.upper(): return original # Starting from the filepath root, work out how much of the filepath is # shared by base and target. base_list = (base).split(os.sep) target_list = (orig_name).split(os.sep) # make sure '/home/person' and 'c:/home/person' both give # list ['home', 'person'] base_list = [_f for _f in base_list if _f] target_list = [_f for _f in target_list if _f] i = -1 # base path is normcase (lower case on Windows) so compare target in lower # on Windows as well for i in range(min(len(base_list), len(target_list))): if base_list[i] != (target_list[i].lower() if win() else target_list[i]): break else: #if break did not happen we are here at end, and add 1. i += 1 rel_list = [os.pardir] * (len(base_list) - i) + target_list[i:] return os.path.join(*rel_list) def expand_path(path, normalize = True): """ Expand environment variables in a path Uses both the environment variables and the Gramps environment The expansion uses the str.format, e.g. "~/{GRAMPSHOME}/{VERSION}/filename.txt" We make the assumption that the user will not use a path that contain variable names (it is technically possible to use characters "{", "}" in paths) """ environment = dict(os.environ) environment.update(ENV) if not 'GRAMPSHOME' in environment: environment['GRAMPSHOME'] = USER_HOME path = path.format(**environment) if normalize: path = os.path.normcase(os.path.normpath(os.path.abspath(path))) return path def media_path(db): """ Given a database, return the mediapath to use as basedir for media """ mpath = db.get_mediapath() return expand_media_path(mpath, db) def expand_media_path(mpath, db): """ Normalize a mediapath: - Relative mediapath are considered as relative to the database - Expand variables, see expand_path - Convert to absolute path - Convert slashes and case (on Windows) """ # Use home dir if no media_path specified if mpath is None: mpath = os.path.abspath(USER_HOME) # Expand environment variables mpath = expand_path(mpath, False) # Relative mediapath are considered as relative to the database if not os.path.isabs(mpath): basepath = db.get_save_path() if not basepath: basepath = USER_HOME mpath = os.path.join(os.path.abspath(basepath), mpath) # Normalize path mpath = os.path.normcase(os.path.normpath(os.path.abspath(mpath))) return mpath def media_path_full(db, filename): """ Given a database and a filename of a media, return the media filename is full form, eg 'graves/tomb.png' becomes '/home/me/genea/graves/tomb.png """ if os.path.isabs(filename): return filename mpath = media_path(db) return os.path.join(mpath, filename) def search_for(name): if name.startswith( '"' ): name = name.split('"')[1] else: name = name.split()[0] if win(): for i in get_env_var('PATH').split(';'): fname = os.path.join(i, name) if os.access(fname, os.X_OK) and not os.path.isdir(fname): return 1 if os.access(name, os.X_OK) and not os.path.isdir(name): return 1 else: for i in os.environ['PATH'].split(':'): #not win() fname = os.path.join(i, name) if os.access(fname, os.X_OK) and not os.path.isdir(fname): return 1 return 0 def create_checksum(full_path): """ Create a md5 hash for the given file. """ full_path = os.path.normpath(full_path) try: with open(full_path, 'rb') as media_file: md5sum = hashlib.md5(media_file.read()).hexdigest() except IOError: md5sum = '' except UnicodeEncodeError: md5sum = '' return md5sum