import gc
import math
import os
import re
import sys
import time


APPLICATION_AUTHORS = ["Tuuuux"]
APPLICATION_DESCRIPTION = ""
APPLICATION_LICENSE = "License WTFPL v2"
APPLICATION_NAME = "glxsh"
APPLICATION_VERSION = "0.2.5"
APPLICATION_WARRANTY = "Copyright (C) 2020-2022 Galaxie Shell Project.\nLicense WTFPL Version 2, December 2004 <http://www.wtfpl.net/about/>.\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n"

def size_of(size, suffix="B"):
    for unit in ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]:
        if size < 1024:
            return "{size:.2f}{unit}{suffix}".format(size=size, unit=unit, suffix=suffix)
        size /= 1024


sep = "/"

def normcase(path):
    return path

def normpath(path):
    if path == "":
        return "."
    initial_slashes = path.startswith("/")
    if initial_slashes and path.startswith(("/" * 2)) and (not path.startswith(("/" * 3))):
        initial_slashes = 2
    new_comps = []
    for comp in path.split("/"):
        if comp in ("", "."):
            continue
        if (comp != "..") or ((not initial_slashes) and (not new_comps)) or (new_comps and (new_comps[(-1)] == "..")):
            new_comps.append(comp)
        elif new_comps:
            new_comps.pop()
    path = "/".join(new_comps)
    if initial_slashes:
        path = ("/" * initial_slashes) + path
    return path or "."

def abspath(path):
    if not isabs(path):
        return normpath(join(os.getcwd(), path))
    else:
        return normpath(path)

def join(*args):
    is_bytes = isinstance(args[0], bytes)
    res = ""
    for a in args:
        if is_bytes:
            a = a.decode()
        if (not res) or a.startswith("/"):
            res = a
        else:
            res += "/" + a
    res = res.replace("//", "/")
    if is_bytes:
        return res.encode()
    return res

def split(path):
    if path == "":
        return ("", "")
    r = path.rsplit("/", 1)
    if len(r) == 1:
        return ("", path)
    head = r[0]
    if not head:
        head = "/"
    return (head, r[1])

def splitext(path):
    r = path.rsplit(".", 1)
    if len(r) == 1:
        return (path, "")
    if not r[0]:
        return (path, "")
    return (r[0], ("." + r[1]))

def splitdrive(path):
    return ("", path)

def dirname(path):
    if path and ("/" in path):
        if (path == "///") or (path == "//") or (path == "/") or (path == (len(path) * "/")):
            path = "/"
        else:
            if path.endswith("//"):
                path = path[:(-2)]
            elif path.endswith("/"):
                path = path[:(-1)]
            if "/" in path:
                r = path.rsplit("/", 1)
                if len(r) == 1:
                    head = ""
                else:
                    head = r[0]
                    if not head:
                        head = "/"
                path = head
            if "/" not in path:
                path = "."
    else:
        path = "."
    if path.endswith("//") and (path != "//"):
        path = path[:(-2)]
    elif path.endswith("/") and (path != "/"):
        path = path[:(-1)]
    return path

def basename(string=None, suffix=None):
    step = 1
    if step == 1:
        if (string is None) or (string == ""):
            string = "."
            step = 6
        else:
            step = 2
    if step == 2:
        if string == "//":
            step = "End"
        else:
            step = 3
    if step == 3:
        if string == (len(string) * "/"):
            string = "/"
            step = "End"
        else:
            step = 4
    if step == 4:
        string = re.sub("/+", "/", string)
        step = 5
    if step == 5:
        if string.endswith("//"):
            string = string[:(-2)]
        elif string.endswith("/"):
            string = string[:(-1)]
        if "/" in string:
            string = string.split("/")[(-1)]
        step = 6
    if step == 6:
        if suffix and (string != suffix) and (not suffix.endswith("/")):
            string = string.replace(suffix, "")
    return string

def exists(path):
    return os.access(path, os.F_OK)

def lexists(path):
    if os.access(path, os.F_OK) and os.readlink(path):
        return True
    return False

def isfile(path):
    import stat

    try:
        mode = os.stat(path)[0]
        return stat.S_ISREG(mode)
    except OSError:
        return False

def isdir(path):
    import stat

    try:
        mode = os.stat(path)[0]
        return stat.S_ISDIR(mode)
    except OSError:
        return False

def islink(path):
    import stat

    try:
        mode = os.lstat(path)[0]
        return stat.S_ISLNK(mode)
    except OSError:
        return False

def isabs(path):
    return path.startswith("/")

def realpath(filename, *, strict=False):
    filename = os.fspath(filename)
    (path, ok) = _joinrealpath(filename[:0], filename, strict, {})
    return abspath(path)

def _joinrealpath(path, rest, strict, seen):
    import stat

    if isinstance(path, bytes):
        sep = b"/"
        curdir = b"."
        pardir = b".."
    else:
        sep = "/"
        curdir = "."
        pardir = ".."
    if isabs(rest):
        rest = rest[1:]
        path = sep
    while rest:
        (name, _, rest) = rest.partition(sep)
        if (not name) or (name == curdir):
            continue
        if name == pardir:
            if path:
                (path, name) = split(path)
                if name == pardir:
                    path = join(path, pardir, pardir)
            else:
                path = pardir
            continue
        newpath = join(path, name)
        try:
            st = os.lstat(newpath)
        except OSError:
            if strict:
                raise
            is_link = False
        else:
            is_link = stat.S_ISLNK(st.st_mode)
        if not is_link:
            path = newpath
            continue
        if newpath in seen:
            path = seen[newpath]
            if path is not None:
                continue
            if strict:
                os.stat(newpath)
            else:
                return (join(newpath, rest), False)
        seen[newpath] = None
        (path, ok) = _joinrealpath(path, os.readlink(newpath), strict, seen)
        if not ok:
            return (join(path, rest), False)
        seen[newpath] = path
    return (path, True)

def expanduser(s):
    if (s == "~") or s.startswith("~/"):
        h = os.getenv("HOME")
        return h + s[1:]
    if s[0] == "~":
        return "/home/" + s[1:]
    return s

def commonprefix(m):
    if not m:
        return ""
    s1 = min(m)
    s2 = max(m)
    for (i, c) in enumerate(s1):
        if c != s2[i]:
            return s1[:i]
    return s1

def relpath(path, start=None):
    if not path:
        raise ValueError("no path specified")
    if isinstance(path, bytes):
        curdir = b"."
        sep = b"/"
        pardir = b".."
    else:
        curdir = "."
        sep = "/"
        pardir = ".."
    if start is None:
        start = curdir
    start_list = [x for x in abspath(start).split(sep) if x]
    path_list = [x for x in abspath(path).split(sep) if x]
    i = len(commonprefix([start_list, path_list]))
    rel_list = ([pardir] * (len(start_list) - i)) + path_list[i:]
    if not rel_list:
        return curdir
    return join(*rel_list)


def wrap(text, width=70, expand_tabs=True, replace_whitespace=True, tabsize=8, **kwargs):
    list_to_return = []
    line = ""
    if replace_whitespace:
        if hasattr(re, "sub"):
            text = re.sub(" +", " ", text)
        text = text.replace("\n", " ")
    if expand_tabs:
        text = text.replace("\t", (" " * tabsize))
    for word in text.split(" "):
        if (len(line) + len(("%s " % word))) <= width:
            line += "%s " % word
        elif (len(line) + len(("%s " % word))) > width:
            list_to_return.append(line.strip(" "))
            line = "%s " % word
    if line and (line != ""):
        list_to_return.append(line.strip(" "))
    if list_to_return:
        return list_to_return
    else:
        return [text]

def indent(text, prefix, predicate=None):
    if predicate is None:

        def predicate(line):
            return line.strip()
    def prefixed_lines():
        for line in text.splitlines(True):
            (yield ((prefix + line) if predicate(line) else line))
    return "".join(prefixed_lines())


try:
    from ucollections import namedtuple
except ImportError:
    from collections import namedtuple

class WithCmdArgParser:
    def __init__(self, parser):
        self.parser = parser
    def __call__(self, func):
        if not self.parser:
            self.parser = func(None, None, None, True)

        def wrapped_function(*args, **kwargs):
            try:
                return func(*args, parsed=self.parser.parse_args(args[1].split()), **kwargs)
            except SystemExit:
                return
        return wrapped_function

class Namespace:
    pass

class _ArgError(BaseException):
    pass

class _Arg:
    def __init__(self, names, dest, action, nargs, const, default, type, help):
        self.names = names
        self.dest = dest
        self.action = action
        self.nargs = nargs
        self.const = const
        self.default = default
        self.type = type
        self.help = help
    def parse(self, optname, eq_arg, args):
        if (self.action == "store") or (self.action == "append"):
            if self.nargs is None:
                if eq_arg is not None:
                    ret = eq_arg
                elif args:
                    ret = args.pop(0)
                else:
                    raise _ArgError(("expecting value for %s" % optname))
                return self.type(ret)
            elif self.nargs == "?":
                if eq_arg is not None:
                    ret = eq_arg
                elif args:
                    ret = args.pop(0)
                else:
                    return self.default
                return self.type(ret)
            else:
                ret = []
                if self.nargs == "*":
                    n = -1
                elif self.nargs == "+":
                    if not args:
                        raise _ArgError(("expecting value for %s" % optname))
                    n = -1
                elif self.nargs == "...":
                    n = 0
                    while args:
                        ret.append(args.pop(0))
                else:
                    n = int(self.nargs)
                stop_at_opt = True
                while args and (n != 0):
                    if stop_at_opt and args[0].startswith("-") and (args[0] != "-"):
                        if args[0] == "--":
                            stop_at_opt = False
                            args.pop(0)
                        else:
                            break
                    else:
                        ret.append(args.pop(0))
                        n -= 1
                if n > 0:
                    raise _ArgError(("expecting value for %s" % optname))
                return ret
        elif self.action == "store_const":
            return self.const
        else:
            assert False

def _dest_from_optnames(opt_names):
    dest = opt_names[0]
    for name in opt_names:
        if name.startswith("--"):
            dest = name
            break
    return dest.lstrip("-").replace("-", "_")

class ArgumentParser:
    def __init__(self, *, prog=None, description="", short_description="", add_help=None):
        self.prog = prog
        self.description = description
        self.short_description = short_description
        self.opt = []
        self.pos = []
        if add_help:
            self.add_argument("-h", "--help", dest="help", action="store_true", help="show this help message and exit")
    def add_argument(self, *args, **kwargs):
        action = kwargs.get("action", "store")
        if action == "store_true":
            action = "store_const"
            const = True
            default = kwargs.get("default", False)
        elif action == "store_false":
            action = "store_const"
            const = False
            default = kwargs.get("default", True)
        elif action == "append":
            const = None
            default = kwargs.get("default", [])
        else:
            const = kwargs.get("const", None)
            default = kwargs.get("default", None)
        if args and args[0].startswith("-"):
            list = self.opt
            dest = kwargs.get("dest")
            if dest is None:
                dest = _dest_from_optnames(args)
        else:
            list = self.pos
            dest = kwargs.get("dest")
            if dest is None:
                dest = args[0]
            if not args:
                args = [dest]
        list.append(
            _Arg(
                args,
                dest,
                action,
                kwargs.get("nargs", None),
                const,
                default,
                kwargs.get("type", str),
                kwargs.get("help", ""),
            )
        )
    @staticmethod
    def error(msg):
        sys.stderr.write(("error: %s\n" % msg))
        sys.exit(2)
    def parse_args(self, args=None, namespace=None):
        return self._parse_args_impl(args, namespace, False)
    def parse_known_args(self, args=None, namespace=None):
        return self._parse_args_impl(args, namespace, True)
    def _parse_args_impl(self, args, namespace, return_unknown):
        if args is None:
            args = sys.argv[1:]
        else:
            args = args[:]
        if namespace is None:
            namespace = Namespace()
        try:
            return self._parse_args(args, namespace, return_unknown)
        except _ArgError as e:
            self.print_usage()
            self.error(str(e))
    def _parse_args(self, args, argholder, return_unknown):
        for opt in self.opt:
            setattr(argholder, opt.dest, opt.default)
        unknown = []

        def consume_unknown():
            while args and (not args[0].startswith("-")):
                unknown.append(args.pop(0))
        parsed_pos = False
        while args or (not parsed_pos):
            if args and args[0].startswith("-") and (args[0] != "-") and (args[0] != "--"):
                a = args.pop(0)
                eq_arg = None
                if a.startswith("--") and ("=" in a):
                    (a, eq_arg) = a.split("=", 1)
                found = False
                for (i, opt) in enumerate(self.opt):
                    if a in opt.names:
                        val = opt.parse(a, eq_arg, args)
                        if opt.action == "append":
                            getattr(argholder, opt.dest).append(val)
                        else:
                            setattr(argholder, opt.dest, val)
                        found = True
                        break
                if not found:
                    if return_unknown:
                        unknown.append(a)
                        consume_unknown()
                    else:
                        raise _ArgError(("unknown option %s" % a))
            else:
                if parsed_pos:
                    if return_unknown:
                        unknown = unknown + args
                        break
                    else:
                        raise _ArgError(("extra args: %s" % " ".join(args)))
                for pos in self.pos:
                    setattr(argholder, pos.dest, pos.parse(pos.names[0], None, args))
                parsed_pos = True
                if return_unknown:
                    consume_unknown()
        return (argholder, unknown) if return_unknown else argholder
    def format_usage(self):
        sys.stdout.write(("Usage: %s" % (self.prog or sys.argv[0])))

        def render_arg(arg):
            if arg.action == "store":
                return " %s" % arg.dest
            else:
                return ""
        for opt in self.opt:
            sys.stdout.write((" [%s]" % ", ".join(opt.names)))
        for pos in self.pos:
            sys.stdout.write(render_arg(pos))
        sys.stdout.write("\n")
    def format_help(self):
        columns = 79
        sys.stdout.write("NAME\n")
        sys.stdout.write("  ")
        if self.prog or sys.argv[0]:
            sys.stdout.write(("%s" % (self.prog or sys.argv[0])))
            if self.short_description != "":
                sys.stdout.write(" - ")
        if self.short_description != "":
            sys.stdout.write(("%s" % self.short_description))
        sys.stdout.write("\n")
        sys.stdout.write("\nSYNOPSIS\n")
        sys.stdout.write(("  %s" % (self.prog or sys.argv[0])))

        def render_arg(arg):
            if arg.action == "store":
                return " %s" % arg.dest
            else:
                return ""
        for opt in self.opt:
            sys.stdout.write((" [%s]" % ", ".join(opt.names)))
        for pos in self.pos:
            sys.stdout.write(render_arg(pos))
        sys.stdout.write("\n")
        if self.description:
            sys.stdout.write("\nDESCRIPTION\n")
            for line in wrap(self.description, columns, replace_whitespace=False):
                sys.stdout.write(("%s\n" % indent(line, "  ")))
        if self.pos:
            sys.stdout.write("\nOPERANDS\n")
            max_size = max((len(x.names[0]) for x in self.pos))
            for pos in self.pos:
                the_name = pos.names[0]
                the_help = wrap(pos.help, ((columns - max_size) - 4))
                sys.stdout.write(indent(the_name, "  "))
                for help_line in the_help:
                    if help_line == the_help[0]:
                        sys.stdout.write(indent(help_line, (" " * int(((max_size - len(the_name)) + 2)))))
                        sys.stdout.write("\n")
                    else:
                        sys.stdout.write(indent(help_line, (" " * int((max_size + 4)))))
                        sys.stdout.write("\n")
        if self.opt:
            sys.stdout.write("\nOPTIONS\n")
            max_size = max((len(", ".join(x.names)) for x in self.opt))
            for opt in self.opt:
                the_name = ", ".join(opt.names)
                the_help = wrap(opt.help, ((columns - max_size) - 4))
                sys.stdout.write(indent(the_name, "  "))
                for help_line in the_help:
                    if help_line == the_help[0]:
                        sys.stdout.write(indent(help_line, (" " * int(((max_size - len(the_name)) + 2)))))
                        sys.stdout.write("\n")
                    else:
                        sys.stdout.write(indent(help_line, (" " * int((max_size + 4)))))
                        sys.stdout.write("\n")
    def print_usage(self, file=None):
        self.format_usage()
    def print_help(self, file=None):
        self.format_help()

class FileType(object):
    def __init__(self, mode="r", bufsize=(-1), encoding=None, errors=None):
        self._mode = mode
        self._bufsize = bufsize
        self._encoding = encoding
        self._errors = errors
    def __call__(self, string):
        if string == "-":
            if "r" in self._mode:
                return sys.stdin
            elif "w" in self._mode:
                return sys.stdout
            else:
                raise ValueError(('argument "-" with mode %r' % self._mode))
        try:
            return open(string, self._mode, self._bufsize, self._encoding, self._errors)
        except OSError as e:
            args = {"filename": string, "error": e}
            message = "can't open '%(filename)s': %(error)s"
            raise TypeError((message % args))
    def __repr__(self):
        args = (self._mode, self._bufsize)
        kwargs = [("encoding", self._encoding), ("errors", self._errors)]
        args_str = ", ".join(
            (
                [repr(arg) for arg in args if (arg != (-1))]
                + [("%s=%r" % (kw, arg)) for (kw, arg) in kwargs if (arg is not None)]
            )
        )
        return "%s(%s)" % (type(self).__name__, args_str)


parser_man = ArgumentParser(
    prog="man", description="The man utility shall write information about each of the name operands.", add_help=True
)
parser_man.add_argument("name", nargs="*", default=[], help="A keyword or the name of a standard utility.")
PROMPT = "(Cmd) "
IDENTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"

class Cmd:
    prompt = PROMPT
    identchars = IDENTCHARS
    ruler = "="
    lastcmd = ""
    intro = None
    doc_leader = ""
    doc_header = "Documented commands (type man <topic>):"
    misc_header = "Miscellaneous help topics:"
    undoc_header = "Undocumented commands:"
    nohelp = "No manual entry for %s"
    use_rawinput = 1

    def __init__(self, stdin=None, stdout=None):
        if stdin is not None:
            self.stdin = stdin
        else:
            self.stdin = sys.stdin
        if stdout is not None:
            self.stdout = stdout
        else:
            self.stdout = sys.stdout
        self.cmdqueue = []
        self.exit_code = 0
    def cmdloop(self, intro=None):
        self.preloop()
        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                self.stdout.write((str(self.intro) + "\n"))
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                elif self.use_rawinput:
                    try:
                        line = input(self.prompt)
                    except EOFError:
                        line = "EOF"
                else:
                    self.stdout.write(self.prompt)
                    self.stdout.flush()
                    line = self.stdin.readline()
                    if not len(line):
                        line = "EOF"
                    else:
                        line = line.rstrip("\r\n")
                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)
            self.postloop()
        finally:
            pass
    def precmd(self, line):
        return line
    def postcmd(self, stop, line):
        return stop
    def preloop(self):
        pass
    def postloop(self):
        pass
    def parseline(self, line):
        line = line.strip()
        if not line:
            return (None, None, line)
        elif line[0] == "?":
            line = "man " + line[1:]
        elif line[0] == "!":
            if hasattr(self, "do_shell"):
                line = "shell " + line[1:]
            else:
                return (None, None, line)
        (i, n) = (0, len(line))
        while (i < n) and (line[i] in self.identchars):
            i = i + 1
        (cmd, arg) = (line[:i], line[i:].strip())
        return (cmd, arg, line)
    def onecmd(self, line):
        (cmd, arg, line) = self.parseline(line)
        if not line:
            return self.emptyline()
        if cmd is None:
            return self.default(line)
        self.lastcmd = line
        if line == "EOF":
            self.lastcmd = ""
        if cmd == "":
            return self.default(line)
        else:
            try:
                func = getattr(self, ("do_" + cmd))
            except AttributeError:
                return self.default(line)
            return func(arg)
    def emptyline(self):
        if self.lastcmd:
            return self.onecmd(self.lastcmd)
    def default(self, line):
        self.stdout.write(("*** Unknown syntax: %s\n" % line))
    def get_names(self):
        return dir(self.__class__)
    @staticmethod
    def help_man():
        parser_man.print_help()
    @WithCmdArgParser(parser_man)
    def do_man(self, arg, **kwargs):
        if kwargs["parsed"].help:
            self.help_man()
            return
        if arg:
            try:
                func = getattr(self, ("help_" + arg))
            except AttributeError:
                self.stdout.write(("%s\n" % str((self.nohelp % (arg,)))))
                return
            func()
        else:
            names = self.get_names()
            cmds_doc = []
            cmds_undoc = []
            help = {}
            for name in names:
                if name[:5] == "help_":
                    help[name[5:]] = 1
            names.sort()
            prevname = ""
            for name in names:
                if name[:3] == "do_":
                    if name == prevname:
                        continue
                    prevname = name
                    cmd = name[3:]
                    if cmd in help:
                        cmds_doc.append(cmd)
                        del help[cmd]
                    else:
                        cmds_undoc.append(cmd)
            self.stdout.write(("%s\n" % self.doc_leader))
            self.print_topics(self.doc_header, cmds_doc, 15, 80)
            self.print_topics(self.misc_header, list(help.keys()), 15, 80)
            self.print_topics(self.undoc_header, cmds_undoc, 15, 80)
    def print_topics(self, header, cmds, cmdlen, maxcol):
        if cmds:
            self.stdout.write(("%s\n" % header))
            if self.ruler:
                self.stdout.write(("%s\n" % str((self.ruler * len(header)))))
            self.columnize(cmds, (maxcol - 1))
            self.stdout.write("\n")
    def columnize(self, list, displaywidth=80):
        if not list:
            self.stdout.write("<empty>\n")
            return
        nonstrings = [i for i in range(len(list)) if (not isinstance(list[i], str))]
        if nonstrings:
            raise TypeError(("list[i] not a string for i in %s" % ", ".join(map(str, nonstrings))))
        size = len(list)
        if size == 1:
            self.stdout.write(("%s\n" % str(list[0])))
            return
        for nrows in range(1, len(list)):
            ncols = ((size + nrows) - 1) // nrows
            colwidths = []
            totwidth = -2
            for col in range(ncols):
                colwidth = 0
                for row in range(nrows):
                    i = row + (nrows * col)
                    if i >= size:
                        break
                    x = list[i]
                    colwidth = max(colwidth, len(x))
                colwidths.append(colwidth)
                totwidth += colwidth + 2
                if totwidth > displaywidth:
                    break
            if totwidth <= displaywidth:
                break
        else:
            nrows = len(list)
            ncols = 1
            colwidths = [0]
        for row in range(nrows):
            texts = []
            for col in range(ncols):
                i = row + (nrows * col)
                if i >= size:
                    x = ""
                else:
                    x = list[i]
                texts.append(x)
            while texts and (not texts[(-1)]):
                del texts[(-1)]
            for col in range(len(texts)):
                texts[col] = "%-*s" % (colwidths[col], texts[col])
            self.stdout.write(("%s\n" % str("  ".join(texts))))

def tabulate(tabular_data, headers, tablefmt, colalign):
    value_to_return = []
    columns_info = {}
    if headers:
        tabular_data.insert(0, headers)
    for line in tabular_data:
        for (index_col, cell_value) in enumerate(line):
            columns_info[index_col] = {}
            columns_info[index_col]["data"] = []
            columns_info[index_col]["text"] = []
            columns_info[index_col]["size"] = 0
            columns_info[index_col]["colalign"] = None
    for line in tabular_data:
        for (index_col, cell_value) in enumerate(line):
            columns_info[index_col]["data"].append(cell_value)
            columns_info[index_col]["text"].append(cell_value)
            if colalign:
                columns_info[index_col]["colalign"] = colalign[index_col]
            if len(str(cell_value)) > columns_info[index_col]["size"]:
                columns_info[index_col]["size"] = len(str(cell_value))
    for (key, value) in columns_info.items():
        for (index, item) in enumerate(value["data"]):
            value["text"][index] = str(value["text"][index])
            if len(str(item)) < value["size"]:
                if value["colalign"].lower() == "right":
                    spacing = " " * int((value["size"] - len(str(item))))
                    value["text"][index] = "%s%s" % (spacing, value["text"][index])
                elif value["colalign"].lower() == "center":
                    spacing = " " * int((int((value["size"] - len(str(item)))) / 2))
                    value["text"][index] = "%s%s%s" % (spacing, value["text"][index], spacing)
                else:
                    spacing = " " * int((value["size"] - len(str(item))))
                    value["text"][index] = "%s%s" % (value["text"][index], spacing)
    line_to_append = ""
    spacing = " "
    for (line, line_value) in enumerate(tabular_data):
        for (col, col_value) in enumerate(line_value):
            line_to_append += "%s%s" % (columns_info[col]["text"][line], spacing)
        value_to_return.append(line_to_append.strip(" "))
        line_to_append = ""
    return "\n".join(value_to_return)

class EINVAL(Exception):
    pass

class ENOMEM(Exception):
    pass

class GLXEnviron(object):
    def __init__(self):
        self.__environ = None
        self.environ = None
    @property
    def environ(self):
        return self.__environ
    @environ.setter
    def environ(self, value):
        if value is None:
            value = {}
        if type(value) != dict:
            raise TypeError("'environ' property value must be a dict type or None")
        if self.environ != value:
            self.__environ = value
    def getenv(self, name=None):
        if type(name) != str:
            raise TypeError("'name' parameter must be a str or None")
        if name in self.environ:
            return self.environ[name]
        else:
            return None
    def setenv(self, envname=None, envval=None, overwrite=None):
        if type(envname) != str:
            raise TypeError("'name' parameter must be a str")
        if type(envval) != str:
            raise TypeError("'value' parameter must be a str")
        if (envname == "") or ("=" in envname):
            raise EINVAL()
        if (envname in self.environ) and (overwrite == 0):
            return 0
        elif ((envname in self.environ) and (overwrite != 0)) or (envname not in self.environ):
            try:
                self.environ[envname] = envval
                return 0
            except MemoryError:
                raise ENOMEM()
        return -1
    def unsetenv(self, name):
        if type(name) != str:
            raise TypeError("'name' parameter value must be a str type")
        if (name == "") or ("=" in name):
            raise EINVAL()
        if name not in self.environ:
            return 0
        try:
            del self.environ[name]
            return 0
        except KeyError:
            return -1


parser_basename = ArgumentParser(
    prog="basename",
    description="The string operand shall be treated as a pathname, as defined in XBD Pathname. The string string shall be converted to the filename corresponding to the last pathname component in string and then the suffix string suffix, if present, shall be removed.",
    short_description="return non-directory portion of a pathname",
)
parser_basename.add_argument("string", type=str, nargs="?", default=None, help="a string")
parser_basename.add_argument("suffix", nargs="?", default=None, help="a string")

def basename(string=None, suffix=None):
    step = 1
    if step == 1:
        if (string is None) or (string == ""):
            string = "."
            step = 6
        else:
            step = 2
    if step == 2:
        if string == "//":
            step = "End"
        else:
            step = 3
    if step == 3:
        if string == (len(string) * string[0]):
            string = "/"
            step = "End"
        else:
            step = 4
    if step == 4:
        string = re.sub("/+", "/", string)
        step = 5
    if step == 5:
        if string.endswith("/"):
            string = string[:(-1)]
        if "/" in string:
            string = string.split("/")[(-1)]
        step = 6
    if step == 6:
        if suffix and (string != suffix) and (not suffix.endswith("/")):
            string = string.replace(suffix, "")
    return string


parser_cat = ArgumentParser(
    prog="cat",
    description="The cat utility shall read files in sequence and shall write their contents to the standard output in the same sequence.",
    short_description="concatenate and print files",
)
parser_cat.add_argument(
    "file",
    nargs="*",
    type=FileType("r"),
    default=sys.stdin,
    help="with no FILE, or when FILE is -, read standard input.",
)
parser_cat.add_argument(
    "-u",
    dest="update",
    action="store_true",
    default=False,
    help="Write bytes from the input file to the standard output without delay as each is read.",
)

def cat(files, update):
    head(files, (1 << 30))


parser_cd = ArgumentParser(
    prog="cd",
    short_description="change the working directory",
    description="The cd utility shall change the working directory of the current shell execution environment",
)
parser_cd.add_argument(
    "directory",
    nargs="?",
    const=0,
    help="An absolute or relative pathname of the directory that shall become the new working directory. The interpretation of a relative pathname by cd depends on the -L option and the CDPATH and PWD environment variables. If directory is an empty string, the results are unspecified.",
)
parser_cd.add_argument(
    "-P",
    dest="physical",
    action="store_true",
    default=False,
    help="Handle the operand dot-dot physically; symbolic link components shall be resolved before dot-dot components are processed",
)
parser_cd.add_argument(
    "-L",
    dest="logical",
    action="store_true",
    default=False,
    help="Handle the operand dot-dot logically; symbolic link components shall not be resolved before dot-dot components are processed ",
)

def cd(directory=None, logical=None, physical=None, **kwargs):
    shell = kwargs.get("shell") or None
    curpath = None
    CDPATH = os.getenv("CDPATH")
    if os.getenv("CDPATH") is None:
        CDPATH = ""
    if logical and physical:
        physical = False
    if (not logical) and (not physical):
        logical = True
    if directory == "-":
        if os.getenv("OLDPWD"):
            directory = os.getenv("OLDPWD")
        else:
            directory = os.getenv("PWD")
    elif directory is not None:
        directory = expanduser(directory)
    step = 1
    if step == 1:
        print(("Step 1 -> %s" % curpath))
        if ((directory is None) and (not os.getenv("HOME"))) or (os.getenv("HOME") == ""):
            return
        else:
            step = 2
    if step == 2:
        print(("Step 2 -> %s" % curpath))
        if (directory is None) and os.getenv("HOME"):
            directory = os.getenv("HOME")
        step = 3
    if step == 3:
        print(("Step 3 -> %s" % curpath))
        if directory.startswith("/"):
            curpath = directory
            step = 7
        else:
            step = 4
    if step == 4:
        print(("Step 4 -> %s" % curpath))
        if (directory == ".") or (directory == ".."):
            step = 6
        else:
            step = 5
    if step == 5:
        print(("Step 5 -> %s" % curpath))
        if CDPATH:
            for pathname in CDPATH.split(":"):
                if pathname:
                    if isdir(("%s/%s" % (pathname, directory))):
                        curpath = "%s/%s" % (pathname, directory)
                        step = 7
                        break
                    elif isdir(("./%s" % directory)):
                        curpath = "./%s" % directory
                        step = 7
                        break
        else:
            step = 6
    if step == 6:
        print(("Step 6 -> %s" % curpath))
        curpath = directory
        step = 7
    if step == 7:
        print(("Step 7 -> %s" % curpath))
        if physical:
            step = 10
        else:
            if not curpath.startswith("/"):
                if not os.getenv("PWD").endswith("/"):
                    curpath = "%s%s%s" % (os.getenv("PWD"), "/", curpath)
            step = 8
    if step == 8:
        print(("Step 8 -> %s" % curpath))
        curpath = realpath(curpath)
        step = 9
    if step == 9:
        print(("Step 9 -> %s" % curpath))
        step = 10
    if step == 10:
        print(("Step 10 -> %s" % curpath))
        err = None
        if not isdir(curpath):
            err = "cd: %s: No such file or directory" % curpath
        elif not os.access(curpath, os.R_OK):
            err = "cd: %s: Permission denied" % curpath
        else:
            try:
                os.chdir(curpath)
            except Exception as ex:
                err = f"{ex}"
            else:
                if os.getenv("OLDPWD") != os.getenv("PWD"):
                    os.putenv("OLDPWD", os.getenv("PWD"))
                if os.getenv("PWD") != os.getcwd():
                    os.putenv("PWD", os.getcwd())
        return err


parser_clear = ArgumentParser(prog="clear", description="Clear screen")

def clear():
    return "\x1b[2J\x1b[H"


parser_cp = ArgumentParser(
    prog="cp",
    short_description="copy files",
    description="The first synopsis form is denoted by two operands, neither of which are existing files of type directory. The cp utility shall copy the contents of source_file (or, if source_file is a file of type symbolic link, the contents of the file referenced by source_file) to the destination path named by target_file.",
)
parser_cp.add_argument(
    "-f",
    dest="force",
    action="store_true",
    default=False,
    help="If a file descriptor for a destination file cannot be obtained, as described in step 3.a.ii., attempt to unlink the destination file and proceed.",
)
parser_cp.add_argument(
    "-i",
    dest="interactive",
    action="store_true",
    help="Write a prompt to standard error before copying to any existing non-directory destination file.",
)
parser_cp.add_argument(
    "source_file",
    nargs="?",
    const=0,
    help="A pathname of a file to be copied. If a source_file operand is '-', it shall refer to a file named -; implementations shall not treat it as meaning standard input. target_file",
)
parser_cp.add_argument(
    "target_file",
    nargs="?",
    const=0,
    help="A pathname of an existing or nonexistent file, used for the output when a single file is copied. If a target_file operand is '-', it shall refer to a file named -; implementations shall not treat it as meaning standard output.",
)

def cp(source_file, target_file, interactive=False):
    if interactive:
        if exists(target_file):
            if input(("do you want to overwrite %s file ? (Y/n)" % target_file)).upper().startswith("N"):
                return
    try:
        with open(source_file, "r") as source_file:
            with open(target_file, "w") as target_file:
                target_file.write(source_file.read())
    except Exception as error:
        sys.stdout.write(("cp: %s\n" % error))


parser_df = ArgumentParser(
    prog="df",
    short_description="report free disk space",
    description="The df utility shall write the amount of available space and file slots for file systems on which the invoking user has appropriate read access. File systems shall be specified by the file operands; when none are specified, information shall be written for all file systems.The format of the default output from df is unspecified, but all space figures are reported in 512-byte units, unless the -k option is specified. This output shall contain at least the file system names, amount of available space on each of these file systems, and, if no options other than -t are specified, the number of free file slots, or inodes, available; when -t is specified, the output shall contain the total allocated space as well.",
)
parser_df.add_argument(
    "-h", dest="human_readable", action="store_true", default=False, help="print sizes in powers of 1024 (e.g., 1023M)"
)
parser_df.add_argument(
    "-k",
    dest="kilo",
    action="store_const",
    const=1024,
    help="use 1024-byte units, instead of the default 512-byte units, when writing space figures.",
)
parser_df.add_argument("-P", dest="portability", action="store_true", default=True, help="produce a POSIX output")
parser_df.add_argument(
    "-t", dest="total", action="store_true", help="include total allocated-space figures in the output. "
)
parser_df.add_argument(
    "file",
    nargs="?",
    const=0,
    help="A pathname of a file within the hierarchy of the desired file system. If a file other than a FIFO, a regular file, a directory, or a special file representing the device containing the file system (for example, /dev/dsk/0s1) is specified, the results are unspecified. If the file operand names a file other than a special file containing a file system, df shall write the amount of free space in the file system containing the specified file operand. Otherwise, df shall write the amount of free space in that file system. ",
)

def df(file=None, block_size=None, total=None, human_readable=None):
    if block_size is None:
        block_size = 512
    devices_list = []
    if file:
        if not exists(file):
            return "df: %s: No such file or directory" % file
        if (not os.access(file, os.R_OK)) or (not os.access(df_find_mount_point(file), os.R_OK)):
            return "df: %s : Permission denied\n" % file
        for line in df_get_devices():
            if df_find_mount_point(file) == line[1]:
                devices_list.append(
                    df_get_device_information(file_system_name=line[0], file_system_root=line[1], block_size=block_size)
                )
    else:
        for line in df_get_devices():
            devices_list.append(
                df_get_device_information(file_system_name=line[0], file_system_root=line[1], block_size=block_size)
            )
    if devices_list:
        if total:
            (total_space_free, total_space_used, total_total_space) = df_get_totals(devices_list)
            devices_list.append(
                [
                    "total",
                    total_total_space,
                    total_space_used,
                    total_space_free,
                    (
                        "%d%%"
                        % int(math.ceil((100 * (float((total_total_space - total_space_free)) / total_total_space))))
                    ),
                    "-",
                ]
            )
        (block_size_text, tabular_data) = df_get_info_to_print(block_size, devices_list, human_readable)
        return df_print_final(block_size_text, tabular_data)

def df_get_info_to_print(block_size, devices_list, human_readable):
    block_size_text = f"{block_size}-blocks"
    if human_readable:
        tabular_data = []
        block_size_text = "Size"
        for line in devices_list:
            if (str(line[1]) != "-") and (str(line[2]) != "-") and (str(line[3]) != "-"):
                tabular_data.append(
                    [
                        line[0],
                        size_of(size=(int(line[1]) * block_size), suffix=""),
                        size_of(size=(int(line[2]) * block_size), suffix=""),
                        size_of(size=(int(line[3]) * block_size), suffix=""),
                        line[4],
                        line[5],
                    ]
                )
            else:
                tabular_data.append(line)
    else:
        tabular_data = devices_list
    return (block_size_text, tabular_data)

def df_get_totals(devices_list):
    total_total_space = 0
    total_space_used = 0
    total_space_free = 0
    for device in devices_list:
        try:
            total_total_space += int(device[1])
        except ValueError:
            pass
        try:
            total_space_used += int(device[2])
        except ValueError:
            pass
        try:
            total_space_free += int(device[3])
        except ValueError:
            pass
    return (total_space_free, total_space_used, total_total_space)

def df_print_final(block_size_text, tabular_data):
    return "%s" % tabulate(
        tabular_data=tabular_data,
        headers=["Filesystem", block_size_text, "Used", "Available", "Capacity", "Mounted on"],
        tablefmt="plain",
        colalign=("left", "right", "right", "right", "right", "left"),
    )

def df_find_mount_point(path):
    if not islink(path):
        path = abspath(path)
    elif islink(path) and lexists(os.readlink(path)):
        path = realpath(path)
    if hasattr(os, "path") and hasattr(os, path, "ismount"):
        while not os.path.ismount(path):
            path = dirname(path)
            if islink(path) and lexists(os.readlink(path)):
                path = realpath(path)
    return path

def df_get_device_information(file_system_name=None, file_system_root=None, block_size=None):
    try:
        statvfs = os.statvfs(file_system_root)
        if type(statvfs) == tuple:
            #     statvfs[0] = f_bsize
            #     statvfs[1] = f_frsize
            #     statvfs[2] = f_blocks
            #     statvfss[3] = f_bfree
            #     statvfs[4] = f_bavail
            #     statvfss[5] = f_files
            #     statvfs[6] = f_ffree
            #     statvfs[7] = f_favail
            #     statvfs[8] = f_flags
            #     statvfss[9] = f_namemax
            space_free = statvfs[4] * statvfs[1] / block_size
            total_space = statvfs[2] * statvfs[1] / block_size
        else:
            space_free = statvfs.f_bavail * statvfs.f_frsize / block_size
            total_space = statvfs.f_blocks * statvfs.f_frsize / block_size
        space_used = total_space - space_free
        if total_space == 0:
            percentage_used = "-"
        else:
            percentage_used = "%d%%" % int(math.ceil(100 * (float(total_space - space_free) / total_space)))

        return [
            ("%s" % file_system_name),
            ("%d" % total_space),
            ("%d" % space_used),
            ("%d" % space_free),
            ("%s" % percentage_used),
            ("%s" % file_system_root),
        ]
    except Exception:
        return [
            ("%s" % file_system_name),
            ("%s" % "-"),
            ("%s" % "-"),
            ("%s" % "-"),
            ("%s" % "-"),
            ("%s" % file_system_root),
        ]

def df_get_devices(file=None):
    if (file is None) and exists("/etc/mtab"):
        file = "/etc/mtab"
    if (file is None) and exists("/proc/mounts"):
        file = "/proc/mounts"
    if file is None:
        raise SystemError("Impossible to locate /etc/mtab or /proc/mounts file")
    file_entries = []
    for line in df_get_file_content(file=file).splitlines():
        if len(line.split()) < 4:
            continue
        file_entries.append(line.split())
    return file_entries

def df_get_file_content(file=None):
    if not exists(file):
        raise FileExistsError(f"{file} do not exist")
    if not os.access(file, os.R_OK):
        raise PermissionError(f"{file} can't be read")
    with open(file) as datafile:
        return datafile.read().strip()

parser_dirname = ArgumentParser(
    prog="dirname",
    short_description="return the directory portion of a pathname",
    description="The string operand shall be treated as a pathname, as defined in XBD Pathname. The string string shall be converted to the name of the directory containing the filename corresponding to the last pathname component in string.",
)
parser_dirname.add_argument("string", nargs="?", const=0, help="A string")

parser_env = ArgumentParser(
    prog="env",
    short_description="set the environment for command invocation",
    description="The env utility shall obtain the current environment, modify it according to its arguments, then invoke the utility named by the utility operand with the modified environment.",
)
parser_env.add_argument(
    "-i",
    dest="invoke",
    action="store_true",
    help="Invoke utility with exactly the environment specified by the arguments; the inherited environment shall be ignored completely.",
)
parser_env.add_argument(
    "name",
    nargs="*",
    dest="name",
    default=None,
    help="Arguments of the form name= value shall modify the execution environment, and shall be placed into the inherited environment before the utility is invoked.",
)
parser_env.add_argument(
    "utility",
    nargs="*",
    dest="utility",
    default=None,
    help="The name of the utility to be invoked. If the utility operand names any of the special built-in utilities in Special Built-In Utilities, the results are undefined.",
)
parser_env.add_argument(
    "argument",
    nargs="*",
    dest="argument",
    default=None,
    help="A string to pass as an argument for the invoked utility.",
)

def env(name, utility, argument, **kwargs):
    stdout = kwargs.get("stdout", sys.stdout)
    stdin = kwargs.get("stdin", sys.stdin)
    stderr = kwargs.get("stdin", sys.stderr)
    shell = kwargs.get("shell", None)
    if shell and hasattr(shell, "environ"):
        if name:
            stdout.write(("%s\n" % name))
        else:
            for (name, value) in shell.environ.items():
                stdout.write(("%s=%s\n" % (name, value)))

parser_exit = ArgumentParser(prog="exit", description="exit shell with a given exit code")
parser_exit.add_argument("code", nargs="*", type="int", help="exit code")

parser_head = ArgumentParser(
    prog="head",
    short_description="copy the first part of",
    description="The head utility shall copy its input files to the standard output, ending the output for each file at a designated point. ",
)
parser_head.add_argument(
    "file",
    nargs="*",
    help="A pathname of an input file. If no file operands are specified, the standard input shall be used.",
)
parser_head.add_argument(
    "-n",
    dest="number",
    type=int,
    default=10,
    help="The first number lines of each input file shall be copied to standard output. ",
)

def head(files, number=10, **kwargs):
    stdout = kwargs.get("stdout", sys.stdout)
    if (files is None) or (not isinstance(files, list)):
        return
    if isinstance(number, int) and (number < 1):
        return
    for file in files:
        with open(file) as f:
            for i in range(number):
                line = f.readline()
                if not line:
                    break
                stdout.write(line)


parser_ls = ArgumentParser(
    prog="ls",
    short_description="list directory contents",
    description="For each operand that names a file of a type other than directory or symbolic link to a directory, ls shall write the name of the file as well as any requested, associated information. For each operand that names a file of type directory, ls shall write the names of files contained within the directory as well as any requested, associated information. Filenames beginning with a <period> ( '.' ) and any associated information shall not be written out unless explicitly referenced, the -A or -a option is supplied, or an implementation-defined condition causes them to be written. If one or more of the -d, -F, or -l options are specified, and neither the -H nor the -L option is specified, for each operand that names a file of type symbolic link to a directory, ls shall write the name of the file as well as any requested, associated information. If none of the -d, -F, or -l options are specified, or the -H or -L options are specified, for each operand that names a file of type symbolic link to a directory, ls shall write the names of files contained within the directory as well as any requested, associated information. In each case where the names of files contained within a directory are written, if the directory contains any symbolic links then ls shall evaluate the file information and file type to be those of the symbolic link itself, unless the -L option is specified.\n\nIf no operands are specified, ls shall behave as if a single operand of dot ( '.' ) had been specified. If more than one operand is specified, ls shall write non-directory operands first; it shall sort directory and non-directory operands separately according to the collating sequence in the current locale.\n",
)

def ls(path=None):
    if (path is None) or (path == ""):
        try:
            path = os.getcwd()
        except Exception as error:
            return "ls: %s" % error
    try:
        listdir = os.listdir(path)
        listdir.sort()
        Cmd().columnize(listdir)
    except Exception as error:
        return "ls: %s" % error


parser_mkdir = ArgumentParser(
    prog="mkdir",
    short_description="make directories",
    description="The mkdir utility shall create the directories specified by the operands",
)
parser_mkdir.add_argument("dir", nargs="*", help="a pathname of a directory to be created.")
parser_mkdir.add_argument(
    "-m",
    dest="mode",
    nargs="?",
    const=1,
    type=str,
    default="755",
    help="set the file permission bits of the newly-created directory to the specified mode value.",
)
parser_mkdir.add_argument(
    "-p",
    dest="parents",
    action="store_true",
    default=False,
    help="create any missing intermediate pathname components.",
)

def mkdir(directories=None, parents=False, mode="755"):
    value_to_return = []
    for directory in directories:
        if parents:
            if str(directory).startswith("/"):
                directory_to_create = "/"
            else:
                directory_to_create = ""
            for sub_directory in directory.split("/"):
                if directory_to_create == "/":
                    directory_to_create = "%s%s" % (directory_to_create, sub_directory)
                elif (directory_to_create != "") and (sub_directory != ""):
                    directory_to_create = "%s%s%s" % (directory_to_create, "/", sub_directory)
                elif sub_directory == "":
                    continue
                elif sub_directory == ".":
                    directory_to_create = "."
                    continue
                elif sub_directory == "..":
                    directory_to_create = ".."
                    continue
                else:
                    directory_to_create = sub_directory
                try:
                    os.mkdir(path=directory_to_create, mode=int(mode))
                except TypeError:
                    os.mkdir(directory_to_create)
                except OSError as error:
                    return "mkdir: %s" % error
        else:
            try:
                os.stat(directory)
                value_to_return.append(("mkdir: cannot create directory '%s': File exists" % directory))
            except OSError:
                try:
                    try:
                        os.mkdir(path=directory, mode=int(mode))
                    except OSError as error:
                        return "mkdir: %s" % error
                except TypeError:
                    try:
                        os.mkdir(directory)
                    except OSError as error:
                        return "mkdir: %s" % error
    if value_to_return:
        return "\n".join(value_to_return)


parser_mv = ArgumentParser(
    prog="mv",
    short_description="move files",
    description="The mv utility shall move the file named by the source_file operand to the destination specified by the target_file. This first synopsis form is assumed when the final operand does not name an existing directory and is not a symbolic link referring to an existing directory. In this case, if source_file names a non-directory file and target_file ends with a trailing <slash> character, mv shall treat this as an error and no source_file operands will be processed.",
)
parser_mv.add_argument("source_file", nargs="?", const=0, help="A pathname of a file to be copied.")
parser_mv.add_argument("source_file", nargs="?", const=0, help="A pathname of a file or directory to be moved.")
parser_mv.add_argument("target_file", nargs="?", const=0, help="A new pathname for the file or directory being moved.")
parser_mv.add_argument(
    "target_dir", nargs="?", const=0, help="A pathname of an existing directory into which to move the input files."
)
parser_mv.add_argument(
    "-f",
    dest="force",
    action="store_true",
    default=False,
    help="Do not prompt for confirmation if the destination path exists. Any previous occurrence of the -i option is ignored.",
)
parser_mv.add_argument(
    "-i",
    dest="interactive",
    action="store_true",
    default=False,
    help="Prompt for confirmation if the destination path exists. Any previous occurrence of the -f option is ignored.",
)

def mv(source_file=None, target_file=None, target_dir=None, force=None, interactive=None):
    try:
        if os.access(source_file, os.F_OK):
            os.rename(source_file, target_file)
    except Exception as error:
        return "mv: %s\n" % error
    pass


parser_pwd = ArgumentParser(
    prog="pwd",
    short_description="return working directory name",
    description="The pwd utility shall write to standard output an absolute pathname of the current working directory, which does not contain the filenames dot or dot-dot.",
)
parser_pwd.add_argument(
    "-L",
    dest="logical",
    action="store_true",
    default=False,
    help="f the PWD environment variable contains an absolute pathname of the current directory and the pathname does not contain any components that are dot or dot-dot, pwd shall write this pathname to standard output, except that if the PWD environment variable is longer than {PATH_MAX} bytes including the terminating null",
)
parser_pwd.add_argument(
    "-P",
    dest="physical",
    action="store_true",
    default=False,
    help="The pathname written to standard output shall not contain any components that refer to files of type symbolic link. If there are multiple pathnames that the pwd utility could write to standard output, one beginning with a single <slash> character and one or more beginning with two <slash> characters, then it shall write the pathname beginning with a single <slash> character. The pathname shall not contain any unnecessary <slash> characters after the leading one or two <slash> characters.",
)

def pwd(logical, physical):
    if (not logical) and (not physical):
        logical = True
    if logical:
        return normpath(os.getcwd())
    else:
        return realpath(os.getcwd())


parser_rm = ArgumentParser(
    prog="rm",
    short_description="remove directory entries",
    description="The rm utility shall remove the directory entry specified by each file argument.\n\nIf either of the files dot or dot-dot are specified as the basename portion of an operand (that is, the final pathname component) or if an operand resolves to the root directory, rm shall write a diagnostic message to standard error and do nothing more with such operands.",
)
parser_rm.add_argument(
    "-f",
    dest="force",
    action="store_true",
    default=False,
    help="Do not prompt for confirmation. Do not write diagnostic messages or modify the exit status in the case of no file operands, or in the case of operands that do not exist. Any previous occurrences of the -i option shall be ignored.",
)
parser_rm.add_argument(
    "-i",
    dest="interactive",
    action="store_true",
    default=False,
    help="Prompt for confirmation as described previously. Any previous occurrences of the -f option shall be ignored.",
)
parser_rm.add_argument(
    "-R",
    "-r",
    dest="recursive",
    action="store_true",
    default=False,
    help="Remove file hierarchies. See the DESCRIPTION.",
)
parser_rm.add_argument("file", nargs="*", default=[], help="A pathname of a directory entry to be removed.")

def rm(files=None, recursive=None, interactive=None, force=None):
    to_return = []

    def delete_file(f):
        try:
            os.remove(f)
        except Exception as error:
            to_return.append(("rm: %s" % error))
    if files:
        for file in files:
            delete_file(file)
    if to_return:
        return "\n".join(to_return)


parser_rmdir = ArgumentParser(
    prog="rmdir",
    short_description="remove directories",
    description="The rmdir utility shall remove the directory entry specified by each dir operand.",
)
parser_rmdir.add_argument("dir", nargs="*", default=[], help="A pathname of an empty directory to be removed.")
parser_rmdir.add_argument(
    "-p", dest="parents", action="store_true", default=False, help="Remove all directories in a pathname."
)

def rmdir(directories=None, parents=False):
    to_return = []

    def delete_directory(d):
        try:
            os.rmdir(path=d)
        except OSError as error:
            to_return.append(("rmdir: %s" % error))
    for directory in directories:
        if parents:
            for (path, dirs, files) in os.walk(directory, False):
                for f in files:
                    os.unlink(((path + "/") + f))
                delete_directory(path)
        else:
            delete_directory(directory)
    if to_return:
        return "\n".join(to_return)


parser_sleep = ArgumentParser(
    prog="sleep",
    short_description="suspend execution for an interval",
    description="The sleep utility shall suspend execution for at least the integral number of seconds specified by the time operand. ",
)
parser_sleep.add_argument(
    "time",
    nargs="?",
    default=0,
    help="A non-negative decimal integer or float specifying the number of seconds for which to suspend execution.",
)

def sleep(sec):
    to_return = []

    def string_to_numeric_if_possible(x):
        try:
            val = float(x)
            return int(val) if (val == int(val)) else val
        except (TypeError, ValueError):
            return x
    try:
        time.sleep(string_to_numeric_if_possible(sec))
    except TypeError as error:
        to_return.append(("sleep: %s" % error))
    except KeyboardInterrupt:
        return
    if to_return:
        return "\n".join(to_return)

parser_touch = ArgumentParser(
    prog="touch",
    short_description="change file access and modification times",
    description="The touch utility shall change the last data modification timestamps, the last data access timestamps, or both.\n\nThe time used can be specified by the -t time option-argument, the corresponding time fields of the file referenced by the -r ref_file option-argument, or the -d date_time option-argument, as specified in the following sections. If none of these are specified, touch shall use the current time.",
)
parser_touch.add_argument(
    "-a",
    dest="access_time",
    help="Change the access time of file. Do not change the modification time unless -m is also specified.",
)
parser_touch.add_argument(
    "-c",
    help="Do not create a specified file if it does not exist. Do not write any diagnostic messages concerning this condition.",
)
parser_touch.add_argument(
    "-m", help="Change the modification time of file. Do not change the access time unless -a is also specified."
)
parser_touch.add_argument(
    "-r", help="Use the corresponding time of the file named by the pathname ref_file instead of the current time."
)
parser_touch.add_argument(
    "-t",
    help="Use the specified time instead of the current time. The option-argument shall be a decimal number of the form",
)
parser_touch.add_argument(
    "file",
    help="Use the specified time instead of the current time. The option-argument shall be a decimal number of the form",
)

def touch(file=None):
    raise NotImplementedError("Touch is not implemented yet ...")


parser_tty = ArgumentParser(
    prog="tty",
    short_description="return user's terminal name",
    description="The tty utility shall write to the standard output the name of the terminal that is open as standard input.",
)

def tty():
    to_return = []
    try:
        to_return.append(("%s" % os.ttyname(0)))
    except OSError:
        to_return.append("not a tty")
    if to_return:
        return "\n".join(to_return)


parser_uname = ArgumentParser(
    prog="uname",
    short_description="return system name",
    description="By default, the uname utility shall write the operating system name to standard output. When options are specified, symbols representing one or more system characteristics shall be written to the standard output. The format and contents of the symbols are implementation-defined. On systems conforming to the System Interfaces volume of POSIX.1-2017, the symbols written shall be those supported by the uname() function as defined in the System Interfaces volume of POSIX.1-2017.",
)
parser_uname.add_argument(
    "-a", dest="all", action="store_true", help="Behave as though all of the options -mnrsv were specified."
)
parser_uname.add_argument(
    "-s", dest="sysname", action="store_true", help="Write the name of the implementation of the operating system."
)
parser_uname.add_argument(
    "-n",
    dest="nodename",
    action="store_true",
    help="Write the name of this node within an implementation-defined communications network.",
)
parser_uname.add_argument(
    "-r",
    dest="release",
    action="store_true",
    help="Write the current release level of the operating system implementation.",
)
parser_uname.add_argument(
    "-v",
    action="store_true",
    dest="version",
    help="Write the current version level of this release of the operating system implementation.",
)
parser_uname.add_argument(
    "-m",
    dest="machine",
    action="store_true",
    help="Write the name of the hardware type on which the system is running to standard output.",
)

def uname(all=False, sysname=False, nodename=False, release=False, version=False, machine=False):
    info = os.uname()

    def gen_lines():
        if all or nodename:
            (yield info.nodename)
        if all or release:
            (yield info.release)
        if all or version:
            (yield info.version)
        if all or machine:
            (yield info.machine)
    lines = list(gen_lines())
    if all or sysname or (not lines):
        lines.insert(0, info.sysname)
    return " ".join(lines)


class GLXUsh(Cmd, GLXEnviron):
    prompt = "> "
    if hasattr(sys.implementation, "mpy"):
        loader_mpy = "MPY %s" % sys.implementation.mpy
    else:
        loader_mpy = ""
    if hasattr(gc, "mem_free") and hasattr(gc, "mem_alloc"):
        gc.collect()
        memory_total = "%s MEMORY SYSTEM\n" % size_of((gc.mem_free() + gc.mem_alloc())).upper()
        memory_free = "%s FREE\n" % size_of(gc.mem_free()).upper()
    elif hasattr(os, "sysconf"):
        memory_total = "%s RAM SYSTEM\n" % size_of((os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES"))).upper()
        memory_free = "%s FREE\n" % size_of((os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_AVPHYS_PAGES"))).upper()
    else:
        memory_total = ""
        memory_free = ""
    intro = (
        "******************************* %s V%s **********************************\n\n%s\nLOADER %s %s %s\nEXEC PYTHON V%s\n%s%s"
        % (
            APPLICATION_NAME.upper(),
            APPLICATION_VERSION.upper(),
            APPLICATION_LICENSE.upper(),
            sys.implementation.name.upper(),
            ".".join((str(item).upper() for item in list(sys.implementation.version))),
            loader_mpy,
            sys.version.upper(),
            memory_total,
            memory_free,
        )
    )

    def __init__(self, *args, **kwargs):
        super().__init__()
        GLXEnviron.__init__(self)
        if hasattr(os, "environ"):
            self.environ = os.environ.copy()
        else:
            self.setenv("PATH", os.getcwd(), 1)
            self.setenv("HOME", "/", 1)
    def postloop(self):
        return self.exit_code
    def do_EOF(self, line):
        self.stdout.write("\n")
        return True
    def default(self, line):
        sys.stderr.write("glxush: %s : unknown command\n")
        return
    @staticmethod
    def help_basename():
        parser_basename.print_help()
    @WithCmdArgParser(parser_basename)
    def do_basename(self, *args, **kwargs):
        self.stdout.write(("%s\n" % basename(string=kwargs["parsed"].string, suffix=kwargs["parsed"].suffix)))
    @staticmethod
    def help_cat():
        parser_cat.print_help()
    @WithCmdArgParser(parser_cat)
    def do_cat(self, *args, **kwargs):
        returned = cat(files=kwargs["parsed"].file, update=kwargs["parsed"].update)
        if returned:
            self.stdout.write(("%s\n" % returned))
    @WithCmdArgParser(parser_cd)
    def do_cd(self, *args, **kwargs):
        returned = cd(
            directory=kwargs["parsed"].directory,
            logical=kwargs["parsed"].logical,
            physical=kwargs["parsed"].physical,
            shell=self,
        )
        if returned:
            self.stdout.write(("%s\n" % returned))
    @staticmethod
    def help_cd():
        parser_cd.print_help()
    @WithCmdArgParser(parser_clear)
    def do_clear(self, *args, **kwargs):
        self.stdout.write(("%s" % clear()))
    @staticmethod
    def help_clear():
        parser_clear.print_help()
    @WithCmdArgParser(parser_cp)
    def do_cp(self, *args, **kwargs):
        cp(
            source_file=kwargs["parsed"].source_file,
            target_file=kwargs["parsed"].target_file,
            interactive=kwargs["parsed"].interactive,
        )
    @staticmethod
    def help_cp():
        parser_cp.print_help()
    @WithCmdArgParser(parser_df)
    def do_df(self, *args, **kwargs):
        self.stdout.write(
            (
                "%s\n"
                % df(
                    file=kwargs["parsed"].file,
                    block_size=kwargs["parsed"].kilo,
                    total=kwargs["parsed"].total,
                    human_readable=kwargs["parsed"].human_readable,
                )
            )
        )
    @staticmethod
    def help_df():
        parser_df.print_help()
    @WithCmdArgParser(parser_dirname)
    def do_dirname(self, *args, **kwargs):
        self.stdout.write(("%s\n" % dirname(kwargs["parsed"].string)))
    @staticmethod
    def help_dirname():
        parser_dirname.print_help()
    @staticmethod
    def help_env():
        parser_env.print_help()
    @WithCmdArgParser(parser_env)
    def do_env(self, *args, **kwargs):
        env(
            name=kwargs["parsed"].name,
            utility=kwargs["parsed"].utility,
            argument=kwargs["parsed"].argument,
            stdout=self.stdout,
            shell=self,
        )
    @WithCmdArgParser(parser_head)
    def do_head(self, *args, **kwargs):
        if not kwargs["parsed"].file:
            parser_head.print_usage()
            return
        head(files=kwargs["parsed"].file, number=kwargs["parsed"].number)
    @staticmethod
    def help_head():
        parser_head.print_help()
    def emptyline(self):
        pass
    @WithCmdArgParser(parser_ls)
    def do_ls(self, *args, **kwargs):
        ls()
    @staticmethod
    def help_ls():
        parser_ls.print_help()
    @WithCmdArgParser(parser_exit)
    def do_exit(self, *args, **kwargs):
        if not kwargs["parsed"].code:
            self.exit_code = 0
        else:
            self.exit_code = kwargs["parsed"].code[0]
        return True
    @staticmethod
    def help_exit():
        parser_exit.print_help()
    @WithCmdArgParser(parser_mkdir)
    def do_mkdir(self, *args, **kwargs):
        if not kwargs["parsed"].dir:
            parser_mkdir.print_usage()
            return
        returned = mkdir(directories=kwargs["parsed"].dir, parents=kwargs["parsed"].parents, mode=kwargs["parsed"].mode)
        if returned:
            self.stdout.write(("%s\n" % returned))
    @staticmethod
    def help_mkdir():
        parser_mkdir.print_help()
    @WithCmdArgParser(parser_mv)
    def do_mv(self, *args, **kwargs):
        returned = mv(
            source_file=kwargs["parsed"].source_file,
            target_file=kwargs["parsed"].target_file,
            target_dir=kwargs["parsed"].target_dir,
            force=kwargs["parsed"].force,
            interactive=kwargs["parsed"].interactive,
        )
        if returned:
            self.stdout.write(("%s\n" % returned))
    @staticmethod
    def help_mv():
        parser_mv.print_help()
    @WithCmdArgParser(parser_pwd)
    def do_pwd(self, *args, **kwargs):
        try:
            sys.stdout.write(("%s\n" % pwd(logical=kwargs["parsed"].logical, physical=kwargs["parsed"].physical)))
        except Exception as error:
            sys.stderr.write(("pwd: %s\n" % error))
    @staticmethod
    def help_pwd():
        parser_pwd.print_help()
    @WithCmdArgParser(parser_rm)
    def do_rm(self, *args, **kwargs):
        if not kwargs["parsed"].file:
            parser_rm.print_usage()
            return
        returned_value = rm(
            files=kwargs["parsed"].file,
            recursive=kwargs["parsed"].recursive,
            interactive=kwargs["parsed"].interactive,
            force=kwargs["parsed"].force,
        )
        if returned_value:
            self.stdout.write(("%s\n" % returned_value))
    @staticmethod
    def help_rm():
        parser_rm.print_help()
    @WithCmdArgParser(parser_rmdir)
    def do_rmdir(self, *args, **kwargs):
        if not kwargs["parsed"].dir:
            parser_rmdir.print_usage()
            return
        returned_value = rmdir(directories=kwargs["parsed"].dir, parents=kwargs["parsed"].parents)
        if returned_value:
            self.stdout.write(("%s\n" % returned_value))
    @staticmethod
    def help_rmdir():
        parser_rmdir.print_help()
    @WithCmdArgParser(parser_sleep)
    def do_sleep(self, *args, **kwargs):
        if not kwargs["parsed"].time:
            parser_sleep.print_usage()
            return
        returned_value = sleep(sec=kwargs["parsed"].time)
        if returned_value:
            self.stdout.write(("%s\n" % returned_value))
    @staticmethod
    def help_tty():
        parser_tty.print_help()
    @WithCmdArgParser(parser_tty)
    def do_tty(self, *args, **kwargs):
        returned_value = tty()
        if returned_value:
            self.stdout.write(("%s\n" % returned_value))
    @staticmethod
    def help_sleep():
        parser_sleep.print_help()
    @WithCmdArgParser(parser_uname)
    def do_uname(self, *args, **kwargs):
        try:
            sys.stdout.write(
                (
                    "%s\n"
                    % uname(
                        all=kwargs["parsed"].all,
                        sysname=kwargs["parsed"].sysname,
                        nodename=kwargs["parsed"].nodename,
                        release=kwargs["parsed"].release,
                        version=kwargs["parsed"].version,
                        machine=kwargs["parsed"].machine,
                    )
                )
            )
        except AttributeError as e:
            sys.stdout.write(("%s\n" % e))
    @staticmethod
    def help_uname():
        parser_uname.print_help()


parser_glxsh = ArgumentParser(prog="glxsh", add_help=True)
parser_glxsh.add_argument(
    "command", nargs="?", help="optional commands or file to run, if no commands given, enter an interactive shell"
)
parser_glxsh.add_argument(
    "command_args", nargs="...", help="if commands is not a file use optional arguments for commands"
)

def main():
    if len(sys.argv) > 1:
        args = parser_glxsh.parse_args(sys.argv[1:])
        if args.help:
            parser_glxsh.print_help()
            return
        if isfile(args.command):
            raise NotImplementedError("Load file script is not implemented yet")
        else:
            sys.exit(GLXUsh().onecmd(("%s %s" % (args.command, " ".join(args.command_args)))))
    else:
        sys.exit(GLXUsh().cmdloop())

if __name__ == "__main__":
    sys.exit(main())
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT