def sys_to_ami_path(self, sys_path): """try to map an absolute system path back to an amiga path if multiple volumes overlap then take the shortest amiga path return ami_path or None if sys_path can't be mapped """ if not os.path.isabs(sys_path): sys_path = resolve_sys_path(sys_path) log_path.debug("vol: sys_to_ami_path: resolved rel path: %s", sys_path) res_len = None result = None for volume in self.volumes: vol_sys_path = volume.get_path() cp = os.path.commonprefix([vol_sys_path, sys_path]) if cp == vol_sys_path: remainder = sys_path[len(vol_sys_path):] n = len(remainder) if n > 0 and remainder[0] == '/': remainder = remainder[1:] n -= 1 # get volume name and build amiga path vol_name = volume.get_name() ami_path = vol_name + ":" + remainder log_path.debug( "vol: sys_to_ami_path: sys='%s' -> ami='%s'", sys_path, ami_path) if result is None or n < res_len: result = ami_path res_len = n # return best result log_path.info( "vol: sys_to_ami_path: sys='%s' -> ami=%s", sys_path, result) return result
def abspath(self, ami_path, env=None): """return an absolute Amiga path If the path is already absolute then return path itself. Otherwise create a new AmiPath object with the absolute path by joining this path with the current directory of the path env. An AmiPathError is raised if the relative path cannot be joined to the current directory, e.g.: "foo:".join("/") """ if type(ami_path) is str: ami_path = AmiPath(ami_path) # already absolute? if ami_path.is_absolute(): log_path.debug("abspath: is_absolute: '%s'", ami_path) return ami_path # get current directory and join cur dir with my path if env is None: env = self.default_env cwd = env.get_cwd() else: # make sure its a volume path cwd = env.get_cwd() res_path = cwd.join(ami_path) log_path.debug("abspath: relpath='%s' cwd='%s' -> join '%s'", ami_path, cwd, res_path) return res_path
def shutdown(self): # shutdown all volumes log_path.debug("shutting down volumes") for volume in self.volumes: log_path.info("cleaning up volume: %s", volume) volume.shutdown() volume.is_setup = False
def create_rel_sys_path(self, rel_path): if type(rel_path) in (list, tuple): rel_path = os.path.join(*rel_path) dir_path = os.path.join(self.path, rel_path) if os.path.isdir(dir_path): log_path.debug( "rel sys path in volume already exists '%s' + %s -> %s", self.name, rel_path, dir_path, ) return dir_path try: log_path.debug( "creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path, ) os.makedirs(dir_path) return dir_path except OSError as e: log_path.error( "error creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path, ) return None
def sys_to_ami_path(self, sys_path): """try to map an absolute system path back to an amiga path if multiple volumes overlap then take the shortest amiga path return ami_path or None if sys_path can't be mapped """ if not os.path.isabs(sys_path): log_path.error("vol: sys_to_ami_path: no abs path: '%s'", sys_path) return None res_len = None result = None for vol_sys_path in self.sys2volume: cp = os.path.commonprefix([vol_sys_path, sys_path]) if cp == vol_sys_path: remainder = sys_path[len(vol_sys_path):] n = len(remainder) if n > 0 and remainder[0] == '/': remainder = remainder[1:] n -= 1 # get volume name and build amiga path vol_name = self.sys2volume[vol_sys_path] vol_name = self.orig_names[vol_name] ami_path = vol_name + ":" + remainder log_path.debug("vol: sys_to_ami_path: sys='%s' -> ami='%s'", sys_path, ami_path) if result is None or n < res_len: result = ami_path res_len = n # return best result log_path.info("vol: sys_to_ami_path: sys='%s' -> ami=%s", sys_path, result) return result
def setup(self): log_path.debug("setting up assigns") for a in self.assigns: if not a.setup(self): log_path.error("error setting up assign: %s", a) return False a.is_setup = True self.is_setup = True return True
def setup(self): # setup all defined volumes log_path.debug("setting up volumes") for volume in self.volumes: if not volume.setup(): log_path.error("Error settign up volume: %s", volume) return False volume.is_setup = True self.is_setup = True return True
def _create_dir(self, volume, rel_dir): log_path.debug("%s: create assign dir: rel_dir='%s'", self.name, rel_dir) path = volume.create_rel_sys_path(rel_dir) if not path: log_path.error("assign '%s': can't create relative dir: %s", self.name, rel_dir) return False else: log_path.debug("created sys path: %s", path) return True
def resolve_assigns(self, ami_path, recursive=True): """replace all assigns found in path until only a volume path exists. do not touch relative paths or abs paths without assign prefix. return: original path if path is not absolute or does not contain assign prefix or: string if no multi assigns are involved or: list of string if multi assigns were encountered """ log_path.info("resolve_assign: ami_path='%s'", ami_path) split = self._split_volume_remainder(ami_path) if split is None: # relative path log_path.debug("resolve_assign: ami_path='%s' is rel_path!", ami_path) return ami_path else: # is assign name = split[0].lower() if self.assigns.has_key(name): remainder = split[1] aname_list = self.assigns[name] # single assign if len(aname_list) == 1: aname = aname_list[0] new_path = self._concat_assign(aname, remainder) log_path.info( "resolve_assign: ami_path='%s' -> single assign: '%s'", ami_path, new_path) if recursive: return self.resolve_assigns(new_path) else: return new_path # multi assign else: result = [] for aname in aname_list: new_path = self._concat_assign(aname, remainder) log_path.info( "resolve_assign: ami_path='%s' -> multi assign: '%s'", ami_path, new_path) if recursive: new_path = self.resolve_assigns(new_path) if new_path is None: return None if type(new_path) is str: result.append(new_path) else: result += new_path return result # prefix is not an assign else: log_path.debug("resolve_assign: ami_path='%s' has no assign!", ami_path) return ami_path
def _create_dir(self, volume, rel_dir): log_path.debug("%s: create assign dir: rel_dir='%s'", self.name, rel_dir) path = volume.create_rel_sys_path(rel_dir) if not path: log_path.error( "assign '%s': can't create relative dir: %s", self.name, rel_dir ) return False else: log_path.debug("created sys path: %s", path) return True
def _create_temp(self, path): if os.path.exists(path): log_path.error("temp volume volume path already exists: '%s'", path) return False # create temp dir try: log_path.debug("creating temp dir: %s", path) os.makedirs(path) return True except OSError: log_path.error("error creating temp dir: '%s'", path) return False
def ami_to_sys_path(self, ami_path, fast=False): """Map an Amiga path to a system path. An absolute Amiga path with volume prefix is expected. Any other path returns None. If volume does not exist also return None. It replaces the volume with the sys_path prefix. Furthermore, the remaining Amiga path is mapped to the system file system and case corrected if a corresponding entry is found. If 'fast' mode is enabled then the original case of the path elements is kept if the underlying FS is case insensitive. Return None on error or system path """ # find volume pos = ami_path.find(':') if pos <= 0: log_path.debug("vol: ami_to_sys_path: empty volume: %s", ami_path) return None vol_name = ami_path[:pos].lower() # check volume name if vol_name in self.vols_by_name: volume = self.vols_by_name[vol_name] vol_sys_path = volume.get_path() remainder = ami_path[pos + 1:] # only volume name given if len(remainder) == 0: log_path.info("vol: direct volume: ami='%s' -> sys='%s'", ami_path, vol_sys_path) return vol_sys_path # invalid volume:/... path if remainder[0] == '/': log_path.error("vol: ami_to_sys_path: invalid :/ path: %s", ami_path) return None # follow ami path along in sys world dirs = remainder.split('/') sys_path = self._follow_path_no_case(vol_sys_path, dirs, fast) log_path.info("vol: ami_to_sys_path: ami='%s' -> sys='%s'", ami_path, sys_path) return sys_path else: log_path.error("vol: ami_to_sys_path: volume='%s' not found: %s", vol_name, ami_path) return None
def ami_to_sys_path(self, ami_path, fast=False): """Map an Amiga path to a system path. An absolute Amiga path with volume prefix is expected. Any other path returns None. If volume does not exist also return None. It replaces the volume with the sys_path prefix. Furthermore, the remaining Amiga path is mapped to the system file system and case corrected if a corresponding entry is found. If 'fast' mode is enabled then the original case of the path elements is kept if the underlying FS is case insensitive. Return None on error or system path """ # find volume pos = ami_path.find(':') if pos <= 0: log_path.debug("vol: ami_to_sys_path: empty volume: %s", ami_path) return None vol_name = ami_path[:pos].lower() # check volume name if vol_name in self.vols_by_name: volume = self.vols_by_name[vol_name] vol_sys_path = volume.get_path() remainder = ami_path[pos+1:] # only volume name given if len(remainder) == 0: log_path.info("vol: direct volume: ami='%s' -> sys='%s'", ami_path, vol_sys_path) return vol_sys_path # invalid volume:/... path if remainder[0] == '/': log_path.error("vol: ami_to_sys_path: invalid :/ path: %s", ami_path) return None # follow ami path along in sys world dirs = remainder.split('/') sys_path = self._follow_path_no_case(vol_sys_path, dirs, fast) log_path.info("vol: ami_to_sys_path: ami='%s' -> sys='%s'", ami_path, sys_path) return sys_path else: log_path.error("vol: ami_to_sys_path: volume='%s' not found: %s", vol_name, ami_path) return None
def _setup_base_dir(self): # ensure that vols_base_dir exists base_dir = self.vols_base_dir if not base_dir: raise ValueError("volume manager: no base dir given!") base_dir = resolve_sys_path(base_dir) if not os.path.isdir(base_dir): try: log_path.info("creating volume base dir: %s", base_dir) os.makedirs(base_dir) except OSError as e: log_path.error("error creating volume base dir: %s -> %s", base_dir, e) return None else: log_path.debug("found base dir: %s", base_dir) return base_dir
def create_rel_sys_path(self, rel_path): if type(rel_path) in (list, tuple): rel_path = os.path.join(*rel_path) dir_path = os.path.join(self.path, rel_path) if os.path.isdir(dir_path): log_path.debug("rel sys path in volume already exists '%s' + %s -> %s", self.name, rel_path, dir_path) return dir_path try: log_path.debug("creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path) os.makedirs(dir_path) return dir_path except OSError as e: log_path.error("error creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path) return None
def parse_config(self, cfg): if cfg is None: return True path = cfg.path if path is None: return True if path.cwd: self.cwd.set(path.cwd) if path.command: self.cmd_paths.set(path.command) # enforce validation try: log_path.debug("env: parse_config: %s", cfg) self.cwd.resolve() self.cmd_paths.resolve() log_path.debug("env: done: %s", self) except AmiPathError as e: log_path.error("env: parse config error: %s", e) return False return True
def _parse_spec(self, spec): # auto parse string spec if type(spec) is str: try: spec = Spec.parse(spec) except ValueError as e: log_path.error("error parsing spec: %r -> %s", spec, e) return None # check spec name = spec.get_name() src_list = spec.get_src_list() cfg = spec.get_cfg() n = len(src_list) if n == 0: # local path path = self._get_local_vol_path(name) log_path.debug("local path='%s'", path) if path is None: return None elif n == 1: path = resolve_sys_path(src_list[0]) log_path.debug("resolved path='%s'", path) if n > 1: log_path.error("only one source in volume spec allowed!") return None log_path.debug("name='%s', path='%s'", name, path) # create volume return Volume(name, path, cfg)
def shutdown(self): log_path.debug("shutting down assigns") for a in self.assigns: log_path.info("cleaning up assign: %s", a) a.shutdown() a.is_setup = False
def resolve_assigns(self, ami_path, recursive=True, as_list=False): """replace all assigns found in path until only a volume path exists. do not touch relative paths or abs paths without assign prefix. return: original path if path is not absolute or does not contain assign prefix or: string if no multi assigns are involved or: list of string if multi assigns were encountered """ log_path.info("resolve_assign: ami_path='%s'", ami_path) split = self._split_volume_remainder(ami_path) if split is None: # relative path log_path.debug("resolve_assign: ami_path='%s' is rel_path!", ami_path) if as_list: return [ami_path] else: return ami_path else: # is assign name = split[0].lower() if self.assigns_by_name.has_key(name): remainder = split[1] assign = self.assigns_by_name[name] aname_list = assign.get_assigns() # single assign if len(aname_list) == 1: aname = aname_list[0] new_path = self._concat_assign(aname, remainder) log_path.info("resolve_assign: ami_path='%s' -> single assign: '%s'", ami_path, new_path) if recursive: return self.resolve_assigns(new_path, recursive, as_list) elif as_list: return [new_path] else: return new_path # multi assign else: result = [] for aname in aname_list: new_path = self._concat_assign(aname, remainder) log_path.info( "resolve_assign: ami_path='%s' -> multi assign: '%s'", ami_path, new_path) if recursive: new_path = self.resolve_assigns(new_path, recursive, as_list) if new_path is None: return None if type(new_path) is str: result.append(new_path) else: result += new_path return result # prefix is not an assign else: log_path.debug("resolve_assign: ami_path='%s' has no assign!", ami_path) if as_list: return [ami_path] else: return ami_path
def _delete_temp(self, path): try: log_path.debug("removing temp dir: %s", path) shutil.rmtree(path) except: log_path.error("error removing temp dir: '%s'", path)