Beispiel #1
0
    def _help(self, args: Args) -> AnyErr:
        cmd = args.get_positional()

        if not cmd:
            ok = command_man("usage")
        else:
            ok = False

            commands = self._commands_for(cmd, resolve_alias=False)

            target = self._resolve_alias(cmd, recursive=False)
            if target:
                print(f"alias {cmd}={target}")
                ok = True
            else:
                log.w(f"Neither a command nor an alias: '{cmd}'")

            if commands:
                if len(commands) == 1:
                    command = commands[0]
                    log.d(f"{cmd} resolve into known {command}")
                    ok = command_man(command)
                elif len(commands) > 1:
                    print("Available commands: ")
                    for comm in isorted(commands):
                        print(red(cmd) + comm[len(cmd):])
                    ok = True

        if not ok:
            print(f"Can't provide help for '{cmd}'")

        return ClientErrors.SUCCESS
Beispiel #2
0
def make_hmd(cmd: Type[CommandHelp]):
    s = f"""\
. =============================================
. Automatically generated - {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}
. =============================================
"""

    def opt_str(opt):
        return ", ".join(f"**{alias}**" for alias in opt.aliases) + \
               " " + \
               (" ".join(f"*{param}*" for param in opt.params) if opt.params else "") + \
               "\n" + \
               "    " + opt.description[0].upper() + opt.description[1:]

    options_content = "\n\n".join(
        opt_str(opt)
        for opt in isorted(cmd.options(), key=lambda opt: opt.aliases_str()))

    s += section("COMMAND", f"{cmd.name()} - {cmd.short_description()}")
    s += section("SYNOPSIS", cmd.synopsis())
    s += section("DESCRIPTION", cmd.long_description())
    s += section("OPTIONS", options_content)
    s += section("EXAMPLES", cmd.examples())
    s += section("SEE ALSO", cmd.see_also())

    return s[:len(s) - 1]
Beispiel #3
0
def ls(path: Path,
       sort_by: Union[str, List[str]] = "name",
       reverse: bool = False,
       hidden: bool = False,
       details: bool = False) -> List[FileInfo]:
    """ Wrapper of Path.iterdir() that provides a list of FileInfo """

    if not path:
        raise TypeError("found invalid path")

    sort_by = list(
        filter(lambda field: field in ["name", "size", "ftype"],
               list_wrap(sort_by)))

    log.i("LS")

    ret: List[FileInfo] = []

    # Single file
    if path.is_file():
        # Show it even if it is hidden
        finfo = create_file_info(path,
                                 fetch_size=details,
                                 fetch_time=details,
                                 fetch_perm=details,
                                 fetch_owner=details)
        if not finfo:
            return []
        return [finfo]

    if not path.is_dir():
        log.e("Cannot perform ls; invalid path")

        raise FileNotFoundError()

    # Directory
    p: Path
    for p in path.iterdir():

        if not hidden and is_hidden(p):
            log.d(f"Not showing hidden file: {p}")
            continue
        finfo = create_file_info(p,
                                 fetch_size=details,
                                 fetch_time=details,
                                 fetch_perm=details,
                                 fetch_owner=details)
        if finfo:
            ret.append(finfo)

    # Sort the result for each field of sort_by
    for sort_field in sort_by:
        ret = isorted(ret, key=lambda fi: fi[sort_field])

    if reverse:
        ret.reverse()

    return ret
Beispiel #4
0
def walk_preorder(path: Path, max_depth: int = None):
    root = path
    log.d(f"walk_preorder over '{root}' - max_depth={max_depth}")

    stack: deque = deque([(root, 0)])
    # stack: List[Path] = [root]

    while stack:
        cursor_path, cursor_depth = stack.popleft()
        # cursor = stack.pop(0)

        try:
            fstat = cursor_path.stat()
        except OSError as oserr:
            log.w(f"Can't stat: {oserr}")
            continue

        try:
            is_file = S_ISREG(fstat.st_mode)
        except OSError:
            is_file = False

        if is_file:
            yield cursor_path, fstat
        else:  # probably is_dir
            if cursor_path != root:
                yield cursor_path, fstat

            # Descend further, if allowed by max depth
            if max_depth is None or cursor_depth < max_depth:
                try:
                    children: List = [
                        (c, cursor_depth + 1)
                        for c in isorted(list(cursor_path.iterdir()))
                    ]
                    stack.extendleft(reversed(children))
                    # stack = children + stack
                except OSError as oserr:
                    log.w(f"Can't descend: {oserr}")
            else:
                log.d(
                    f"cursor_depth({cursor_depth}) > max_depth({max_depth}) - not descending further"
                )
Beispiel #5
0
    def _execute_single_real(self, cmd: str) -> AnyErrs:
        if not is_str(cmd):
            log.e("Invalid command")
            return ClientErrors.INVALID_COMMAND_SYNTAX

        cmd = cmd.strip()

        if len(cmd) == 0:
            log.w("Empty command, nothing to do here")
            return ClientErrors.SUCCESS  # no problem...

        log.d(f"Will try to execute '{cmd}'")
        if cmd.startswith("#"):
            log.d("Ignoring, it's a comment")
            return ClientErrors.SUCCESS

        log.d(f"Before alias resolution: {cmd}")
        # resolved_cmd_prefix, resolved_cmd_suffix = self._resolve_alias(cmd, as_string=False)
        resolved_cmd = self._resolve_alias(cmd, as_string=True)
        # log.d(f"resolved_cmd: {resolved_cmd}")
        # Resolved cmd can contain multiple command after alias resolution
        resolved_cmd_prefix, resolved_cmd_suffix = \
            Shell._split_command_prefix_suffix(resolved_cmd, keep_space=True)
        log.d(f"resolved_cmd_prefix: {resolved_cmd_prefix}")
        log.d(f"resolved_cmd_suffix: {resolved_cmd_suffix}")
        # 'command_prefix' might be partial (unique prefix of a valid command)
        commands = self._commands_for(resolved_cmd_prefix, resolve_alias=False)
        log.d(f"Commands found: {commands}")

        # No command
        if len(commands) == 0:
            if get_setting(Settings.SHELL_PASSTHROUGH):
                log.d(
                    "Passing unknown command to underlying shell due to passthrough"
                )
                return self._client.execute_command(Commands.LOCAL_SHELL, cmd)

            return ClientErrors.COMMAND_NOT_RECOGNIZED

        # More than a command for this prefix
        if len(commands) > 1 and resolved_cmd_prefix not in commands:
            print("Available commands: ")
            for comm in isorted(commands):
                print(
                    red(resolved_cmd_prefix) + comm[len(resolved_cmd_prefix):])
            return ClientErrors.SUCCESS

        if len(commands) == 1:
            # Just 1 command found
            command = commands[0]
        else:
            # More than a command, but one matches exactly
            command = resolved_cmd_prefix

        # Exactly a known command, execute it
        try:
            outcome = ClientErrors.COMMAND_NOT_RECOGNIZED

            if self.has_command(command):
                outcome = self._execute_shell_command(command,
                                                      resolved_cmd_suffix)
            elif self._client.has_command(command):
                outcome = self._client.execute_command(command,
                                                       resolved_cmd_suffix)

            log.d(f"Command outcome: {outcome}")

            return outcome
        except ConnectionError:
            log.eexception("Connection error occurred")
            print_errors(ClientErrors.CONNECTION_ERROR)
            self._client.destroy_connection()
        except EOFError:
            log.i("\nCTRL+D: exiting")
            self._client.destroy_connection()
            # for consistency with CTRL+D typed while reading command, exit
            exit(0)
        except KeyboardInterrupt:
            log.d("\nCTRL+C")