Module lib.shell.commands

Class defining a minimalist shell, directly executable on the board. We modify directories, list, delete, move files, edit files .. The commands are : - cd : change directory - pwd : current directory - cat : display the content of file - cls : clear screen - cleanupsd : erase on sdcard all unnecessary files left behind by mac os or windows - mkdir : create directory - mv : move file - rmdir : remove directory - cp : copy file - rm : remove file - ls : list file - ll : list file long - date : get the system date or synchronize with Ntp - setdate : set date and time - uptime : the amount of time system is running - find : find a file - run : execute python script - download : transfer files from device to computer (only available with camflasher) - upload : transfer files from computer to device (only available with camflasher) - edit : edit a text file - exit : exit of shell - exec : execute python string with exec - eval : evaluate python expression - gc : garbage collection - grep : grep text in many files - mount : mount sd card - umount : umount sd card - temperature : device temperature - meminfo : memory informations - flashinfo : flash informations - sysinfo : system informations - deepsleep : deepsleep of board - ping : ping host - reboot : reboot board - help : list all command available - man : manual of one command - df : display free disk space - ip2host : convert ip address in hostname - host2ip : convert hostname in ip address - eval : evaluation python string - exec : execute python string - dump : display hexadecimal dump of the content of file

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
# pylint:disable=too-many-lines
# pylint:disable=consider-using-f-string
# pylint:disable=unspecified-encoding
""" Class defining a minimalist shell, directly executable on the board.
We modify directories, list, delete, move files, edit files ..
The commands are :
- cd          : change directory
- pwd         : current directory
- cat         : display the content of file
- cls         : clear screen
- cleanupsd   : erase on sdcard all unnecessary files left behind by mac os or windows
- mkdir       : create directory
- mv          : move file
- rmdir       : remove directory
- cp          : copy file
- rm          : remove file
- ls          : list file
- ll          : list file long
- date        : get the system date or synchronize with Ntp
- setdate     : set date and time
- uptime      : the amount of time system is running
- find        : find a file
- run         : execute python script
- download    : transfer files from device to computer (only available with camflasher)
- upload      : transfer files from computer to device (only available with camflasher)
- edit        : edit a text file
- exit        : exit of shell
- exec        : execute python string with exec
- eval        : evaluate python expression
- gc          : garbage collection
- grep        : grep text in many files
- mount       : mount sd card
- umount      : umount sd card
- temperature : device temperature
- meminfo     : memory informations
- flashinfo   : flash informations
- sysinfo     : system informations
- deepsleep   : deepsleep of board
- ping        : ping host
- reboot      : reboot board
- help        : list all command available
- man         : manual of one command
- df          : display free disk space
- ip2host     : convert ip address in hostname
- host2ip     : convert hostname in ip address
- eval        : evaluation python string
- exec        : execute python string
- dump        : display hexadecimal dump of the content of file
"""
# pylint:disable=wrong-import-position
import sys
import io
import os
import uos
import machine
import tools.useful
import tools.logger
import tools.sdcard
import tools.filesystem
import tools.exchange
import tools.info
import tools.strings
import tools.terminal
import tools.watchdog
import tools.lang
import tools.date
import tools.console
import tools.system
import tools.archiver

shell_commands = None

def get_screen_size():
        """ Return the screen size and check if output redirected """
        # pylint:disable=global-variable-not-assigned
        if tools.console.Console.is_redirected() is False:
                height, width = tools.terminal.get_screen_size()
        else:
                height, width = tools.terminal.MAXINT, 80
        return height, width

def print_part(message, width, height, count):
        """ Print a part of text """
        # pylint:disable=global-variable-not-assigned
        if isinstance(message , bytes):
                message = message.decode("utf8")
        if count is not None and count >= height:
                tools.console.Console.print(message,end="")
                if tools.console.Console.is_redirected() is False:
                        key = tools.terminal.getch()
                else:
                        key = " "
                count = 1
                if key in ["x","X","q","Q","\x1B"]:
                        return None
                tools.console.Console.print("\n", end="")
        else:
                if count is None:
                        count = 1
                else:
                        count += 1
                tools.console.Console.print(message)
        return count

class Displayer:
        """ Displayer file """
        def __init__(self, path, long, action=print_part):
                """ Constructor """
                self.height, self.width = get_screen_size()
                self.path    = path
                self.showdir = True
                self.count   = 1
                self.long    = long
                self.action  = action

        def purge_path(self, path):
                """ Purge the path for the display """
                path = path.encode("utf8")
                path = tools.filesystem.normpath(path)
                prefix = tools.filesystem.prefix([path, self.path.encode("utf8")])
                return path[len(prefix):].lstrip(b"/")

        def get_info(self, path):
                """ Get info on file or directory """
                result    = None, False
                fileinfo  = tools.filesystem.fileinfo(path)
                file_date = fileinfo[8]
                size      = fileinfo[6]

                # If directory
                if fileinfo[0] & 0x4000 == 0x4000:
                        if self.showdir:
                                if self.long:
                                        message = b"%s %s [%s]"%(tools.date.date_to_bytes(file_date),b" "*7,self.purge_path(path))
                                else:
                                        message = b"[%s]"%self.purge_path(path)
                                result = message, True
                # If file
                else:
                        if self.long:
                                message = b"%s %s %s"%(tools.date.date_to_bytes(file_date),tools.strings.size_to_bytes(size),self.purge_path(path))
                        else:
                                message = self.purge_path(path)
                        result = message, False
                return result

        def show(self, path):
                """ Show the information of a file or directory """
                message, directory = self.get_info(path)
                if message is not None:
                        self.count = self.action(message, self.width, self.height, self.count)

        def show_dir(self, state):
                """ Indicates if the directory must show """
                self.showdir = state

def cd(directory = "/"):
        """ Change directory """
        try:
                uos.chdir(tools.filesystem.normpath(directory))
        except:
                if directory != ".":
                        tools.console.Console.print("No such file or directory '%s'"%directory)

def pwd():
        """ Display the current directory """
        tools.console.Console.print("%s"%uos.getcwd())

def mkdir(directory, recursive=False, quiet=False):
        """ Make directory """
        try:
                if quiet is False:
                        tools.console.Console.print("mkdir '%s'"%directory)
                tools.filesystem.makedir(tools.filesystem.normpath(directory), recursive)
        except:
                tools.console.Console.print("Cannot mkdir '%s'"%directory)

def removedir(directory, force=False, quiet=False, simulate=False, ignore_error=False):
        """ Remove directory """
        try:
                if tools.filesystem.exists(directory+"/.DS_Store"):
                        rmfile(directory+"/.DS_Store", quiet, force, simulate)
                if (tools.filesystem.ismicropython() or force) and simulate is False:
                        uos.rmdir(directory)
                if quiet is False:
                        tools.console.Console.print("rmdir '%s'"%(directory))
        except:
                if ignore_error is False:
                        tools.console.Console.print("rmdir '%s' not removed"%(directory))

def rmdir(directory, root=None, recursive=False, force=False, quiet=False, simulate=False, ignore_error=False):
        """ Remove directory """
        directory = tools.filesystem.normpath(directory)
        if recursive is False:
                removedir(directory, force=force, quiet=quiet, simulate=simulate, ignore_error=ignore_error)
        else:
                directories = [directory]
                d = directory
                while 1:
                        parts = tools.filesystem.split(d)
                        if parts[1] == "" or parts[0] == "":
                                break
                        if root:
                                if len(parts[0]) <= len(root):
                                        break
                        directories.append(parts[0])
                        d = parts[0]
                if "/" in directories:
                        directories.remove("/")
                if tools.sdcard.SdCard.get_mountpoint() in directories:
                        directories.remove(tools.sdcard.SdCard.get_mountpoint())
                for d in directories:
                        if tools.filesystem.exists(d) and d != ".":
                                removedir(d, force=force, quiet=quiet, simulate=simulate, ignore_error=ignore_error)

def movefile(source, destination, quiet):
        """ Move file """
        source      = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)
        if quiet is False:
                tools.console.Console.print("mv '%s' -> '%s'"%(source,destination))
        uos.rename(source,destination)

def mv(source, destination, quiet=False):
        """ Move or rename file """
        source      = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)

        try:
                if tools.filesystem.isfile(source):
                        movefile(source,destination,quiet)
                else:
                        if tools.filesystem.isdir(source):
                                # If destination existing
                                if tools.filesystem.exists(destination):
                                        # If destination is file
                                        if tools.filesystem.isfile(destination):
                                                raise IOError()
                                        else:
                                                # Move source into destination directory
                                                destination = destination + "/" + tools.filesystem.split(source)[1]
                                                movefile(tools.filesystem.normpath(source),tools.filesystem.normpath(destination), quiet)
                                else:
                                        # Rename
                                        movefile(tools.filesystem.normpath(source),tools.filesystem.normpath(destination), quiet)
                        else:
                                # Move many files
                                path, pattern = tools.filesystem.split(source)

                                if path == "":
                                        path = uos.getcwd()

                                _, filenames = tools.filesystem.scandir(path, pattern, False)

                                for src in filenames:
                                        dst = destination + "/" + src[len(path):]
                                        movefile(src,dst,quiet)
        except:
                tools.console.Console.print("Cannot mv '%s'->'%s'"%(source,destination))

def copyfile(src,dst,quiet):
        """ Copy file """
        dst = dst.replace("//","/")
        dst = dst.replace("//","/")
        dstdir, dstfile = tools.filesystem.split(dst)
        try:
                if not tools.filesystem.exists(dstdir):
                        if dstdir != "." and dstdir != "":
                                mkdir(dstdir, recursive=True, quiet=quiet)
                src_file = open(src, 'rb')
                dst_file = open(dst, 'wb')
                if quiet is False:
                        tools.console.Console.print("cp '%s' -> '%s'"%(src,dst))
                while True:
                        buf = src_file.read(256)
                        if len(buf) > 0:
                                dst_file.write(buf)
                        if len(buf) < 256:
                                break
                src_file.close()
                dst_file.close()
        except:
                tools.console.Console.print("Cannot cp '%s' -> '%s'"%(src, dst))

def cp(source, destination, recursive=False, quiet=False):
        """ Copy file command """
        source = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)
        if tools.filesystem.isfile(source):
                copyfile(source,destination,quiet)
        else:
                if tools.filesystem.isdir(source):
                        path = source
                        pattern = "*"
                else:
                        path, pattern = tools.filesystem.split(source)

                _, filenames = tools.filesystem.scandir(path, pattern, recursive)

                for src in filenames:
                        dst = destination + "/" + src[len(path):]
                        copyfile(src,dst,quiet)

def rmfile(filename, quiet=False, force=False, simulate=False):
        """ Remove file """
        try:
                if (tools.filesystem.ismicropython() or force) and simulate is False:
                        uos.remove(tools.filesystem.normpath(filename))
                if quiet is False:
                        tools.console.Console.print("rm '%s'"%(filename))
        except:
                tools.console.Console.print("rm '%s' not removed"%(filename))

class Remover(Displayer):
        """ Class to remove files and directories """
        def __init__(self, file, recursive=False, quiet=False, force=False, simulate=False):
                Displayer.__init__(self, uos.getcwd(), False)
                self.directories = set()
                self.path = uos.getcwd()
                self.file = file
                self.quiet = quiet
                self.force = force
                self.simulate = simulate
                self.recursive = recursive
                if recursive:
                        if tools.filesystem.isdir(self.path + "/" + file):
                                self.directories.add(self.path + "/" + file)

        def show(self, path):
                """ Show the information of a file or directory """
                message, directory = self.get_info(path)
                if message is not None:
                        if directory is False:
                                rmfile(path, force=self.force, quiet=self.quiet, simulate=self.simulate)
                if directory:
                        self.directories.add(path)

        def remove_dir(self):
                """ Remove directories """
                if self.recursive:
                        directories = list(self.directories)
                        directories.sort()
                        directories.reverse()
                        for directory in directories:
                                rmdir(directory, root=self.path, recursive=self.recursive, force=self.force, quiet=self.quiet, simulate=self.simulate, ignore_error=True)

def rm(file, recursive=False, quiet=False, force=False, simulate=False):
        """ Remove file command """
        remover = Remover(file, recursive, quiet, force, simulate)
        searchfile(file, recursive, remover)
        remover.remove_dir()

def ls(file="", recursive=False, long=False):
        """ List files command """
        searchfile(file, recursive, Displayer(uos.getcwd(), long))

def ll(file="", recursive=False):
        """ List files long command """
        searchfile(file, recursive, Displayer(uos.getcwd(), True))

def searchfile(file, recursive, obj = None):
        """ Search file """
        file = tools.filesystem.normpath(file)
        p = tools.filesystem.abspath(uos.getcwd(), file)
        filenames = []
        try:
                # If file not defined then current directory used
                if file == "":
                        _,filenames = tools.filesystem.scandir(uos.getcwd(), "*", recursive, obj)
                # If one file detected
                elif tools.filesystem.isfile(p):
                        if obj is not None:
                                obj.show_dir(False)
                                obj.show(p)
                        filenames = [p]
                # If directory detected
                elif tools.filesystem.isdir(p):
                        _, filenames = tools.filesystem.scandir(p, "*", recursive, obj)
                # Else pattern detected
                else:
                        path, pattern = tools.filesystem.split(p)
                        if pattern != "*":
                                if obj is not None:
                                        obj.show_dir(False)
                        _, filenames = tools.filesystem.scandir(path, pattern, recursive, obj)
        except Exception as err:
                tools.console.Console.print(err)
        if len(filenames) == 0 and file != "" and file != "." and tools.filesystem.isdir(file) is False:
                tools.console.Console.print("%s : No such file or directory"%file)
        return filenames

def find(file):
        """ Find a file in directories """
        filenames = searchfile(file, True)
        for filename in filenames:
                tools.console.Console.print(filename)

def grep(file, text, recursive=False, ignorecase=False, regexp=False):
        """ Grep command """
        from re import search
        file = tools.filesystem.normpath(file)
        def __search(text, content, ignorecase, regexp):
                if ignorecase:
                        content  = content.lower()
                        text = text.lower()
                if regexp:
                        if search(text, content):
                                return True
                else:
                        if content.find(text) != -1:
                                return True
                return False

        def __grep(text, filename, ignorecase, regexp, width, height, count):
                lineNumber = 1
                with open(filename,"r", encoding="latin-1") as f:
                        while 1:
                                line = f.readline()
                                if line:
                                        if __search(text, line, ignorecase, regexp):
                                                line = line.replace("\t","    ")
                                                message = "%s:%d:%s"%(filename, lineNumber, line)
                                                message = message.rstrip()[:width]
                                                count = print_part(message, width, height, count)
                                                if count is None:
                                                        tools.console.Console.print("")
                                                        return None
                                        lineNumber += 1
                                else:
                                        break
                return count

        if tools.filesystem.isfile(file):
                filenames = [file]
        else:
                path, pattern = tools.filesystem.split(file)
                _, filenames = tools.filesystem.scandir(path, pattern, recursive)

        height, width = get_screen_size()
        count = 1
        for filename in filenames:
                count = __grep(text, filename, ignorecase, regexp, width, height, count)
                if count is None:
                        break

def ping(host):
        """ Ping host """
        try:
                from server.ping import ping as ping_
                ping_(host, count=4, timeout=1)
        except:
                tools.console.Console.print("Not available")

def ip2host(ip_address):
        """ Convert ip to hostname """
        try:
                import wifi.station
                _, _, _, dns = wifi.station.Station.get_info()
                from server.dnsclient import resolve_hostname
                tools.console.Console.print(resolve_hostname(dns, ip_address))
        except:
                tools.console.Console.print("Not available")

def host2ip(hostname):
        """ Convert hostname to ip """
        try:
                import wifi.station
                _, _, _, dns = wifi.station.Station.get_info()
                from server.dnsclient import resolve_ip_address
                tools.console.Console.print(resolve_ip_address(dns, hostname))
        except:
                tools.console.Console.print("Not available")

def mountsd(mountpoint="/sd"):
        """ Mount command """
        try:
                tools.sdcard.SdCard.mount(mountpoint)
                tools.console.Console.print("Sd mounted on '%s'"%mountpoint)
        except:
                tools.console.Console.print("Cannot mount sd on '%s'"%mountpoint)

def umountsd(mountpoint="/sd"):
        """ Umount command """
        try:
                tools.sdcard.SdCard.umount(mountpoint)
                tools.console.Console.print("Sd umounted from '%s'"%mountpoint)
        except:
                tools.console.Console.print("Cannot umount sd from '%s'"%mountpoint)

def date_time(update=False, offsetUTC=+1, noDst=False):
        """ Get or set date """
        try:
                from server.timesetting import set_date
                if update:
                        if noDst:
                                dst = False
                        else:
                                dst = True
                        set_date(offsetUTC, dst)
                del sys.modules["server.timesetting"]
        except:
                pass
        tools.console.Console.print(tools.date.date_to_string())

def setdate(datetime=""):
        """ Set date and time """
        import re
        file_date=re.compile("[/: ]")
        failed = False
        try:
                spls = file_date.split(datetime)

                lst = []
                if len(spls) > 1:
                        for spl in spls:
                                if len(spl) > 0:
                                        try:
                                                r = spl.lstrip("0")
                                                if len(r) == 0:
                                                        lst.append(0)
                                                else:
                                                        lst.append(eval(r))
                                        except:
                                                failed = True
                                                break
                        if len(lst) == 6:
                                # pylint: disable=unbalanced-tuple-unpacking
                                year,month,day,hour,minute,second = lst
                                machine.RTC().datetime((year, month, day, 0, hour, minute, second, 0))
                        else:
                                failed = True
                else:
                        failed = True
        except Exception as err:
                failed = True
                tools.logger.syslog(err)

        if failed is True:
                tools.console.Console.print('Expected format "YYYY/MM/DD hh:mm:ss"')

def formatsd(fstype="FAT"):
        """ Format sd card """
        if fstype in ["FAT","LFS"]:
                if tools.sdcard.SdCard.is_mounted() is False:
                        res = input("All data will be lost on Sd card ? proceed with format (y/n) :")
                        if res in ["y","Y"]:
                                if tools.sdcard.SdCard.formatsd() is True:
                                        tools.console.Console.print("Formatting terminated")
                                else:
                                        tools.console.Console.print("Formatting failed")
                else:
                        tools.console.Console.print("Sd card is mounted, a reboot required")
        else:
                tools.console.Console.print("Filesystem supported : FAT or LFS")

def cleanupsd():
        """ Remove not useful directory and file from sd card """
        if tools.sdcard.SdCard.is_mounted():
                cd(tools.sdcard.SdCard.get_mountpoint())
                rm   (".Spotlight-V100", recursive=True)
                rm   (".fseventsd",      recursive=True)
                rm   (".Trashes",       recursive=True)
                rm   ("RECYCLER",        recursive=True)
                rm   ("/._*",            recursive=True)
                rm   ("/.DS_Store",      recursive=True)
                rm   ("System Volume Information",        recursive=True)
        else:
                tools.console.Console.print("Sd card not mounted")

def reboot():
        """ Reboot command """
        try:
                tools.system.reboot("Reboot device with command")
        except:
                machine.deepsleep(1000)

def deepsleep(seconds=60):
        """ Deep sleep command """
        machine.deepsleep(int(seconds)*1000)

def ligthsleep(seconds=60):
        """ Light sleep command """
        machine.lightsleep(int(seconds)*1000)

edit_class = None
def edit(file, no_color=False, read_only=False):
        """ Edit command """
        # pylint:disable=global-variable-not-assigned
        global edit_class
        if tools.console.Console.is_redirected() is False:
                if edit_class is None:
                        try:
                                from shell.editor import Editor
                        except:
                                from editor import Editor
                        edit_class = Editor
                edit_class(file, no_color=no_color, read_only=read_only)

def cat(file):
        """ Cat command """
        try:
                f = open(file, "r")
                height, width = get_screen_size()
                count = 1
                while 1:
                        line = f.readline()
                        if not line:
                                break
                        message = line.replace("\t","    ").rstrip()[:width]
                        count = print_part(message, width, height, count)
                        if count is None:
                                break
                f.close()
        except:
                tools.console.Console.print("Cannot cat '%s'"%(file))

def df(mountpoint = None):
        """ Display free disk space """
        tools.console.Console.print(tools.strings.tostrings(tools.info.flashinfo(mountpoint=mountpoint)))

def gc():
        """ Garbage collector command """
        from gc import collect
        collect()

def uptime():
        """ Tell how long the system has been running """
        tools.console.Console.print(tools.strings.tostrings(tools.info.uptime()))

def man(command):
        """ Man command """
        tools.console.Console.print(man_one(command))

def man_one(command_name):
        """ Manual of one command """
        try:
                command_name, command_function, command_params, command_flags = get_command(command_name)
                text = "  " + command_name + " "
                for param in command_params:
                        text += param + " "
                text += "\n"
                for flag,flagName,val in command_flags:
                        text += "    %s : %s\n"%(flag,flagName)
                result = text[:-1]
        except:
                result = "Unknown command '%s'"%command_name
        return result

# pylint: disable=redefined-builtin
def help():
        """ Help command """
        height, width = get_screen_size()
        count = 1
        cmds = list(shell_commands.keys())
        cmds.sort()
        for command in cmds:
                lines = man_one(command)
                lines = "-"*30+"\n" + lines
                for line in lines.split("\n"):
                        count = print_part(line, width, height, count)
                        if count is None:
                                return

def eval_(string):
        """ Evaluate content of string """
        tools.console.Console.print(eval(string))

def exec_(string):
        """ Execute content of string """
        exec(string)

shell_exited = False
def exit():
        """ Exit shell command """
        global shell_exited
        shell_exited = True

def dump_(filename):
        """ dump file content """
        height, width = get_screen_size()
        if tools.console.Console.is_redirected() is False:
                width = (width - 12)//4
        else:
                width = 16
        offset = 0
        file = open(filename, "rb")
        data = b' '
        count = 1
        while True:
                line = io.BytesIO()
                line.write(b'%08X  ' % offset)
                data = file.read(width)
                if len(data) <= 0:
                        break
                tools.strings.dump_line (data, line, width)
                offset += width
                count = print_part(line.getvalue(), width, height, count)
                if count is None:
                        break

def cls():
        """ clear screen """
        tools.console.Console.print("\x1B[2J\x1B[0;0f", end="")

def check_cam_flasher():
        """ Check if the terminal is CamFlasher """
        # pylint:disable=global-variable-not-assigned
        if tools.console.Console.is_redirected() is False:
                # Request terminal device attribut
                sys.stdout.write(b"\x1B[0c")

                # Wait terminal device attribut response
                response = tools.terminal.getch(duration=1000)

                # If CamFlasher detected
                if response == "\x1B[?3;2c":
                        return True
        return False

def upload(file="", recursive=False):
        """ Upload file from computer to device """
        if check_cam_flasher():
                tools.console.Console.print("Upload to device start")
                try:
                        command = tools.exchange.UploadCommand(uos.getcwd())
                        command.write(file, recursive, sys.stdin.buffer, sys.stdout.buffer)
                        result = True
                        while result:
                                file_reader = tools.exchange.FileReader()
                                result = file_reader.read(uos.getcwd(), sys.stdin.buffer, sys.stdout.buffer)
                                tools.watchdog.WatchDog.feed()
                        tools.console.Console.print("Upload end")
                except Exception as err:
                        tools.logger.syslog(err, display=False)
                        tools.console.Console.print("Upload failed")
        else:
                tools.console.Console.print("CamFlasher application required for this command")

class Exporter:
        """ Exporter file to camflasher """
        def __init__(self):
                """ Constructor """

        def send_file(self, path):
                """ Send the file """
                result = True
                fileinfo = tools.filesystem.fileinfo(path)

                # If a file
                if fileinfo[0] & 0x4000 != 0x4000:
                        file_write = tools.exchange.FileWriter()
                        if tools.filesystem.exists(path):
                                sys.stdout.buffer.write("࿊".encode("utf8"))
                                result = file_write.write(path, sys.stdin.buffer, sys.stdout.buffer)
                                tools.watchdog.WatchDog.feed()
                return result

        def show(self, path):
                """ Show the information of a file or directory """
                for _ in range(3):
                        # If the send successful exit, else retry three time
                        if self.send_file(path) is True:
                                break

        def show_dir(self, state):
                """ Indicates if the directory must show """

def download(file="", recursive=False):
        """ Download file from device to computer """
        if check_cam_flasher():
                tools.console.Console.print("Download from device start")
                try:
                        searchfile(file, recursive, Exporter())
                        tools.console.Console.print("Download end")
                except Exception as err:
                        tools.logger.syslog(err, display=False)
                        tools.console.Console.print("Download failed")
        else:
                tools.console.Console.print("CamFlasher application required for this command")

def temperature():
        """ Get the internal temperature """
        celcius, farenheit = tools.info.temperature()
        tools.console.Console.print("%.2f°C, %d°F"%(celcius, farenheit))

def meminfo():
        """ Get memory informations """
        tools.console.Console.print(tools.strings.tostrings(b"%s : %s"%(tools.lang.memory_label, tools.info.meminfo())))

def flashinfo(mountpoint=None):
        """ Get flash informations """
        tools.console.Console.print(tools.strings.tostrings(b"%s : %s"%(tools.lang.flash_label, tools.info.flashinfo(mountpoint=mountpoint))))

def sysinfo():
        """ Get system informations """
        tools.console.Console.print(tools.strings.tostrings(tools.info.sysinfo()))

def execute(python_string):
        """ Execute python string """
        exec(python_string)

def evaluate(python_string):
        """ Evaluate python string """
        print(eval(python_string))

def vtcolors():
        """ Show all VT100 colors """
        res = b'\x1B[4m4 bits colors\x1B[m\n'
        for i in range(16):
                if i % 8 == 0:
                        res += b"\n  "
                if i < 9:
                        forecolor = 15
                else:
                        forecolor = 0
                res += b"\x1B[38;5;%dm\x1B[48;5;%dm %2d \x1B[0m"%(forecolor, i,i)
        res += b'\n\n\x1B[4m8 bits colors\x1B[m\n'
        j = 0
        for i in range(16,256):
                if j % 12== 0:
                        res += b"\n  "
                backcolor = i
                if j % 36 < 36//2:
                        forecolor = 15
                else:
                        forecolor = 0
                res += b"\x1B[38;5;%dm\x1B[48;5;%dm %3d \x1B[0m"%(forecolor,backcolor,i)
                j += 1
        res += b'\n\n\x1B[4mModifiers\x1B[m\n\n'

        for i,j in [(0,"reset/normal"),(1,b"bold"),(3,b"italic"),(4,b"underline"),(7,b"reverse")]:
                res += b"  %d : \x1B[%dm%s\x1B[0m\n"%(i,i,j)
        res += b'\n\x1B[4mExamples\x1B[m\n\n'
        res += b'  >>> print("\\033[\033[1m1\033[m;\033[7m7\033[mmBold reverse\\033[0m")\n'
        res += b"  \033[1;7mBold reverse\033[0m"
        res += b"\n\n"
        res += b'  >>> print("\033[38;5;15m\033[48;5;1m\\033[48;5;1m\033[m\033[38;5;13m\\033[38;5;13m\033[mHello\\033[m")\n'
        res += b"  \033[48;5;1m\033[38;5;13mHello\033[m\n"

        tools.console.Console.print(res.decode("utf8"))

def get_command(command_name):
        """ Get a command callback according to the command name """
        try:
                # pylint:disable=global-variable-not-assigned
                global shell_commands
                command = shell_commands[command_name]
                command_function = command[0]
                command_params = []
                command_flags  = []
                for item in command[1:]:
                        if type(item) == type(""):
                                command_params.append(item)
                        if type(item) == type((0,)):
                                command_flags.append(item)
        except  Exception as err:
                # pylint: disable=raise-missing-from
                raise RuntimeError("Command not found '%s'"%command_name)
        return command_name, command_function, command_params, command_flags

def exec_command(args):
        """ Execute command """
        # pylint:disable=global-variable-not-assigned
        command_name = ""
        command_function = None
        command_params = []
        command_flags = []
        output_redirection = None
        output_filename = None
        try:
                if len(args) >= 1:
                        paramsCount = 0
                        flags   = {}
                        for arg in args:
                                arg = arg.strip()
                                if len(arg) > 0:
                                        if command_name == "":
                                                command_name, command_function, command_params, command_flags = get_command(arg)
                                        else:
                                                if len(arg) >= 2 and arg[:2] == "--":
                                                        for commandFlag in command_flags:
                                                                if arg.strip()[2:] == commandFlag[1].strip():
                                                                        flags[commandFlag[1]] = commandFlag[2]
                                                                        break
                                                        else:
                                                                raise RuntimeError("Illegal option '%s'"%arg)
                                                elif arg[0] == "-":
                                                        for commandFlag in command_flags:
                                                                if arg.strip() == commandFlag[0].strip():
                                                                        flags[commandFlag[1]] = commandFlag[2]
                                                                        break
                                                        else:
                                                                raise RuntimeError("Illegal option '%s'"%arg)
                                                elif arg[0] == ">":
                                                        output_redirection = True
                                                else:
                                                        if output_redirection is None:
                                                                if paramsCount >= len(command_params):
                                                                        raise RuntimeError("Too many parameters on '%s'"%command_name)
                                                                else:
                                                                        flags[command_params[paramsCount]] = arg
                                                                        paramsCount += 1
                                                        elif output_redirection is True:
                                                                output_filename = arg

        except Exception as err:
                tools.console.Console.print(err)
                return
        tools.console.Console.close()
        try:
                if command_name.strip() != "":
                        if output_filename is not None:
                                try:
                                        tools.console.Console.open(output_filename)
                                except:
                                        pass
                        command_function(**flags)
        except TypeError as err:
                tools.logger.syslog(err, display=False)
                tools.console.Console.print("Missing parameters for '%s'"%command_name)
        except KeyboardInterrupt as err:
                tools.logger.syslog(err)
                tools.console.Console.print(" [Canceled]")
        except Exception as err:
                tools.logger.syslog(err)
        finally:
                tools.console.Console.close()

def parse_command_line(commandLine):
        """ Parse command line """
        commands_ = []
        args = []
        quote = None
        arg = ""
        for char in commandLine:
                if char == '"' or char == "'":
                        if quote is not None:
                                if quote == char:
                                        args.append(arg)
                                        arg = ""
                                        quote = None
                                else:
                                        arg += char
                        else:
                                quote = char
                elif char == " ":
                        if quote is not None:
                                arg += char
                        else:
                                args.append(arg)
                                arg = ""
                elif char == ";":
                        if quote is not None:
                                arg += char
                        else:
                                if len(arg) > 0:
                                        args.append(arg)
                                commands_.append(args)
                                arg = ""
                                args = []
                else:
                        arg += char
        if len(arg) > 0:
                args.append(arg)
        if len(args) > 0:
                commands_.append(args)

        for command in commands_:
                exec_command(command)

def commands(path=None, throw=False):
        """ Start the shell """
        global shell_exited, shell_commands
        current_dir = uos.getcwd()

        create_shell_commands()

        if path is not None:
                uos.chdir(path)

        shell_exited = False
        while shell_exited is False:
                try:
                        commandLine = ""
                        commandLine = input("%s=> "%os.getcwd())
                        tools.watchdog.WatchDog.feed()
                except EOFError:
                        tools.console.Console.print("")
                        break
                except KeyboardInterrupt:
                        tools.console.Console.print("Ctr-C detected, use 'exit' to restart server or 'quit' to get python prompt")

                if commandLine.strip() == "quit":
                        if throw is True:
                                raise KeyboardInterrupt()
                        else:
                                break
                parse_command_line(commandLine)

        shell_commands = None
        uos.chdir(current_dir)

def create_shell_commands():
        """ Return the shell commands """
        global shell_commands
        shell_commands = \
        {
                "cd"         :[cd              ,"directory"            ],
                "pwd"        :[pwd                                     ],
                "cat"        :[cat             ,"file"                 ],
                "cls"        :[cls                                     ],
                "mkdir"      :[mkdir           ,"directory",             ("-r","recursive",True)],
                "mv"         :[mv              ,"source","destination",  ("-q","quiet",True) ],
                "rmdir"      :[rmdir           ,"directory",             ("-r","recursive",True),("-f","force",True),("-q","quiet",True),("-s","simulate",True)],
                "cp"         :[cp              ,"source","destination",  ("-r","recursive",True),("-q","quiet",True)],
                "rm"         :[rm              ,"file",                  ("-r","recursive",True),("-f","force",True),("-s","simulate",True)],
                "ls"         :[ls              ,"file",                  ("-r","recursive",True),("-l","long",True)],
                "ll"         :[ll              ,"file",                  ("-r","recursive",True)],
                "date"       :[date_time       ,"offsetUTC" ,            ("-u","update",True),   ("-n","noDst",True)],
                "setdate"    :[setdate         ,"datetime"             ],
                "uptime"     :[uptime                                  ],
                "find"       :[find            ,"file"                 ],
                "run"        :[tools.useful.run      ,"filename"             ],
                "download"   :[download        ,"file",                  ("-r","recursive",True)],
                "upload"     :[upload          ,"file",                  ("-r","recursive",True)],
                "edit"       :[edit            ,"file",                  ("-n","no_color",True),("-r","read_only",True)],
                "exit"       :[exit                                    ],
                "exec"       :[execute         ,"python_string"        ],
                "eval"       :[evaluate        ,"python_string"        ],
                "gc"         :[gc                                      ],
                "grep"       :[grep            ,"text","file",           ("-r","recursive",True),("-i","ignorecase",True),("-e","regexp",True)],
                "mount"      :[mountsd         ,"mountpoint"           ],
                "umount"     :[umountsd        ,"mountpoint"           ],
                "temperature":[temperature                             ],
                "meminfo"    :[meminfo                                 ],
                "flashinfo"  :[flashinfo                               ],
                "sysinfo"    :[sysinfo                                 ],
                "deepsleep"  :[deepsleep       ,"seconds"              ],
                "lightsleep" :[ligthsleep      ,"seconds"              ],
                "ping"       :[ping            ,"host"                 ],
                "reboot"     :[reboot                                  ],
                "help"       :[help                                    ],
                "man"        :[man             ,"command"              ],
                "memdump"    :[tools.info.memdump                      ],
                "df"         :[df              ,"mountpoint"           ],
                "ip2host"    :[ip2host         ,"ip_address"           ],
                "host2ip"    :[host2ip         ,"hostname"             ],
                "eval"       :[eval_           ,"string"               ],
                "exec"       :[exec_           ,"string"               ],
                "dump"       :[dump_           ,"filename"             ],
                "formatsd"   :[formatsd        ,"fstype"               ],
                "cleanupsd"  :[cleanupsd                               ],
                "vtcolors"   :[vtcolors                                ],
        }

if __name__ == "__main__":
        commands(sys.argv[1])

Functions

def cat(file)

Cat command

Expand source code
def cat(file):
        """ Cat command """
        try:
                f = open(file, "r")
                height, width = get_screen_size()
                count = 1
                while 1:
                        line = f.readline()
                        if not line:
                                break
                        message = line.replace("\t","    ").rstrip()[:width]
                        count = print_part(message, width, height, count)
                        if count is None:
                                break
                f.close()
        except:
                tools.console.Console.print("Cannot cat '%s'"%(file))
def cd(directory='/')

Change directory

Expand source code
def cd(directory = "/"):
        """ Change directory """
        try:
                uos.chdir(tools.filesystem.normpath(directory))
        except:
                if directory != ".":
                        tools.console.Console.print("No such file or directory '%s'"%directory)
def check_cam_flasher()

Check if the terminal is CamFlasher

Expand source code
def check_cam_flasher():
        """ Check if the terminal is CamFlasher """
        # pylint:disable=global-variable-not-assigned
        if tools.console.Console.is_redirected() is False:
                # Request terminal device attribut
                sys.stdout.write(b"\x1B[0c")

                # Wait terminal device attribut response
                response = tools.terminal.getch(duration=1000)

                # If CamFlasher detected
                if response == "\x1B[?3;2c":
                        return True
        return False
def cleanupsd()

Remove not useful directory and file from sd card

Expand source code
def cleanupsd():
        """ Remove not useful directory and file from sd card """
        if tools.sdcard.SdCard.is_mounted():
                cd(tools.sdcard.SdCard.get_mountpoint())
                rm   (".Spotlight-V100", recursive=True)
                rm   (".fseventsd",      recursive=True)
                rm   (".Trashes",       recursive=True)
                rm   ("RECYCLER",        recursive=True)
                rm   ("/._*",            recursive=True)
                rm   ("/.DS_Store",      recursive=True)
                rm   ("System Volume Information",        recursive=True)
        else:
                tools.console.Console.print("Sd card not mounted")
def cls()

clear screen

Expand source code
def cls():
        """ clear screen """
        tools.console.Console.print("\x1B[2J\x1B[0;0f", end="")
def commands(path=None, throw=False)

Start the shell

Expand source code
def commands(path=None, throw=False):
        """ Start the shell """
        global shell_exited, shell_commands
        current_dir = uos.getcwd()

        create_shell_commands()

        if path is not None:
                uos.chdir(path)

        shell_exited = False
        while shell_exited is False:
                try:
                        commandLine = ""
                        commandLine = input("%s=> "%os.getcwd())
                        tools.watchdog.WatchDog.feed()
                except EOFError:
                        tools.console.Console.print("")
                        break
                except KeyboardInterrupt:
                        tools.console.Console.print("Ctr-C detected, use 'exit' to restart server or 'quit' to get python prompt")

                if commandLine.strip() == "quit":
                        if throw is True:
                                raise KeyboardInterrupt()
                        else:
                                break
                parse_command_line(commandLine)

        shell_commands = None
        uos.chdir(current_dir)
def copyfile(src, dst, quiet)

Copy file

Expand source code
def copyfile(src,dst,quiet):
        """ Copy file """
        dst = dst.replace("//","/")
        dst = dst.replace("//","/")
        dstdir, dstfile = tools.filesystem.split(dst)
        try:
                if not tools.filesystem.exists(dstdir):
                        if dstdir != "." and dstdir != "":
                                mkdir(dstdir, recursive=True, quiet=quiet)
                src_file = open(src, 'rb')
                dst_file = open(dst, 'wb')
                if quiet is False:
                        tools.console.Console.print("cp '%s' -> '%s'"%(src,dst))
                while True:
                        buf = src_file.read(256)
                        if len(buf) > 0:
                                dst_file.write(buf)
                        if len(buf) < 256:
                                break
                src_file.close()
                dst_file.close()
        except:
                tools.console.Console.print("Cannot cp '%s' -> '%s'"%(src, dst))
def cp(source, destination, recursive=False, quiet=False)

Copy file command

Expand source code
def cp(source, destination, recursive=False, quiet=False):
        """ Copy file command """
        source = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)
        if tools.filesystem.isfile(source):
                copyfile(source,destination,quiet)
        else:
                if tools.filesystem.isdir(source):
                        path = source
                        pattern = "*"
                else:
                        path, pattern = tools.filesystem.split(source)

                _, filenames = tools.filesystem.scandir(path, pattern, recursive)

                for src in filenames:
                        dst = destination + "/" + src[len(path):]
                        copyfile(src,dst,quiet)
def create_shell_commands()

Return the shell commands

Expand source code
def create_shell_commands():
        """ Return the shell commands """
        global shell_commands
        shell_commands = \
        {
                "cd"         :[cd              ,"directory"            ],
                "pwd"        :[pwd                                     ],
                "cat"        :[cat             ,"file"                 ],
                "cls"        :[cls                                     ],
                "mkdir"      :[mkdir           ,"directory",             ("-r","recursive",True)],
                "mv"         :[mv              ,"source","destination",  ("-q","quiet",True) ],
                "rmdir"      :[rmdir           ,"directory",             ("-r","recursive",True),("-f","force",True),("-q","quiet",True),("-s","simulate",True)],
                "cp"         :[cp              ,"source","destination",  ("-r","recursive",True),("-q","quiet",True)],
                "rm"         :[rm              ,"file",                  ("-r","recursive",True),("-f","force",True),("-s","simulate",True)],
                "ls"         :[ls              ,"file",                  ("-r","recursive",True),("-l","long",True)],
                "ll"         :[ll              ,"file",                  ("-r","recursive",True)],
                "date"       :[date_time       ,"offsetUTC" ,            ("-u","update",True),   ("-n","noDst",True)],
                "setdate"    :[setdate         ,"datetime"             ],
                "uptime"     :[uptime                                  ],
                "find"       :[find            ,"file"                 ],
                "run"        :[tools.useful.run      ,"filename"             ],
                "download"   :[download        ,"file",                  ("-r","recursive",True)],
                "upload"     :[upload          ,"file",                  ("-r","recursive",True)],
                "edit"       :[edit            ,"file",                  ("-n","no_color",True),("-r","read_only",True)],
                "exit"       :[exit                                    ],
                "exec"       :[execute         ,"python_string"        ],
                "eval"       :[evaluate        ,"python_string"        ],
                "gc"         :[gc                                      ],
                "grep"       :[grep            ,"text","file",           ("-r","recursive",True),("-i","ignorecase",True),("-e","regexp",True)],
                "mount"      :[mountsd         ,"mountpoint"           ],
                "umount"     :[umountsd        ,"mountpoint"           ],
                "temperature":[temperature                             ],
                "meminfo"    :[meminfo                                 ],
                "flashinfo"  :[flashinfo                               ],
                "sysinfo"    :[sysinfo                                 ],
                "deepsleep"  :[deepsleep       ,"seconds"              ],
                "lightsleep" :[ligthsleep      ,"seconds"              ],
                "ping"       :[ping            ,"host"                 ],
                "reboot"     :[reboot                                  ],
                "help"       :[help                                    ],
                "man"        :[man             ,"command"              ],
                "memdump"    :[tools.info.memdump                      ],
                "df"         :[df              ,"mountpoint"           ],
                "ip2host"    :[ip2host         ,"ip_address"           ],
                "host2ip"    :[host2ip         ,"hostname"             ],
                "eval"       :[eval_           ,"string"               ],
                "exec"       :[exec_           ,"string"               ],
                "dump"       :[dump_           ,"filename"             ],
                "formatsd"   :[formatsd        ,"fstype"               ],
                "cleanupsd"  :[cleanupsd                               ],
                "vtcolors"   :[vtcolors                                ],
        }
def date_time(update=False, offsetUTC=1, noDst=False)

Get or set date

Expand source code
def date_time(update=False, offsetUTC=+1, noDst=False):
        """ Get or set date """
        try:
                from server.timesetting import set_date
                if update:
                        if noDst:
                                dst = False
                        else:
                                dst = True
                        set_date(offsetUTC, dst)
                del sys.modules["server.timesetting"]
        except:
                pass
        tools.console.Console.print(tools.date.date_to_string())
def deepsleep(seconds=60)

Deep sleep command

Expand source code
def deepsleep(seconds=60):
        """ Deep sleep command """
        machine.deepsleep(int(seconds)*1000)
def df(mountpoint=None)

Display free disk space

Expand source code
def df(mountpoint = None):
        """ Display free disk space """
        tools.console.Console.print(tools.strings.tostrings(tools.info.flashinfo(mountpoint=mountpoint)))
def download(file='', recursive=False)

Download file from device to computer

Expand source code
def download(file="", recursive=False):
        """ Download file from device to computer """
        if check_cam_flasher():
                tools.console.Console.print("Download from device start")
                try:
                        searchfile(file, recursive, Exporter())
                        tools.console.Console.print("Download end")
                except Exception as err:
                        tools.logger.syslog(err, display=False)
                        tools.console.Console.print("Download failed")
        else:
                tools.console.Console.print("CamFlasher application required for this command")
def dump_(filename)

dump file content

Expand source code
def dump_(filename):
        """ dump file content """
        height, width = get_screen_size()
        if tools.console.Console.is_redirected() is False:
                width = (width - 12)//4
        else:
                width = 16
        offset = 0
        file = open(filename, "rb")
        data = b' '
        count = 1
        while True:
                line = io.BytesIO()
                line.write(b'%08X  ' % offset)
                data = file.read(width)
                if len(data) <= 0:
                        break
                tools.strings.dump_line (data, line, width)
                offset += width
                count = print_part(line.getvalue(), width, height, count)
                if count is None:
                        break
def edit(file, no_color=False, read_only=False)

Edit command

Expand source code
def edit(file, no_color=False, read_only=False):
        """ Edit command """
        # pylint:disable=global-variable-not-assigned
        global edit_class
        if tools.console.Console.is_redirected() is False:
                if edit_class is None:
                        try:
                                from shell.editor import Editor
                        except:
                                from editor import Editor
                        edit_class = Editor
                edit_class(file, no_color=no_color, read_only=read_only)
def eval_(string)

Evaluate content of string

Expand source code
def eval_(string):
        """ Evaluate content of string """
        tools.console.Console.print(eval(string))
def evaluate(python_string)

Evaluate python string

Expand source code
def evaluate(python_string):
        """ Evaluate python string """
        print(eval(python_string))
def exec_(string)

Execute content of string

Expand source code
def exec_(string):
        """ Execute content of string """
        exec(string)
def exec_command(args)

Execute command

Expand source code
def exec_command(args):
        """ Execute command """
        # pylint:disable=global-variable-not-assigned
        command_name = ""
        command_function = None
        command_params = []
        command_flags = []
        output_redirection = None
        output_filename = None
        try:
                if len(args) >= 1:
                        paramsCount = 0
                        flags   = {}
                        for arg in args:
                                arg = arg.strip()
                                if len(arg) > 0:
                                        if command_name == "":
                                                command_name, command_function, command_params, command_flags = get_command(arg)
                                        else:
                                                if len(arg) >= 2 and arg[:2] == "--":
                                                        for commandFlag in command_flags:
                                                                if arg.strip()[2:] == commandFlag[1].strip():
                                                                        flags[commandFlag[1]] = commandFlag[2]
                                                                        break
                                                        else:
                                                                raise RuntimeError("Illegal option '%s'"%arg)
                                                elif arg[0] == "-":
                                                        for commandFlag in command_flags:
                                                                if arg.strip() == commandFlag[0].strip():
                                                                        flags[commandFlag[1]] = commandFlag[2]
                                                                        break
                                                        else:
                                                                raise RuntimeError("Illegal option '%s'"%arg)
                                                elif arg[0] == ">":
                                                        output_redirection = True
                                                else:
                                                        if output_redirection is None:
                                                                if paramsCount >= len(command_params):
                                                                        raise RuntimeError("Too many parameters on '%s'"%command_name)
                                                                else:
                                                                        flags[command_params[paramsCount]] = arg
                                                                        paramsCount += 1
                                                        elif output_redirection is True:
                                                                output_filename = arg

        except Exception as err:
                tools.console.Console.print(err)
                return
        tools.console.Console.close()
        try:
                if command_name.strip() != "":
                        if output_filename is not None:
                                try:
                                        tools.console.Console.open(output_filename)
                                except:
                                        pass
                        command_function(**flags)
        except TypeError as err:
                tools.logger.syslog(err, display=False)
                tools.console.Console.print("Missing parameters for '%s'"%command_name)
        except KeyboardInterrupt as err:
                tools.logger.syslog(err)
                tools.console.Console.print(" [Canceled]")
        except Exception as err:
                tools.logger.syslog(err)
        finally:
                tools.console.Console.close()
def execute(python_string)

Execute python string

Expand source code
def execute(python_string):
        """ Execute python string """
        exec(python_string)
def exit()

Exit shell command

Expand source code
def exit():
        """ Exit shell command """
        global shell_exited
        shell_exited = True
def find(file)

Find a file in directories

Expand source code
def find(file):
        """ Find a file in directories """
        filenames = searchfile(file, True)
        for filename in filenames:
                tools.console.Console.print(filename)
def flashinfo(mountpoint=None)

Get flash informations

Expand source code
def flashinfo(mountpoint=None):
        """ Get flash informations """
        tools.console.Console.print(tools.strings.tostrings(b"%s : %s"%(tools.lang.flash_label, tools.info.flashinfo(mountpoint=mountpoint))))
def formatsd(fstype='FAT')

Format sd card

Expand source code
def formatsd(fstype="FAT"):
        """ Format sd card """
        if fstype in ["FAT","LFS"]:
                if tools.sdcard.SdCard.is_mounted() is False:
                        res = input("All data will be lost on Sd card ? proceed with format (y/n) :")
                        if res in ["y","Y"]:
                                if tools.sdcard.SdCard.formatsd() is True:
                                        tools.console.Console.print("Formatting terminated")
                                else:
                                        tools.console.Console.print("Formatting failed")
                else:
                        tools.console.Console.print("Sd card is mounted, a reboot required")
        else:
                tools.console.Console.print("Filesystem supported : FAT or LFS")
def gc()

Garbage collector command

Expand source code
def gc():
        """ Garbage collector command """
        from gc import collect
        collect()
def get_command(command_name)

Get a command callback according to the command name

Expand source code
def get_command(command_name):
        """ Get a command callback according to the command name """
        try:
                # pylint:disable=global-variable-not-assigned
                global shell_commands
                command = shell_commands[command_name]
                command_function = command[0]
                command_params = []
                command_flags  = []
                for item in command[1:]:
                        if type(item) == type(""):
                                command_params.append(item)
                        if type(item) == type((0,)):
                                command_flags.append(item)
        except  Exception as err:
                # pylint: disable=raise-missing-from
                raise RuntimeError("Command not found '%s'"%command_name)
        return command_name, command_function, command_params, command_flags
def get_screen_size()

Return the screen size and check if output redirected

Expand source code
def get_screen_size():
        """ Return the screen size and check if output redirected """
        # pylint:disable=global-variable-not-assigned
        if tools.console.Console.is_redirected() is False:
                height, width = tools.terminal.get_screen_size()
        else:
                height, width = tools.terminal.MAXINT, 80
        return height, width
def grep(file, text, recursive=False, ignorecase=False, regexp=False)

Grep command

Expand source code
def grep(file, text, recursive=False, ignorecase=False, regexp=False):
        """ Grep command """
        from re import search
        file = tools.filesystem.normpath(file)
        def __search(text, content, ignorecase, regexp):
                if ignorecase:
                        content  = content.lower()
                        text = text.lower()
                if regexp:
                        if search(text, content):
                                return True
                else:
                        if content.find(text) != -1:
                                return True
                return False

        def __grep(text, filename, ignorecase, regexp, width, height, count):
                lineNumber = 1
                with open(filename,"r", encoding="latin-1") as f:
                        while 1:
                                line = f.readline()
                                if line:
                                        if __search(text, line, ignorecase, regexp):
                                                line = line.replace("\t","    ")
                                                message = "%s:%d:%s"%(filename, lineNumber, line)
                                                message = message.rstrip()[:width]
                                                count = print_part(message, width, height, count)
                                                if count is None:
                                                        tools.console.Console.print("")
                                                        return None
                                        lineNumber += 1
                                else:
                                        break
                return count

        if tools.filesystem.isfile(file):
                filenames = [file]
        else:
                path, pattern = tools.filesystem.split(file)
                _, filenames = tools.filesystem.scandir(path, pattern, recursive)

        height, width = get_screen_size()
        count = 1
        for filename in filenames:
                count = __grep(text, filename, ignorecase, regexp, width, height, count)
                if count is None:
                        break
def help()

Help command

Expand source code
def help():
        """ Help command """
        height, width = get_screen_size()
        count = 1
        cmds = list(shell_commands.keys())
        cmds.sort()
        for command in cmds:
                lines = man_one(command)
                lines = "-"*30+"\n" + lines
                for line in lines.split("\n"):
                        count = print_part(line, width, height, count)
                        if count is None:
                                return
def host2ip(hostname)

Convert hostname to ip

Expand source code
def host2ip(hostname):
        """ Convert hostname to ip """
        try:
                import wifi.station
                _, _, _, dns = wifi.station.Station.get_info()
                from server.dnsclient import resolve_ip_address
                tools.console.Console.print(resolve_ip_address(dns, hostname))
        except:
                tools.console.Console.print("Not available")
def ip2host(ip_address)

Convert ip to hostname

Expand source code
def ip2host(ip_address):
        """ Convert ip to hostname """
        try:
                import wifi.station
                _, _, _, dns = wifi.station.Station.get_info()
                from server.dnsclient import resolve_hostname
                tools.console.Console.print(resolve_hostname(dns, ip_address))
        except:
                tools.console.Console.print("Not available")
def ligthsleep(seconds=60)

Light sleep command

Expand source code
def ligthsleep(seconds=60):
        """ Light sleep command """
        machine.lightsleep(int(seconds)*1000)
def ll(file='', recursive=False)

List files long command

Expand source code
def ll(file="", recursive=False):
        """ List files long command """
        searchfile(file, recursive, Displayer(uos.getcwd(), True))
def ls(file='', recursive=False, long=False)

List files command

Expand source code
def ls(file="", recursive=False, long=False):
        """ List files command """
        searchfile(file, recursive, Displayer(uos.getcwd(), long))
def man(command)

Man command

Expand source code
def man(command):
        """ Man command """
        tools.console.Console.print(man_one(command))
def man_one(command_name)

Manual of one command

Expand source code
def man_one(command_name):
        """ Manual of one command """
        try:
                command_name, command_function, command_params, command_flags = get_command(command_name)
                text = "  " + command_name + " "
                for param in command_params:
                        text += param + " "
                text += "\n"
                for flag,flagName,val in command_flags:
                        text += "    %s : %s\n"%(flag,flagName)
                result = text[:-1]
        except:
                result = "Unknown command '%s'"%command_name
        return result
def meminfo()

Get memory informations

Expand source code
def meminfo():
        """ Get memory informations """
        tools.console.Console.print(tools.strings.tostrings(b"%s : %s"%(tools.lang.memory_label, tools.info.meminfo())))
def mkdir(directory, recursive=False, quiet=False)

Make directory

Expand source code
def mkdir(directory, recursive=False, quiet=False):
        """ Make directory """
        try:
                if quiet is False:
                        tools.console.Console.print("mkdir '%s'"%directory)
                tools.filesystem.makedir(tools.filesystem.normpath(directory), recursive)
        except:
                tools.console.Console.print("Cannot mkdir '%s'"%directory)
def mountsd(mountpoint='/sd')

Mount command

Expand source code
def mountsd(mountpoint="/sd"):
        """ Mount command """
        try:
                tools.sdcard.SdCard.mount(mountpoint)
                tools.console.Console.print("Sd mounted on '%s'"%mountpoint)
        except:
                tools.console.Console.print("Cannot mount sd on '%s'"%mountpoint)
def movefile(source, destination, quiet)

Move file

Expand source code
def movefile(source, destination, quiet):
        """ Move file """
        source      = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)
        if quiet is False:
                tools.console.Console.print("mv '%s' -> '%s'"%(source,destination))
        uos.rename(source,destination)
def mv(source, destination, quiet=False)

Move or rename file

Expand source code
def mv(source, destination, quiet=False):
        """ Move or rename file """
        source      = tools.filesystem.normpath(source)
        destination = tools.filesystem.normpath(destination)

        try:
                if tools.filesystem.isfile(source):
                        movefile(source,destination,quiet)
                else:
                        if tools.filesystem.isdir(source):
                                # If destination existing
                                if tools.filesystem.exists(destination):
                                        # If destination is file
                                        if tools.filesystem.isfile(destination):
                                                raise IOError()
                                        else:
                                                # Move source into destination directory
                                                destination = destination + "/" + tools.filesystem.split(source)[1]
                                                movefile(tools.filesystem.normpath(source),tools.filesystem.normpath(destination), quiet)
                                else:
                                        # Rename
                                        movefile(tools.filesystem.normpath(source),tools.filesystem.normpath(destination), quiet)
                        else:
                                # Move many files
                                path, pattern = tools.filesystem.split(source)

                                if path == "":
                                        path = uos.getcwd()

                                _, filenames = tools.filesystem.scandir(path, pattern, False)

                                for src in filenames:
                                        dst = destination + "/" + src[len(path):]
                                        movefile(src,dst,quiet)
        except:
                tools.console.Console.print("Cannot mv '%s'->'%s'"%(source,destination))
def parse_command_line(commandLine)

Parse command line

Expand source code
def parse_command_line(commandLine):
        """ Parse command line """
        commands_ = []
        args = []
        quote = None
        arg = ""
        for char in commandLine:
                if char == '"' or char == "'":
                        if quote is not None:
                                if quote == char:
                                        args.append(arg)
                                        arg = ""
                                        quote = None
                                else:
                                        arg += char
                        else:
                                quote = char
                elif char == " ":
                        if quote is not None:
                                arg += char
                        else:
                                args.append(arg)
                                arg = ""
                elif char == ";":
                        if quote is not None:
                                arg += char
                        else:
                                if len(arg) > 0:
                                        args.append(arg)
                                commands_.append(args)
                                arg = ""
                                args = []
                else:
                        arg += char
        if len(arg) > 0:
                args.append(arg)
        if len(args) > 0:
                commands_.append(args)

        for command in commands_:
                exec_command(command)
def ping(host)

Ping host

Expand source code
def ping(host):
        """ Ping host """
        try:
                from server.ping import ping as ping_
                ping_(host, count=4, timeout=1)
        except:
                tools.console.Console.print("Not available")
def print_part(message, width, height, count)

Print a part of text

Expand source code
def print_part(message, width, height, count):
        """ Print a part of text """
        # pylint:disable=global-variable-not-assigned
        if isinstance(message , bytes):
                message = message.decode("utf8")
        if count is not None and count >= height:
                tools.console.Console.print(message,end="")
                if tools.console.Console.is_redirected() is False:
                        key = tools.terminal.getch()
                else:
                        key = " "
                count = 1
                if key in ["x","X","q","Q","\x1B"]:
                        return None
                tools.console.Console.print("\n", end="")
        else:
                if count is None:
                        count = 1
                else:
                        count += 1
                tools.console.Console.print(message)
        return count
def pwd()

Display the current directory

Expand source code
def pwd():
        """ Display the current directory """
        tools.console.Console.print("%s"%uos.getcwd())
def reboot()

Reboot command

Expand source code
def reboot():
        """ Reboot command """
        try:
                tools.system.reboot("Reboot device with command")
        except:
                machine.deepsleep(1000)
def removedir(directory, force=False, quiet=False, simulate=False, ignore_error=False)

Remove directory

Expand source code
def removedir(directory, force=False, quiet=False, simulate=False, ignore_error=False):
        """ Remove directory """
        try:
                if tools.filesystem.exists(directory+"/.DS_Store"):
                        rmfile(directory+"/.DS_Store", quiet, force, simulate)
                if (tools.filesystem.ismicropython() or force) and simulate is False:
                        uos.rmdir(directory)
                if quiet is False:
                        tools.console.Console.print("rmdir '%s'"%(directory))
        except:
                if ignore_error is False:
                        tools.console.Console.print("rmdir '%s' not removed"%(directory))
def rm(file, recursive=False, quiet=False, force=False, simulate=False)

Remove file command

Expand source code
def rm(file, recursive=False, quiet=False, force=False, simulate=False):
        """ Remove file command """
        remover = Remover(file, recursive, quiet, force, simulate)
        searchfile(file, recursive, remover)
        remover.remove_dir()
def rmdir(directory, root=None, recursive=False, force=False, quiet=False, simulate=False, ignore_error=False)

Remove directory

Expand source code
def rmdir(directory, root=None, recursive=False, force=False, quiet=False, simulate=False, ignore_error=False):
        """ Remove directory """
        directory = tools.filesystem.normpath(directory)
        if recursive is False:
                removedir(directory, force=force, quiet=quiet, simulate=simulate, ignore_error=ignore_error)
        else:
                directories = [directory]
                d = directory
                while 1:
                        parts = tools.filesystem.split(d)
                        if parts[1] == "" or parts[0] == "":
                                break
                        if root:
                                if len(parts[0]) <= len(root):
                                        break
                        directories.append(parts[0])
                        d = parts[0]
                if "/" in directories:
                        directories.remove("/")
                if tools.sdcard.SdCard.get_mountpoint() in directories:
                        directories.remove(tools.sdcard.SdCard.get_mountpoint())
                for d in directories:
                        if tools.filesystem.exists(d) and d != ".":
                                removedir(d, force=force, quiet=quiet, simulate=simulate, ignore_error=ignore_error)
def rmfile(filename, quiet=False, force=False, simulate=False)

Remove file

Expand source code
def rmfile(filename, quiet=False, force=False, simulate=False):
        """ Remove file """
        try:
                if (tools.filesystem.ismicropython() or force) and simulate is False:
                        uos.remove(tools.filesystem.normpath(filename))
                if quiet is False:
                        tools.console.Console.print("rm '%s'"%(filename))
        except:
                tools.console.Console.print("rm '%s' not removed"%(filename))
def searchfile(file, recursive, obj=None)

Search file

Expand source code
def searchfile(file, recursive, obj = None):
        """ Search file """
        file = tools.filesystem.normpath(file)
        p = tools.filesystem.abspath(uos.getcwd(), file)
        filenames = []
        try:
                # If file not defined then current directory used
                if file == "":
                        _,filenames = tools.filesystem.scandir(uos.getcwd(), "*", recursive, obj)
                # If one file detected
                elif tools.filesystem.isfile(p):
                        if obj is not None:
                                obj.show_dir(False)
                                obj.show(p)
                        filenames = [p]
                # If directory detected
                elif tools.filesystem.isdir(p):
                        _, filenames = tools.filesystem.scandir(p, "*", recursive, obj)
                # Else pattern detected
                else:
                        path, pattern = tools.filesystem.split(p)
                        if pattern != "*":
                                if obj is not None:
                                        obj.show_dir(False)
                        _, filenames = tools.filesystem.scandir(path, pattern, recursive, obj)
        except Exception as err:
                tools.console.Console.print(err)
        if len(filenames) == 0 and file != "" and file != "." and tools.filesystem.isdir(file) is False:
                tools.console.Console.print("%s : No such file or directory"%file)
        return filenames
def setdate(datetime='')

Set date and time

Expand source code
def setdate(datetime=""):
        """ Set date and time """
        import re
        file_date=re.compile("[/: ]")
        failed = False
        try:
                spls = file_date.split(datetime)

                lst = []
                if len(spls) > 1:
                        for spl in spls:
                                if len(spl) > 0:
                                        try:
                                                r = spl.lstrip("0")
                                                if len(r) == 0:
                                                        lst.append(0)
                                                else:
                                                        lst.append(eval(r))
                                        except:
                                                failed = True
                                                break
                        if len(lst) == 6:
                                # pylint: disable=unbalanced-tuple-unpacking
                                year,month,day,hour,minute,second = lst
                                machine.RTC().datetime((year, month, day, 0, hour, minute, second, 0))
                        else:
                                failed = True
                else:
                        failed = True
        except Exception as err:
                failed = True
                tools.logger.syslog(err)

        if failed is True:
                tools.console.Console.print('Expected format "YYYY/MM/DD hh:mm:ss"')
def sysinfo()

Get system informations

Expand source code
def sysinfo():
        """ Get system informations """
        tools.console.Console.print(tools.strings.tostrings(tools.info.sysinfo()))
def temperature()

Get the internal temperature

Expand source code
def temperature():
        """ Get the internal temperature """
        celcius, farenheit = tools.info.temperature()
        tools.console.Console.print("%.2f°C, %d°F"%(celcius, farenheit))
def umountsd(mountpoint='/sd')

Umount command

Expand source code
def umountsd(mountpoint="/sd"):
        """ Umount command """
        try:
                tools.sdcard.SdCard.umount(mountpoint)
                tools.console.Console.print("Sd umounted from '%s'"%mountpoint)
        except:
                tools.console.Console.print("Cannot umount sd from '%s'"%mountpoint)
def upload(file='', recursive=False)

Upload file from computer to device

Expand source code
def upload(file="", recursive=False):
        """ Upload file from computer to device """
        if check_cam_flasher():
                tools.console.Console.print("Upload to device start")
                try:
                        command = tools.exchange.UploadCommand(uos.getcwd())
                        command.write(file, recursive, sys.stdin.buffer, sys.stdout.buffer)
                        result = True
                        while result:
                                file_reader = tools.exchange.FileReader()
                                result = file_reader.read(uos.getcwd(), sys.stdin.buffer, sys.stdout.buffer)
                                tools.watchdog.WatchDog.feed()
                        tools.console.Console.print("Upload end")
                except Exception as err:
                        tools.logger.syslog(err, display=False)
                        tools.console.Console.print("Upload failed")
        else:
                tools.console.Console.print("CamFlasher application required for this command")
def uptime()

Tell how long the system has been running

Expand source code
def uptime():
        """ Tell how long the system has been running """
        tools.console.Console.print(tools.strings.tostrings(tools.info.uptime()))
def vtcolors()

Show all VT100 colors

Expand source code
def vtcolors():
        """ Show all VT100 colors """
        res = b'\x1B[4m4 bits colors\x1B[m\n'
        for i in range(16):
                if i % 8 == 0:
                        res += b"\n  "
                if i < 9:
                        forecolor = 15
                else:
                        forecolor = 0
                res += b"\x1B[38;5;%dm\x1B[48;5;%dm %2d \x1B[0m"%(forecolor, i,i)
        res += b'\n\n\x1B[4m8 bits colors\x1B[m\n'
        j = 0
        for i in range(16,256):
                if j % 12== 0:
                        res += b"\n  "
                backcolor = i
                if j % 36 < 36//2:
                        forecolor = 15
                else:
                        forecolor = 0
                res += b"\x1B[38;5;%dm\x1B[48;5;%dm %3d \x1B[0m"%(forecolor,backcolor,i)
                j += 1
        res += b'\n\n\x1B[4mModifiers\x1B[m\n\n'

        for i,j in [(0,"reset/normal"),(1,b"bold"),(3,b"italic"),(4,b"underline"),(7,b"reverse")]:
                res += b"  %d : \x1B[%dm%s\x1B[0m\n"%(i,i,j)
        res += b'\n\x1B[4mExamples\x1B[m\n\n'
        res += b'  >>> print("\\033[\033[1m1\033[m;\033[7m7\033[mmBold reverse\\033[0m")\n'
        res += b"  \033[1;7mBold reverse\033[0m"
        res += b"\n\n"
        res += b'  >>> print("\033[38;5;15m\033[48;5;1m\\033[48;5;1m\033[m\033[38;5;13m\\033[38;5;13m\033[mHello\\033[m")\n'
        res += b"  \033[48;5;1m\033[38;5;13mHello\033[m\n"

        tools.console.Console.print(res.decode("utf8"))

Classes

class Displayer (path, long, action=<function print_part>)

Displayer file

Constructor

Expand source code
class Displayer:
        """ Displayer file """
        def __init__(self, path, long, action=print_part):
                """ Constructor """
                self.height, self.width = get_screen_size()
                self.path    = path
                self.showdir = True
                self.count   = 1
                self.long    = long
                self.action  = action

        def purge_path(self, path):
                """ Purge the path for the display """
                path = path.encode("utf8")
                path = tools.filesystem.normpath(path)
                prefix = tools.filesystem.prefix([path, self.path.encode("utf8")])
                return path[len(prefix):].lstrip(b"/")

        def get_info(self, path):
                """ Get info on file or directory """
                result    = None, False
                fileinfo  = tools.filesystem.fileinfo(path)
                file_date = fileinfo[8]
                size      = fileinfo[6]

                # If directory
                if fileinfo[0] & 0x4000 == 0x4000:
                        if self.showdir:
                                if self.long:
                                        message = b"%s %s [%s]"%(tools.date.date_to_bytes(file_date),b" "*7,self.purge_path(path))
                                else:
                                        message = b"[%s]"%self.purge_path(path)
                                result = message, True
                # If file
                else:
                        if self.long:
                                message = b"%s %s %s"%(tools.date.date_to_bytes(file_date),tools.strings.size_to_bytes(size),self.purge_path(path))
                        else:
                                message = self.purge_path(path)
                        result = message, False
                return result

        def show(self, path):
                """ Show the information of a file or directory """
                message, directory = self.get_info(path)
                if message is not None:
                        self.count = self.action(message, self.width, self.height, self.count)

        def show_dir(self, state):
                """ Indicates if the directory must show """
                self.showdir = state

Subclasses

Methods

def get_info(self, path)

Get info on file or directory

Expand source code
def get_info(self, path):
        """ Get info on file or directory """
        result    = None, False
        fileinfo  = tools.filesystem.fileinfo(path)
        file_date = fileinfo[8]
        size      = fileinfo[6]

        # If directory
        if fileinfo[0] & 0x4000 == 0x4000:
                if self.showdir:
                        if self.long:
                                message = b"%s %s [%s]"%(tools.date.date_to_bytes(file_date),b" "*7,self.purge_path(path))
                        else:
                                message = b"[%s]"%self.purge_path(path)
                        result = message, True
        # If file
        else:
                if self.long:
                        message = b"%s %s %s"%(tools.date.date_to_bytes(file_date),tools.strings.size_to_bytes(size),self.purge_path(path))
                else:
                        message = self.purge_path(path)
                result = message, False
        return result
def purge_path(self, path)

Purge the path for the display

Expand source code
def purge_path(self, path):
        """ Purge the path for the display """
        path = path.encode("utf8")
        path = tools.filesystem.normpath(path)
        prefix = tools.filesystem.prefix([path, self.path.encode("utf8")])
        return path[len(prefix):].lstrip(b"/")
def show(self, path)

Show the information of a file or directory

Expand source code
def show(self, path):
        """ Show the information of a file or directory """
        message, directory = self.get_info(path)
        if message is not None:
                self.count = self.action(message, self.width, self.height, self.count)
def show_dir(self, state)

Indicates if the directory must show

Expand source code
def show_dir(self, state):
        """ Indicates if the directory must show """
        self.showdir = state
class Exporter

Exporter file to camflasher

Constructor

Expand source code
class Exporter:
        """ Exporter file to camflasher """
        def __init__(self):
                """ Constructor """

        def send_file(self, path):
                """ Send the file """
                result = True
                fileinfo = tools.filesystem.fileinfo(path)

                # If a file
                if fileinfo[0] & 0x4000 != 0x4000:
                        file_write = tools.exchange.FileWriter()
                        if tools.filesystem.exists(path):
                                sys.stdout.buffer.write("࿊".encode("utf8"))
                                result = file_write.write(path, sys.stdin.buffer, sys.stdout.buffer)
                                tools.watchdog.WatchDog.feed()
                return result

        def show(self, path):
                """ Show the information of a file or directory """
                for _ in range(3):
                        # If the send successful exit, else retry three time
                        if self.send_file(path) is True:
                                break

        def show_dir(self, state):
                """ Indicates if the directory must show """

Methods

def send_file(self, path)

Send the file

Expand source code
def send_file(self, path):
        """ Send the file """
        result = True
        fileinfo = tools.filesystem.fileinfo(path)

        # If a file
        if fileinfo[0] & 0x4000 != 0x4000:
                file_write = tools.exchange.FileWriter()
                if tools.filesystem.exists(path):
                        sys.stdout.buffer.write("࿊".encode("utf8"))
                        result = file_write.write(path, sys.stdin.buffer, sys.stdout.buffer)
                        tools.watchdog.WatchDog.feed()
        return result
def show(self, path)

Show the information of a file or directory

Expand source code
def show(self, path):
        """ Show the information of a file or directory """
        for _ in range(3):
                # If the send successful exit, else retry three time
                if self.send_file(path) is True:
                        break
def show_dir(self, state)

Indicates if the directory must show

Expand source code
def show_dir(self, state):
        """ Indicates if the directory must show """
class Remover (file, recursive=False, quiet=False, force=False, simulate=False)

Class to remove files and directories

Constructor

Expand source code
class Remover(Displayer):
        """ Class to remove files and directories """
        def __init__(self, file, recursive=False, quiet=False, force=False, simulate=False):
                Displayer.__init__(self, uos.getcwd(), False)
                self.directories = set()
                self.path = uos.getcwd()
                self.file = file
                self.quiet = quiet
                self.force = force
                self.simulate = simulate
                self.recursive = recursive
                if recursive:
                        if tools.filesystem.isdir(self.path + "/" + file):
                                self.directories.add(self.path + "/" + file)

        def show(self, path):
                """ Show the information of a file or directory """
                message, directory = self.get_info(path)
                if message is not None:
                        if directory is False:
                                rmfile(path, force=self.force, quiet=self.quiet, simulate=self.simulate)
                if directory:
                        self.directories.add(path)

        def remove_dir(self):
                """ Remove directories """
                if self.recursive:
                        directories = list(self.directories)
                        directories.sort()
                        directories.reverse()
                        for directory in directories:
                                rmdir(directory, root=self.path, recursive=self.recursive, force=self.force, quiet=self.quiet, simulate=self.simulate, ignore_error=True)

Ancestors

Methods

def remove_dir(self)

Remove directories

Expand source code
def remove_dir(self):
        """ Remove directories """
        if self.recursive:
                directories = list(self.directories)
                directories.sort()
                directories.reverse()
                for directory in directories:
                        rmdir(directory, root=self.path, recursive=self.recursive, force=self.force, quiet=self.quiet, simulate=self.simulate, ignore_error=True)

Inherited members