Example #1
0
def refresh():
    """Write the shell declare variable statements to stdout."""
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if len(args) >= 2:
        abort(USAGE + 'dtags: too many arguments')
    if len(args) == 1:
        shell_name = args[0]
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_name))
    else:
        shell_path = os.environ.get('SHELL')
        if shell_path is None:
            abort('dtags: undefined environment variable: ' +
                  style.bad('SHELL'))
        shell_name = None
        for supported_shell_name in SUPPORTED_SHELLS:
            if supported_shell_name in shell_path:
                shell_name = supported_shell_name
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_path))

    assign_string = 'set -g {} "{}"' if shell_name == 'fish' else '{}="{}"'
    mapping, _ = load_mapping()
    print('\n'.join(
        assign_string.format(tag.replace('-', '_'), paths.pop())
        for tag, paths in mapping.items()
        if len(paths) == 1 and
        tag.replace('-', '_') not in os.environ and
        not get_invalid_tag_chars(tag)
    ))
Example #2
0
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head.startswith('-'):
        abort(USAGE + 'u: invalid argument: ' + style.bad(head))
    path = expand(head)
    tags_removed = set()
    for tag in tail if tail else mapping.keys():
        if path in mapping[tag]:
            mapping[tag].remove(path)
            tags_removed.add(tag)
    if not tags_removed:
        finish('Nothing to do')
    save_mapping(mapping)
    if excluded:
        print('Cleaned the following invalid entries:\n' + excluded + '\n')
    finish(
        style.path(path) + ' ' + ' '.join(
            style.sign('-') + style.tag(tag) for tag in sorted(tags_removed)))
Example #3
0
File: u.py Project: zhuhon/dtags
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head.startswith('-'):
        abort(USAGE + 'u: invalid argument: ' + style.bad(head))
    path = expand(head)
    tags_removed = set()
    for tag in tail if tail else mapping.keys():
        if path in mapping[tag]:
            mapping[tag].remove(path)
            tags_removed.add(tag)
    if not tags_removed:
        finish('Nothing to do')
    save_mapping(mapping)
    if excluded:
        print('Cleaned the following invalid entries:\n' + excluded + '\n')
    finish(style.path(path) + ' ' + ' '.join(
        style.sign('-') + style.tag(tag) for tag in sorted(tags_removed)
    ))
Example #4
0
def _edit(args):
    """Edit the mapping directly using an editor."""
    if args:
        abort(USAGE + 'dtags: too many arguments')
    try:
        with TempFile(
            mode='w+t', delete=False, prefix='mapping.', dir=CFG_DIR
        ) as tfile:
            with io.open(MAPPING_FILE, 'rt') as mapping_file:
                tfile.write(EDIT_HELP_COMMENTS + mapping_file.read())
            tfile.flush()
    except (OSError, IOError) as err:
        abort('dtags: failed to edit mapping: {}'.format(err), err.errno)
    else:
        editor = shlex.split(os.environ.get('EDITOR'))
        if not editor:
            abort('dtags: undefined environment variable: ' +
                  style.bad('EDITOR'))
        try:
            sp.check_call(editor + [tfile.name])
        except sp.CalledProcessError as err:
            abort('dtags: failed to edit mapping: {}'.format(err.message))
        else:
            mapping, excluded = parse_mapping(tfile.name)
            save_mapping(mapping)
            rm_files(tfile.name)
            if excluded:
                print('Cleaned the following entries:\n' + excluded + '\n')
            finish('New entries saved successfully')
Example #5
0
File: t.py Project: joowani/dtags
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == "--help":
        finish(USAGE + DESCRIPTION)
    elif head == "--version":
        finish("Version " + VERSION)
    path = expand(head)
    invalid_path_chars = get_invalid_path_chars(path)
    if invalid_path_chars:
        abort(
            "t: directory path {} contains bad characters {}".format(
                style.bad(path), style.bad_chars(invalid_path_chars)
            )
        )
    if not os.path.isdir(path):
        abort("t: invalid directory: " + style.bad(path))
    mapping, excluded = load_mapping()
    tags_added = set()
    if not tail:
        tail.append(os.path.basename(path))
    for tag in tail:
        if not tag[0].isalpha():
            abort("t: tag name {} does not start with an alphabet".format(style.bad(tag)))
        if " " in tag:
            abort("t: tag name {} contains whitespaces".format(style.bad(tag)))
        invalid_tag_chars = get_invalid_tag_chars(tag)
        if invalid_tag_chars:
            abort(
                "t: tag name {} contains bad characters {}".format(style.bad(tag), style.bad_chars(invalid_tag_chars))
            )
        if path not in mapping[tag]:
            mapping[tag].add(path)
            tags_added.add(tag)
    if tags_added or excluded:
        save_mapping(mapping)
    if excluded:
        print("Cleaned the following invalid entries:\n" + excluded + "\n")
    if not tags_added:
        finish("Nothing to do")
    else:
        finish(style.path(path) + " " + " ".join(style.sign("+") + style.tag(tag) for tag in sorted(tags_added)))
Example #6
0
File: t.py Project: tnt-wolve/dtags
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    path = expand(head)
    invalid_path_chars = get_invalid_path_chars(path)
    if invalid_path_chars:
        abort('t: directory path {} contains bad characters {}'.format(
            style.bad(path), style.bad_chars(invalid_path_chars)))
    if not os.path.isdir(path):
        abort('t: invalid directory: ' + style.bad(path))
    mapping, excluded = load_mapping()
    tags_added = set()
    if not tail:
        tail.append(os.path.basename(path))
    for tag in tail:
        if not tag[0].isalpha():
            abort('t: tag name {} does not start with an alphabet'.format(
                style.bad(tag)))
        if ' ' in tag:
            abort('t: tag name {} contains whitespaces'.format(style.bad(tag)))
        invalid_tag_chars = get_invalid_tag_chars(tag)
        if invalid_tag_chars:
            abort('t: tag name {} contains bad characters {}'.format(
                style.bad(tag), style.bad_chars(invalid_tag_chars)))
        if path not in mapping[tag]:
            mapping[tag].add(path)
            tags_added.add(tag)
    if tags_added or excluded:
        save_mapping(mapping)
    if excluded:
        print('Cleaned the following invalid entries:\n' + excluded + '\n')
    if not tags_added:
        finish('Nothing to do')
    else:
        finish(
            style.path(path) + ' ' + ' '.join(
                style.sign('+') + style.tag(tag)
                for tag in sorted(tags_added)))
Example #7
0
def activate():
    """Write the shell runtime configuration to stdout."""
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if len(args) >= 2:
        abort(USAGE + 'dtags: too many arguments')
    if len(args) == 1:
        shell_name = args[0]
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_name))
    else:
        shell_path = os.environ.get('SHELL')
        if shell_path is None:
            abort('dtags: undefined environment variable: ' +
                  style.bad('SHELL'))
        shell_name = None
        for supported_shell_name in SUPPORTED_SHELLS:
            if supported_shell_name in shell_path:
                shell_name = supported_shell_name
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_path))
    finish(
        config.format(
            mapping_file=MAPPING_FILE,
            tags_file=TAGS_FILE,
            version=VERSION,
            usage=d.USAGE,
            description=d.DESCRIPTION,
            arg_err_tty=d.ARG_ERR_TTY,
            dest_err_tty=d.DEST_ERR_TTY,
            input_err_tty=d.INPUT_ERR_TTY,
            index_err_tty=d.INDEX_ERR_TTY,
            prompt_tty=d.PROMPT_TTY,
            choice_tty=d.CHOICE_TTY,
            arg_err=d.ARG_ERR,
            dest_err=d.DEST_ERR,
            input_err=d.INPUT_ERR,
            index_err=d.INDEX_ERR,
            prompt=d.PROMPT,
            choice=d.CHOICE,
        ))
Example #8
0
def activate():
    """Write the shell runtime configuration to stdout."""
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if len(args) >= 2:
        abort(USAGE + 'dtags: too many arguments')
    if len(args) == 1:
        shell_name = args[0]
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_name))
    else:
        shell_path = os.environ.get('SHELL')
        if shell_path is None:
            abort('dtags: undefined environment variable: ' +
                  style.bad('SHELL'))
        shell_name = None
        for supported_shell_name in SUPPORTED_SHELLS:
            if supported_shell_name in shell_path:
                shell_name = supported_shell_name
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('dtags: unsupported shell: ' + style.bad(shell_path))
    finish(config.format(
        mapping_file=MAPPING_FILE,
        tags_file=TAGS_FILE,
        version=VERSION,
        usage=d.USAGE,
        description=d.DESCRIPTION,
        arg_err_tty=d.ARG_ERR_TTY,
        dest_err_tty=d.DEST_ERR_TTY,
        input_err_tty=d.INPUT_ERR_TTY,
        index_err_tty=d.INDEX_ERR_TTY,
        prompt_tty=d.PROMPT_TTY,
        choice_tty=d.CHOICE_TTY,
        arg_err=d.ARG_ERR,
        dest_err=d.DEST_ERR,
        input_err=d.INPUT_ERR,
        index_err=d.INDEX_ERR,
        prompt=d.PROMPT,
        choice=d.CHOICE,
    ))
Example #9
0
def _list(targets=None, reverse=False):
    """List directories and tags.

    :param targets: the list of directories or tags to highlight
    :param reverse: whether to display the reverse mapping or not
    """
    if targets is None:
        targets = set()
    else:
        targets = set(targets)
        additional_targets = set()
        for target in targets:
            if target.startswith('-'):
                abort(USAGE + 'dtags: invalid argument: ' + style.bad(target))
            else:
                additional_targets.add(expand(target))
        targets.update(additional_targets)
    mapping, excluded = load_mapping()
    if excluded:
        print(
            'Found invalid entries in the mapping:\n' + excluded +
            '\nRun ' + style.cmd('dtags clean') + ' to remove them' + '\n'
        )
    if not mapping:
        finish('Nothing to list')
    msgs = []
    if reverse:
        if targets:
            mapping = {
                tag: paths for tag, paths in mapping.items()
                if tag in targets or paths & targets
            }
        for tag in sorted(mapping):
            lines = [style.tag(tag, tag in targets)]
            for path in sorted(mapping[tag]):
                lines.append(style.path(path, path in targets))
            msgs.append('\n'.join(lines))
        finish('\n\n'.join(msgs) if msgs else 'Nothing to list')
    else:
        rmapping = defaultdict(set)
        for tag, paths in mapping.items():
            for path in paths:
                rmapping[path].add(tag)
        if targets:
            rmapping = {
                path: tags for path, tags in rmapping.items()
                if path in targets or tags & targets
            }
        for path in sorted(rmapping):
            tags = ' '.join(
                style.tag(tag, tag in targets)
                for tag in sorted(rmapping[path])
            )
            msgs.append(style.path(path, path in targets) + ' ' + tags)
        finish('\n'.join(msgs) if msgs else 'Nothing to list')
Example #10
0
def _shell(args):
    """Write the shell runtime configuration to stdout."""
    if len(args) >= 2:
        abort(USAGE + 'Too many arguments')
    if len(args) == 1:
        shell_name = args[0]
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('Unsupported shell: ' + style.bad(shell_name))
    else:
        shell_path = os.environ.get('SHELL')
        if shell_path is None:
            abort('Undefined environment variable: ' + style.bad('SHELL'))
        shell_name = None
        for supported_shell_name in SUPPORTED_SHELLS:
            if supported_shell_name in shell_path:
                shell_name = supported_shell_name
        config = SUPPORTED_SHELLS.get(shell_name)
        if config is None:
            abort('Unsupported shell: ' + style.bad(shell_path))
    finish(config.format(
        mapping_file=MAPPING_FILE,
        tags_file=TAGS_FILE,
        version=VERSION,
        usage=directory.USAGE,
        description=directory.DESCRIPTION,
        goto_msg_tty=directory.GOTO_MSG_TTY,
        arg_err_tty=directory.ARG_ERR_TTY,
        dest_err_tty=directory.DEST_ERR_TTY,
        input_err_tty=directory.INPUT_ERR_TTY,
        index_err_tty=directory.INDEX_ERR_TTY,
        prompt_tty=directory.PROMPT_TTY,
        choice_tty=directory.CHOICE_TTY,
        goto_msg=directory.GOTO_MSG,
        arg_err=directory.ARG_ERR,
        dest_err=directory.DEST_ERR,
        input_err=directory.INPUT_ERR,
        index_err=directory.INDEX_ERR,
        prompt=directory.PROMPT,
        choice=directory.CHOICE,
    ))
Example #11
0
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head.startswith('-'):
        abort(USAGE + 'Invalid argument: ' + style.bad(head))
    path = expand(head)
    invalid_path_chars = get_invalid_path_chars(path)
    if invalid_path_chars:
        abort('Directory path {} contains bad characters {}'.format(
            style.bad(path), style.bad_chars(invalid_path_chars)
        ))
    if not os.path.isdir(path):
        abort('Invalid directory ' + style.bad(path))
    mapping, excluded = load_mapping()
    tags_added = set()
    if not tail:
        tail.append(os.path.basename(path))
    for tag in tail:
        if not tag[0].isalpha():
            abort('Tag name {} does not start with an alphabet'
                  .format(style.bad(tag)))
        if ' ' in tag:
            abort('Tag name {} contains whitespaces'.format(style.bad(tag)))
        invalid_tag_chars = get_invalid_tag_chars(tag)
        if invalid_tag_chars:
            abort('Tag name {} contains bad characters {}'.format(
                style.bad(tag), style.bad_chars(invalid_tag_chars)
            ))
        if path not in mapping[tag]:
            mapping[tag].add(path)
            tags_added.add(tag)
    if tags_added or excluded:
        save_mapping(mapping)
    if excluded:
        print('Cleaned the following invalid entries:\n' + excluded + '\n')
    if not tags_added:
        finish('Nothing to do')
    else:
        finish(style.path(path) + ' ' + ' '.join(
            style.sign('+') + style.tag(tag) for tag in sorted(tags_added)
        ))
Example #12
0
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if not args:
        _list()
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == 'edit':
        _edit(tail)
    elif head == 'clean':
        _clean(tail)
    elif head == 'list':
        _list(tail, reverse=False)
    elif head == 'reverse':
        _list(tail, reverse=True)
    elif head == 'shell':
        _shell(tail)
    else:
        abort(USAGE + 'Invalid argument: ' + style.bad(head))
Example #13
0
def manage():
    """Manage directory tags."""
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    args = sys.argv[1:]
    if not args:
        _list()
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == 'edit':
        _edit(tail)
    elif head == 'clean':
        _clean(tail)
    elif head == 'list':
        _list(tail, reverse=False)
    elif head == 'reverse':
        _list(tail, reverse=True)
    elif head == 'commands':
        _commands(tail)
    else:
        abort(USAGE + 'dtags: invalid argument: ' + style.bad(head))
Example #14
0
File: p.py Project: joowani/dtags
def main():
    global temp_file, process, processes

    atexit.register(_cleanup_resources)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    if excluded:
        print(
            'Found invalid entries in the mapping:\n' +
            excluded + '\nRun ' + style.cmd('dtags clean') +
            ' to remove them' + '\n'
        )
    # Parse the optional arguments
    interactive = False
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == '-i' and not tail:
        abort(USAGE + 'p: missing argument: ' + style.bad('<targets>'))
    elif head == '-i' and tail:
        interactive = True
        head, tail = tail[0], tail[1:]
    elif head.startswith('-'):
        abort(USAGE + 'p: invalid argument: ' + style.bad(head))

    # Parse the positional arguments
    if not tail:
        abort(USAGE + 'p: missing argument: ' + style.bad('<command>'))
    directories = collections.defaultdict(set)
    for target in head.split(','):
        if not target:
            continue
        elif target in mapping:
            for directory in sorted(mapping[target]):
                directories[directory].add(target)
        else:
            path = expand(target)
            if os.path.isdir(path):
                directories[path].add(None)
            else:
                abort(USAGE + 'p: invalid target: ' + style.bad(target))

    # Check which shell is in use (e.g. zsh, bash, fish)
    shell = os.environ.get('SHELL')
    if shell is None:
        abort('p: undefined environment variable: ' + style.bad('SHELL'))

    if interactive:
        cmd = [shell, '-i', '-c', sp.list2cmdline(tail)]
    else:
        cmd = [shell, '-c', sp.list2cmdline(tail)]

    # Add signal handlers to terminate child processes gracefully
    signal.signal(signal.SIGINT, _handle_signal)
    signal.signal(signal.SIGABRT, _handle_signal)
    signal.signal(signal.SIGTERM, _handle_signal)

    # Execute in parallel and pipe the output to temporary files
    for directory in sorted(directories):
        tags = directories[directory]
        temp_file = tempfile.TemporaryFile(mode='w+t')
        process = sp.Popen(
            cmd,
            cwd=directory,
            stdout=temp_file,
            stderr=temp_file,
            preexec_fn=os.setsid
        )
        processes.append((directory, tags, process, temp_file))

    # Read from the temporary files back to stdout line by line
    sys.stdout.write('\n')
    for directory, tags, process, temp_file in processes:
        status = process.wait()
        tags = [style.tag(tag) for tag in tags if tag]
        sys.stdout.write('{} {}{}\n'.format(
            style.msg('in'), style.path(directory),
            ((' ' + ' '.join(tags)) if tags else '') + style.msg(':')
        ))
        temp_file.seek(0)
        for line in temp_file:
            sys.stdout.write(line)
        temp_file.close()
        if status != 0:
            sys.stdout.write(style.bad('Exit status: {}\n\n'.format(status)))
        else:
            sys.stdout.write('\n\n')
Example #15
0
File: e.py Project: zhuhon/dtags
def main():
    atexit.register(_cleanup_resources)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    if excluded:
        print(
            'Found invalid entries in the mapping:\n' +
            excluded + '\nRun ' + style.cmd('dtags clean') +
            ' to remove them' + '\n'
        )
    # Parse the optional arguments
    parallel = False
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == '-p' and not tail:
        abort(USAGE + 'e: missing argument: ' + style.bad('<targets>'))
    elif head == '-p' and tail:
        parallel = True
        head, tail = tail[0], tail[1:]
    elif head.startswith('-'):
        abort(USAGE + 'e: invalid argument: ' + style.bad(head))

    # Parse the positional arguments
    if not tail:
        abort(USAGE + 'e: missing argument: ' + style.bad('<command>'))
    directories = collections.defaultdict(set)
    for target in head.split(','):
        if not target:
            continue
        elif target in mapping:
            for directory in sorted(mapping[target]):
                directories[directory].add(target)
        else:
            path = expand(target)
            if os.path.isdir(path):
                directories[path].add(None)
            else:
                abort(USAGE + 'e: invalid target: ' + style.bad(target))
    command = sp.list2cmdline(tail)

    # Check which shell is in use (e.g. zsh, bash, fish)
    shell = os.environ.get('SHELL')
    if shell is None:
        abort('e: undefined environment variable: ' + style.bad('SHELL'))

    # Execute the command in the targeted directories
    msg_head = style.msg('Executing command ') + style.cmd(command)
    if parallel:
        global temp_file, process, processes

        # Add signal handlers to terminate child processes gracefully
        signal.signal(signal.SIGINT, _handle_signal)
        signal.signal(signal.SIGABRT, _handle_signal)
        signal.signal(signal.SIGTERM, _handle_signal)

        # Execute in parallel and pipe the output to temporary files
        sys.stdout.write(msg_head + style.msg(' in parallel...\n\n'))
        for directory in sorted(directories):
            tags = directories[directory]
            temp_file = tempfile.TemporaryFile(mode='w+t')
            process = sp.Popen(
                [shell, '-i', '-c', command],
                cwd=directory,
                stdout=temp_file,
                stderr=temp_file,
                preexec_fn=os.setsid
            )
            processes.append((directory, tags, process, temp_file))

        # Read from the temporary files back to stdout line by line
        for directory, tags, process, temp_file in processes:
            status = process.wait()
            tags = [style.tag(tag) for tag in tags if tag]
            sys.stdout.write('{} {}{}\n'.format(
                style.msg('in'), style.path(directory),
                ((' ' + ' '.join(tags)) if tags else '') + style.msg(':')
            ))
            temp_file.seek(0)
            for line in temp_file:
                sys.stdout.write(line)
            temp_file.close()
            sys.stdout.write(style.msg('Exit status: {}\n\n'.format(status)))
    else:
        # Generate the command string and execute all in one subprocess call
        commands = []
        status_printf = 'printf "{}\n\n"'.format(style.msg(
            'Exit status: ' + ('$status' if '/fish' in shell else '$?')
        ))
        for directory in sorted(directories):
            tags = [style.tag(tag) for tag in directories[directory] if tag]
            commands.append('printf "{} {}{}\n"; cd "{}"; {};{}'.format(
                style.msg('in'), style.path(directory),
                ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'),
                directory, command, status_printf
            ))
        sys.stdout.write(msg_head + style.msg(' in sequence...\n\n'))
        sys.stdout.flush()  # flush for printing chronologically
        sp.call([shell, '-i', '-c', ';'.join(commands)], stderr=sp.STDOUT)
Example #16
0
def main():
    atexit.register(close_stdio)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    if excluded:
        print(
            'Found invalid entries in the mapping:\n' +
            excluded + '\nRun ' + style.cmd('dtags clean') +
            ' to remove them' + '\n'
        )
    # Parse the optional arguments
    interactive = False
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == '-i' and not tail:
        abort(USAGE + 'e: missing argument: ' + style.bad('<targets>'))
    elif head == '-i' and tail:
        interactive = True
        head, tail = tail[0], tail[1:]
    elif head.startswith('-'):
        abort(USAGE + 'e: invalid argument: ' + style.bad(head))

    # Parse the positional arguments
    if not tail:
        abort(USAGE + 'e: missing argument: ' + style.bad('<command>'))
    directories = collections.defaultdict(set)
    for target in head.split(','):
        if not target:
            continue
        elif target in mapping:
            for directory in sorted(mapping[target]):
                directories[directory].add(target)
        else:
            path = expand(target)
            if os.path.isdir(path):
                directories[path].add(None)
            else:
                abort(USAGE + 'e: invalid target: ' + style.bad(target))
    cmd = sp.list2cmdline(tail)

    # Check which shell is in use (e.g. zsh, bash, fish)
    shell = os.environ.get('SHELL')
    if shell is None:
        abort('e: undefined environment variable: ' + style.bad('SHELL'))

    # Generate the command string and execute all in one subprocess call
    cmds = []
    if 'fish' in shell:
        status_printf = (
            'if [ $status = 0 ]; printf "\n\n"; else; printf "{}\n\n"; end'
            .format(style.bad('Exit status: $status'))
        )
    else:
        status_printf = (
            'if [ $? -eq 0 ]; then printf "\n\n"; else printf "{}\n\n"; fi'
            .format(style.bad('Exit status: $?'))
        )
    for directory in sorted(directories):
        tags = [style.tag(tag) for tag in directories[directory] if tag]
        cmds.append('printf "{} {}{}\n"; cd "{}"; {}; {}'.format(
            style.msg('in'), style.path(directory),
            ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'),
            directory, cmd, status_printf
        ))
    sys.stdout.write('\n')
    sys.stdout.flush()  # flush for printing chronologically
    if interactive:
        sp.call([shell, '-i', '-c', ';'.join(cmds)], stderr=sp.STDOUT)
    else:
        sp.call([shell, '-c', ';'.join(cmds)], stderr=sp.STDOUT)
Example #17
0
def main():
    atexit.register(_cleanup_resources)
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    mapping, excluded = load_mapping()
    if excluded:
        print(
            'Found invalid entries in the mapping:\n' +
            excluded + '\nRun ' + style.cmd('dtags clean') +
            ' to remove them' + '\n'
        )
    # Parse the optional arguments
    parallel = False
    args = sys.argv[1:]
    if not args:
        finish(USAGE + DESCRIPTION)
    head, tail = args[0], args[1:]
    if head == '--help':
        finish(USAGE + DESCRIPTION)
    elif head == '--version':
        finish('Version ' + VERSION)
    elif head == '-p' and not tail:
        abort(USAGE + 'Missing argument: ' + style.bad('<targets>'))
    elif head == '-p' and tail:
        parallel = True
        head, tail = tail[0], tail[1:]
    elif head.startswith('-'):
        abort(USAGE + 'Invalid argument: ' + style.bad(head))

    # Parse the positional arguments
    if not tail:
        abort(USAGE + 'Missing argument: ' + style.bad('<command>'))
    directories = collections.defaultdict(set)
    for target in head.split(','):
        if not target:
            continue
        elif target in mapping:
            for directory in sorted(mapping[target]):
                directories[directory].add(target)
        else:
            path = expand(target)
            if os.path.isdir(path):
                directories[path].add(None)
            else:
                abort(USAGE + 'Invalid target: ' + style.bad(target))
    command = sp.list2cmdline(tail)

    # Check which shell is in use (e.g. zsh, bash, fish)
    shell = os.environ.get('SHELL')
    if shell is None:
        abort('Undefined environment variable: ' + style.bad('SHELL'))

    # Execute the command in the targeted directories
    msg_head = style.msg('Executing command ') + style.cmd(command)
    if parallel:
        global temp_file, process, processes

        # Add signal handlers to terminate child processes gracefully
        signal.signal(signal.SIGINT, _handle_signal)
        signal.signal(signal.SIGABRT, _handle_signal)
        signal.signal(signal.SIGTERM, _handle_signal)

        # Execute in parallel and pipe the output to temporary files
        sys.stdout.write(msg_head + style.msg(' in parallel...\n\n'))
        for directory in sorted(directories):
            tags = directories[directory]
            temp_file = tempfile.TemporaryFile(mode='w+t')
            process = sp.Popen(
                [shell, '-i', '-c', command],
                cwd=directory,
                stdout=temp_file,
                stderr=temp_file,
                preexec_fn=os.setsid
            )
            processes.append((directory, tags, process, temp_file))

        # Read from the temporary files back to stdout line by line
        for directory, tags, process, temp_file in processes:
            status = process.wait()
            tags = [style.tag(tag) for tag in tags if tag]
            sys.stdout.write('{} {}{}\n'.format(
                style.msg('in'), style.path(directory),
                ((' ' + ' '.join(tags)) if tags else '') + style.msg(':')
            ))
            temp_file.seek(0)
            for line in temp_file:
                sys.stdout.write(line)
            temp_file.close()
            sys.stdout.write(style.msg('Exit status: {}\n\n'.format(status)))
    else:
        # Generate the command string and execute all in one subprocess call
        commands = []
        status_printf = 'printf "{}\n\n"'.format(style.msg(
            'Exit status: ' + ('$status' if '/fish' in shell else '$?')
        ))
        for directory in sorted(directories):
            tags = [style.tag(tag) for tag in directories[directory] if tag]
            commands.append('printf "{} {}{}\n"; cd "{}"; {};{}'.format(
                style.msg('in'), style.path(directory),
                ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'),
                directory, command, status_printf
            ))
        sys.stdout.write(msg_head + style.msg(' in sequence...\n\n'))
        sys.stdout.flush()  # flush for printing chronologically
        sp.call([shell, '-i', '-c', ';'.join(commands)], stderr=sp.STDOUT)
Example #18
0
def parse_mapping(filename):
    """Parse the mapping file and return a mapping object.

    The mapping object looks like this (tag to directories):
    {
        'app' : {'/home/user/app/backend', '/home/app/frontend'},
        'frontend': {'/home/user/app/frontend'},
        'backend': {'/home/user/app/backend'},
    }
    Any invalid directories and tags are ignored.

    :param filename: the name of the mapping file to parse
    :return: the mapping object and set of ignored paths and tags
    """
    # Collect errors while parsing
    excluded = set()
    orphaned_paths = set()

    # The mapping object to populate
    mapping = defaultdict(set)

    # Parse the file line by line
    with io.open(filename, mode='rt') as mapping_file:
        for line in mapping_file.readlines():
            words = []
            for word in line.split(','):
                word = word.strip()
                if word:
                    words.append(word)
            # Skip empty lines or comments
            if not words or words[0].startswith('#'):
                continue
            # Collect orphan directories
            if len(words) == 1:
                orphaned_paths.add(words[0])
                continue
            path, tags = expand(words[0]), words[1:]
            valid_directory = True
            invalid_path_chars = get_invalid_path_chars(path)
            if invalid_path_chars:
                excluded.add(
                    BULLET + style.bad(path) +
                    ' contains bad characters: ' +
                    style.bad_chars(invalid_path_chars)
                )
                valid_directory = False
            elif not os.path.isdir(path):
                excluded.add(
                    BULLET + style.bad(path) +
                    ' is not a directory path'
                )
                valid_directory = False
            for tag in tags:
                if not tag[0].isalpha():
                    excluded.add(
                        BULLET + style.bad(tag) +
                        ' does not start with an alphabet '
                    )
                    continue
                if ' ' in tag:
                    excluded.add(
                        BULLET + style.bad(tag) +
                        ' contains whitespaces '
                    )
                    continue
                invalid_tag_chars = get_invalid_tag_chars(tag)
                if invalid_tag_chars:
                    excluded.add(
                        BULLET + style.bad(tag) +
                        ' contains bad characters: ' +
                        style.bad_chars(invalid_tag_chars)
                    )
                elif valid_directory:
                    mapping[tag].add(path)
                    orphaned_paths.discard(path)
        excluded.update(
            BULLET + style.bad(orphaned_path) +
            ' is not mapped to any valid tags'
            for orphaned_path in orphaned_paths
        )
    # Return the mapping and the ignored entries if any
    return mapping, '\n'.join(excluded) if excluded else None
Example #19
0
File: d.py Project: tnt-wolve/dtags
DESCRIPTION = """
Arguments:
  dir         The directory path
  tag         The directory tag

Options:
  --help      Display the help menu
  --version   Display the version

Change directory. If the destination is not
specified it defaults to the home directory.
If multiple directories are associated with
the same tag, a selection menu is displayed.
"""

ARG_ERR = '%sd: invalid argument: %s\n'
CHOICE = '%s: %s\n'
DEST_ERR = 'd: invalid destination: %s\n'
INDEX_ERR = 'd: index out of range: %s\n'
INPUT_ERR = 'd: invalid input: %s\n'
PROMPT = '\nSelect directory (1 - %s): '

# Style the messages if the output is going to a terminal
tty = True
ARG_ERR_TTY = '%sd: invalid argument: ' + style.bad('%s\n', tty=tty)
CHOICE_TTY = style.msg('%s: ', tty=tty) + style.path('%s\n', tty=tty)
DEST_ERR_TTY = 'd: invalid destination: ' + style.bad('%s\n', tty=tty)
INDEX_ERR_TTY = 'd: index out of range: ' + style.bad('%s\n', tty=tty)
INPUT_ERR_TTY = 'd: invalid input: ' + style.bad('%s\n', tty=tty)
PROMPT_TTY = style.msg('\nSelect directory (1 - %s): ', tty=tty)