A terminal for iOS, with multiple windows

Related tags

Utility a-shell
Overview

a-shell: A terminal for iOS, with multiple windows

Platform: iOS Twitter

The goal in this project is to provide a simple Unix-like terminal on iOS. It uses ios_system for command interpretation, and includes all commands from the ios_system ecosystem (nslookup, whois, python3, lua, pdflatex, lualatex...)

The project uses iPadOS 13 ability to create and manage multiple windows. Each window has its own context, appearance, command history and current directory. newWindow opens a new window, exit closes the current window.

For help, type help in the command line. help -l lists all the available commands. help -l | grep command will tell you if your favorite command is already installed.

You can change the appearance of a-Shell using config. It lets you change the font, the font size, the background color, the text color and the cursor color. Each window can have its own appearance. config -p will make the settings for the current window permanent, that is used for all future windows.

When opening a new window, a-Shell executes the file .profile if it exists. You can use this mechanism to customize further, e.g. have custom environment variables or cleanup temporary files.

AppStore

a-Shell is now available on the AppStore.

How to compile it?

If you want to compile the project yourself, you will need the following steps:

  • download the entire project and its sub-modules: git submodule update --init --recursive
  • download all the xcFrameworks: swift run --package-path xcfs (or downloadFrameworks.sh)
  • create the Python xcFrameworks (or remove them from the project, if you don't need Python):
    • You'll need the Xcode command line tools, if you don't already have them: sudo xcode-select --install
    • You also need the OpenSSL libraries (libssl and libcrypto), XQuartz (freetype), and Node.js (npm) for macOS (we provide the versions for iOS and simulator).
    • change directory to cpython: cd cpython
    • build Python 3.9 and all the associated libraries / frameworks: sh ./downloadAndCompile.sh (this step takes more than 30 mn on a 2GHz i5 MBP, YMMV).
  • get back to the main directory, open the Xcode project and click "Build", then "Run" as you like.

I am looking for ways to make the process more automatic. Then again, unless you change things in Python, you won't need to re-run through steps 2 and 3.

a-Shell now runs on the devices and on the simulator, as you wish.

Because Python 3.9 uses functions that are only available on the iOS 14 SDK, I've set the mimimum iOS version to 14.0. It also reduces the size of the binaries, so ios_system and the other frameworks have the same settings. If you need to run it on an iOS 13 device, you'll have to recompile most frameworks.

Home directory

In iOS, you cannot write in the ~ directory, only in ~/Documents/, ~/Library/ and ~/tmp. Most Unix programs assume the configuration files are in $HOME.

So a-Shell changes several environment variables so that they point to ~/Documents. Type env to see them.

Most configuration files (Python packages, TeX files, Clang SDK...) are in ~/Library.

Sandbox and Bookmarks

a-Shell uses iOS 13 ability to access directories in other Apps sandbox. Type pickFolder to access a directory inside another App. Once you have selected a directory, you can do pretty much anything you want here, so be careful.

All the directories you access with pickFolder are bookmarked, so you can return to them later without pickFolder. You can also bookmark the current directory with bookmark. showmarks will list all the existing bookmarks, jump mark will change the current directory to this specific bookmark, renamemark will let you change the name of a specific bookmark and deletemark will delete a bookmark.

A user-configurable option in Settings lets you use the commands s, g, l, r and d instead or as well.

If you are lost, cd will always bring you back to ~/Documents/. cd - will change to the previous directory.

Shortcuts

a-Shell is compatible with Apple Shortcuts, giving users full control of the Shell. You can write complex Shortcuts to download, process and release files using a-Shell commands. There are three shortcuts:

  • Execute Command, which takes a list of commands and executes them in order. The input can also be a file or a text node, in which case the commands inside the node are executed.
  • Put File and Get File are used to transfer files to and from a-Shell.

Shortcuts can be executed either "In Extension" or "In App". "In Extension" means the shortcut runs in a lightweight version of the App, without no graphical user interface. It is good for light commands that do not require configuration files or system libraries (mkdir, nslookup, whois, touch, cat, echo...). "In App" opens the main application to execute the shortcut. It has access to all the commands, but will take longer. Once a shortcut has opened the App, you can return to the Shortcuts app by calling the command open shortcuts://. The default behaviour is to try to run the commands "in Extension" as much as possible, based on the content of the commands. You can force a specific shortcut to run "in App" or "in Extension", with the warning that it won't always work.

Both kind of shortcuts run by default in the same specific directory, $SHORTCUTS. Of course, since you can run the commands cd and jump in a shortcut, you can pretty much go anywhere.

Programming / add more commands:

a-Shell has several programming languages installed: Python, Lua, JS, C, C++ and TeX.

For C and C++, you compile your programs with clang program.c and it produces a webAssembly file. You can then execute it with wasm a.out. You can also link multiple object files together, make a static library with ar, etc. Once you are satisfied with your program, if you move it to a directory in the $PATH (e.g. ~/Documents/bin) and rename it program.wasm, it will be executed if you type program on the command line.

You can also cross-compile programs on your main computer using our specific WASI-sdk, and transfer the WebAssembly file to your iPad or iPhone.

Precompiled WebAssembly commands specific for a-Shell are available here: https://github.com/holzschu/a-Shell-commands These include zip, unzip, xz, ffmpeg... You install them on your iPad by downloading them and placing them in the $PATH.

We have the limitations of WebAssembly: no sockets, no forks, no interactive user input (piping input from other commands with command | wasm program.wasm works fine).

For Python, you can install more packages with pip install packagename, but only if they are pure Python. The C compiler is not yet able to produce dynamic libraries that could be used by Python.

TeX files are not installed by default. Type any TeX command and the system will prompt you to download them. Same with LuaTeX files.

VoiceOver

If you enable VoiceOver in Settings, a-Shell will work with VoiceOver: reading commands as you type them, reading the result, letting you read the screen with your finger...

Comments
  • Feature Request: Interactive JavaScript Shell

    Feature Request: Interactive JavaScript Shell

    #!python
    
    import os, sys, re
    import subprocess, tempfile
    
    from argparse import ArgumentParser
    
    VALID_JS_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]+$")
    
    LONG_DESCRIPTION = """jsi executes JavaScript by running it within a WkWebView. As such, all standard browser APIs should be available.""".strip()
    
    SHELL_BANNER = "Type 'help' for help and 'quit' to exit."
    
    SHELL_HELP = \
    """This program is a simple, interactive JavaScript shell.
    In addition to browser-provided APIs, the following commands are provided:
        print(message)
        println(message)
            Prints [message] to the terminal.
            `println` prints a trailing newline after printing [message].
    
    Note that changes to `document` won't be visible unless jsi is run with the '-w' or '--in-window' option. If this option is not given, code is run in a background WebView.
    """
    
    JS_UTILITIES = r"""
    /**
     * Additional functions for script output
     */
    
    let print = (...output) => {
        _jsi.notePrintFn(window.print, output);
        return window.print.apply(this, output);
    };
    
    let println = (...output) => {
        _jsi.notePrintFn(window.println, output);
        return window.println.apply(this, output);
    };
    
    let print_error = (...output) => {
        _jsi.notePrintFn(window.print_error, output);
        return window.print_error.apply(this, output);
    };
    
    // Define the _jsi namespace.
    (function() {
    
    if (!window._jsi) {
        window._jsi = {};
        _jsi.uncollectedPrints = [];
    }
    
    /**
     * Print unprinted output.
     */
    _jsi.collectPrints = () => {
        if (_jsi.uncollectedPrints) {
            for (let item of _jsi.uncollectedPrints) {
                item.printFn.apply(item.printFn, item.args);
            }
        }
    
        _jsi.uncollectedPrints = [];
    };
    
    _jsi.pauseSavingFuturePrints = () => {
        _jsi.savingPrintsPaused = true;
    };
    
    _jsi.saveFuturePrints = () => {
        _jsi.savingPrintsPaused = false;
    };
    
    /**
     * Note that we have called a print
     * function. This allows its output to
     * be collected later.
     */
    _jsi.notePrintFn = (fctn, args) => {
        if (_jsi.savingPrintsPaused) {
            return;
        }
    
        _jsi.uncollectedPrints.push({
            printFn: fctn,
            args: args
        });
    };
    
    /**
     * @param obj Entity to stringify and print.
     * @param recurseDepth Maximum depth to print properties of sub-objects.
     * @param indentation How much to indent the region. Optional.
     * @param toplevel Optional; used internally.
     * @return a string description of [obj].
     */
    _jsi.stringify_obj = function (obj, recurseDepth, indentation, toplevel) {
        /* Fill default arguments. */
        indentation = indentation === undefined ? "" : indentation;
        toplevel = toplevel === undefined ? true : toplevel;
        recurseDepth = recurseDepth === undefined ? 1 : recurseDepth;
    
        /* Accumulator for output. */
        let result = "";
        let formatLine = "";
    
        /**
         * Indent [content] with given indententation and additional characters [additional].
         */
        const indent = (content, additional) => {
            additional = additional || "";
    
            return (indentation + additional + content).split('\n').join('\n' + indentation + additional);
        };
    
        const termEscape = (text) => {
            return text.split('\033').join('\\033').split('\0').join('\\0').split('\\').join('\\\\');
        }
    
        /**
         * Surrounds text with multiline quotation characters, escapes text content.
         */
        const inQuotes = (text) => {
            return "`" + termEscape(text).split('`').join('\\`') + "`";
        };
    
        /**
         * Append [content] to result with proper indentation and a newline.
         */
        const outputLine = (content) => {
            result += indent(content) + "\n";
        };
    
        /* If an object, list its properties */
        if (recurseDepth >= 0 && typeof(obj) == 'object') {
            result += '' + obj;
            result += " {\n";
    
            let propDescriptions = [];
    
            for (let prop in obj) {
                result += indent(prop + ": ", "  ");
    
                /* Permission errors may prevent us from accessing/enumerating properties of obj[prop]. */
                try {
                    result += _jsi.stringify_obj(obj[prop], recurseDepth - 1,indentation + "  ", false);
                } catch(e) {
                    result += "(error `" + e + "`)";
                }
    
                result += ",\n";
            }
    
            outputLine("}");
            formatLine = "js";
        } else if (typeof (obj) == "string") {
            const quoted = inQuotes(obj);
            const lines = quoted.split('\n');
            formatLine = "js";
    
            if (quoted.length > 100 && recurseDepth < 0) {
                result += quoted.substring(0, 100) + ' `...';
            } else if (lines.length == 1) {
                result += quoted;
            } else {
                if (obj.search(/[<][\/][a-zA-Z0-9 \t]+[>]/) >= 0 && recurseDepth >= 0) {
                    for (const line of lines) {
                        outputLine(line);
                    }
    
                    formatLine = "html";
                } else {
                    result += quoted;
                }
            }
        } else if (typeof (obj) == 'function') {
            if (recurseDepth < 0) {
                result += "[ Function ]";
            } else {
                const lines = termEscape(obj + '').split('\n');
    
                for (let i = 0; i < lines.length; i++) {
                    if (i == 0) {
                        result += lines[i] + '\n';
                    } else if (i == lines.length - 1) {
                        result += indent(lines[i]);
                    } else {
                        outputLine(lines[i]);
                    }
                }
            }
    
            formatLine = "js";
        } else if (obj == undefined) {
            result += "undefined";
            formatLine = "undefined";
        } else {
            result += termEscape('' + obj);
        }
    
        formatLine += " " + result.split("\n").length;
    
        if (!toplevel) {
            formatLine = "";
        } else {
            result = "\n" + result;
            formatLine = "\n" + formatLine;
        }
    
        return result + formatLine;
    };
    
    })(); /* End declaration of _jsi "namespace". */
    """
    
    def getJSInfo(text, stopIdx=-1):
        """
        Returns information about [text] if
        interpreted as JavaScript. Information
        is returned as a dictionary in the form,
        {
            endsWithOp: '+', '-', '*', '/', '&', '|', '^', or None
            parenLevel: Number of unclosed nested parens
            squareBracketLevel: Number of unclosed nested '[' ']' brackets
            curlyBraceLevel: Number of unclosed nested '{' '}' braces.
            escaped: True iff the last character processed was escaped escaped
            inComment: The last character processed was in some (single or multi-line) comment
            inMultiLineComment: True iff the last character processed was in a multi-line comment
            inQuote: '`', '"', "'", or None
            trailingEmptyLineCount: Number of lines with no non-space characters processed at the end of the input.
            endsWithSemicolon: Whether the last character in the input is ';'
        }
        """
    
        inQuote = None
        inSLComment = False
        inMLComment = False
        escaped = False
        parenLevel = 0
        curlyBraceLevel = 0
        squareBracketLevel = 0
        endsWithOperator = None
        emptyLineCount = 0
        endsWithSemicolon = False
    
        if stopIdx > len(text) or stopIdx < 0:
            stopIdx = len(text)
    
        for i in range(0, stopIdx):
            char = text[i]
            nextChar = ''
            if i + 1 < len(text):
                nextChar = text[i + 1]
    
            isSpaceChar = char == ' ' or char == '\t' or char == '\n'
    
            if not isSpaceChar:
                endsWithOperator = None
                emptyLineCount = 0
            endsWithSemicolon = False
    
            if escaped:
                escaped = False
            elif char == '\n':
                emptyLineCount += 1
                inSLComment = False
            elif char == '*' and nextChar == '/':
                inMLComment = False
            elif inMLComment or inSLComment:
                pass
            elif char == '"' or char == "'" or char == '`':
                if inQuote is None:
                    inQuote = char
                elif char == inQuote:
                    inQuote = None
            elif inQuote:
                pass
            elif char == '/' and nextChar == '*':
                inMLComment = True
            elif char == '/' and nextChar == '/':
                inSLComment = True
            elif char == '\\':
                escaped = True
            elif char == '(':
                parenLevel += 1
            elif char == ')':
                parenLevel -= 1
            elif char == '{':
                curlyBraceLevel += 1
            elif char == '}':
                curlyBraceLevel -= 1
            elif char == '[':
                squareBracketLevel += 1
            elif char == ']':
                squareBracketLevel -= 1
            elif char == ';':
                endsWithSemicolon = True
            elif char == '+' or char == '-' or char == '*' or char == '/' or char == '&' or char == '^' or char == '|':
                endsWithOperator = char
    
        result = {}
        result['endsWithOp'] = endsWithOperator
        result['parenLevel'] = parenLevel
        result['squareBracketLevel'] = squareBracketLevel
        result['curlyBraceLevel'] = curlyBraceLevel
        result['escaped'] = escaped
        result['inComment'] = inMLComment or inSLComment
        result['inMultiLineComment'] = inMLComment
        result['inQuote'] = inQuote
        result['trailingEmptyLineCount'] = emptyLineCount
        result['endsWithSemicolon'] = endsWithSemicolon
        return result
    
    
    def shouldNewlineAccept(text):
        """
        Returns true iff pressing Return
        should append a newline to text.
        """
    
        ctx = getJSInfo(text)
        hasUnfinishedCode = ctx['squareBracketLevel'] > 0 or \
                ctx['curlyBraceLevel'] > 0 or\
                ctx['parenLevel'] > 0 or \
                ctx['inQuote'] or \
                ctx['inMultiLineComment'] or \
                ctx['endsWithOp']
    #            ctx['endsWithSemicolon']
    
        return ctx['trailingEmptyLineCount'] < 2 and hasUnfinishedCode or ctx['escaped']
    
    
    try:
        import pygments
        from pygments.lexers.javascript import JavascriptLexer
        from pygments.lexers.html import HtmlLexer
        from pygments.formatters import TerminalFormatter
        havePygments = True
    except:
        havePygments = False
    
    try:
        import prompt_toolkit, prompt_toolkit.filters, prompt_toolkit.validation
        from prompt_toolkit.lexers import PygmentsLexer
        from prompt_toolkit.completion import Completion, Completer, NestedCompleter
    
        class NewlineAcceptTester(prompt_toolkit.filters.Condition, prompt_toolkit.validation.Validator):
            def __init__(self, session):
                self._session = session
                self._text = ""
            def validate(self, document):
                self._text = document.text
            def __call__(self):
                return shouldNewlineAccept(self._text)
            def __bool__(self):
                return True
    
        class JSCompleter(Completer):
            completions = {
                "window": None,
                "if": None,
                "else": None,
                "var": None,
                "let": None,
                "for": None,
                "of": None,
                "in": None,
                "while": None,
                "const": None,
                "class": None,
                "extends": None,
                "new": None,
                "function": None,
                "println": None,
                "print": None,
                "Math": {
                    "random": None,
                    "sin": None,
                    "cos": None,
                    "asin": None,
                    "acos": None,
                    "atan": None,
                    "atan2": None,
                    "tan": None,
                    "pow": None,
                    "log": None,
                    "abs": None,
                    "cbrt": None,
                    "sqrt": None,
                    "exp": None,
                    "floor": None,
                    "round": None,
                    "ceil": None,
                    "PI": None,
                    "E": None,
                    "SQRT2": None,
                    "LN2": None,
                    "LN10": None
                },
                "BigInt": None,
                "Number": None,
                "Date": None,
                "help": None,
                "quit": None
            }
    
            def __init__(self, runJS):
                self._userPopulated = {}
                self._runJS = runJS
                self._populateFromJS("window")
                self.completions["window"]["window"] = self.completions["window"]
                for key in self.completions["window"]:
                    self.completions[key] = self.completions["window"][key]
    
            def get_completions(self, document, complete_event):
                cursor = document.cursor_position
                text = document.text
                word = document.get_word_before_cursor(pattern=re.compile(r"[a-z0-9A-Z._]+"))
    
                # First, if we're in a quote or comment, quit.
                ctx = getJSInfo(text, cursor)
                if ctx['inComment'] or ctx['inQuote']:
                    return
    
                def fromDict(parts, completionDict):
                    """ Yields completions matching parts from completionDict """
                    result = []
    
                    if len(parts) > 0:
                        for key in completionDict:
                            if len(key) >= len(parts[0]) and key[:len(parts[0])] == parts[0]:
                                if len(parts) == 1:
                                    result.append(Completion(key, start_position = -len(parts[0])))
                                elif completionDict[key]:
                                    result += fromDict(parts[1:], completionDict[key])
                    return result
    
                parts = word.split('.')
                completionCount = 0
    
                for completion in fromDict(parts, self.completions):
                    completionCount += 1
                    yield completion
    
                if completionCount == 0 and len(word) > 0 and len(parts) >= 2:
                    # If the word's parent isn't in this' completion dict,
                    # stop.
                    checkDict = self.completions
                    for part in parts[:-2]:
                        if not part in checkDict:
                            return
                        checkDict = checkDict[part]
    
                    if word[-1] == '.':
                        word = word[:-1]
                    populateFrom = ".".join(parts[:-1])
    
                    # If we've already tried to populate suggestions
                    # for this, stop. There may not be any
                    if populateFrom in self._userPopulated:
                        return
    
                    self._populateFromJS(populateFrom)
                    self._userPopulated[populateFrom] = True
    
            def _populateFromJS(self, base, parentDict = None, depth = 0, maxDepth = 0):
                if depth > maxDepth:
                    return
    
                if parentDict is None:
                    parts = base.split('.')
                    parentDict = self.completions
                    baseSoFar = []
    
                    for part in parts:
                        baseSoFar.append(part)
                        if not part in parentDict or not parentDict[part]:
                            partExistsResult = self._runJS("""
                            try {
                                %s;
                                println('success');
                            } catch(e) {
                                println('error');
                            }
                            """ % ".".join(baseSoFar)).strip()
    
                            # If it doesn't exist, don't generate completions
                            # for it!
                            if partExistsResult != "success":
                                return
    
                            parentDict[part] = {}
                        parentDict = parentDict[part]
    
                out = self._runJS("""{
                let result = '';
                try
                {
                    for (let key in eval(%s)) {
                        if (key.indexOf('\\n') == -1) {
                            result += key + "\\n";
                        }
                    }
                } catch(e) { }
                println(result); }""" % jsQuote(base))
    
                for key in out.split('\n'):
                    if key != "" and VALID_JS_NAME.match(key):
                        parentDict[key] = { }
                        self._populateFromJS(base + "." + key, parentDict[key], depth + 1, maxDepth)
    
        promptTkSession = None
        if sys.stdin.isatty():
            promptTkSession = prompt_toolkit.PromptSession()
    except Exception as e:
        print ("Warning: Unable to import prompt_toolkit: " + str(e))
        promptTkSession = None
    
    class SourceFormatter:
        def __init__(self, disableColor=False):
            self._disableColor = disableColor or not havePygments
    
        def isHighlightingEnabled(self):
            return not self._disableColor
    
        def formatHtml(self, text):
            """ Returns [text] highlighted via ASCII escape sequences """
    
            if self._disableColor:
                return text
            else:
                return self._highlight(text, HtmlLexer())
    
        def formatJavascript(self, text):
            """
                Returns [text] with ASCII escape sequences inserted to provide
            syntax highlighting
            """
    
            if self._disableColor:
                return text
            else:
                return self._highlight(text, JavascriptLexer()).rstrip()
    
        def getJavascriptLexer(self):
            """
                Returns JavascriptLexer, or None, depending on whether coloring is enabled.
            """
    
            if self._disableColor:
                return None
            else:
                return JavascriptLexer
    
        def formatError(self, text):
            if self._disableColor:
                return text
            else:
                return "\033[92m%s\033[0m" % text
    
        def _highlight(self, text, lexer):
            return pygments.highlight(text, lexer, TerminalFormatter())
    
    class InputReader:
        def __init__(self, disableFormatting, promptText, runJS):
            self._formattingDisabled = disableFormatting or not promptTkSession
            self._promptText = promptText
    
            if not self._formattingDisabled:
                self._makeInteractive()
                self._lexer = PygmentsLexer(JavascriptLexer)
                self._shouldNewlineAcceptTest = NewlineAcceptTester(promptTkSession)
                self._completer = JSCompleter(runJS)
    
        def prompt(self):
            if self._formattingDisabled:
                print(self._promptText, end='')
                result = str(input())
                while shouldNewlineAccept(result):
                    result += "\n" + str(input())
                return result
            else:
                return promptTkSession.prompt(self._promptText,
                        lexer=self._lexer,
                        multiline=self._shouldNewlineAcceptTest,
                        validator=self._shouldNewlineAcceptTest,
                        completer=self._completer)
    
        def _makeInteractive(self):
            runJS("window.interactiveCommandRunning = true;", inTermContext = True)
    
    class Printer:
        PRIMARY_COLOR="\033[94m"
        SECONDARY_COLOR="\033[33m"
        HIGHLIGHT_COLOR="\033[93m"
        NO_COLOR="\033[0m"
    
        def __init__(self, disableColorization):
            self._colorDisabled = disableColorization
    
        def print(self, text, end='\n'):
            """ Print [text] with no additional formatting. """
    
            self._print(text, end=end)
    
        def printPrimary(self, text, end='\n'):
            """ Print [text] with primary formatting. """
    
            self._print(text, self.PRIMARY_COLOR, end=end)
    
        def printSecondary(self, text, end='\n'):
            """ Print [text] with secondary formatting. """
    
            self._print(text, self.SECONDARY_COLOR, end=end)
    
        def printHighlight(self, text, end='\n'):
            """ Print [text], highlighted """
    
            self._print(text, self.HIGHLIGHT_COLOR, end=end)
    
        def _print(self, text, colorize=None, end='\n'):
            if colorize is None or self._colorDisabled:
                print(text, end=end)
            else:
                print("%s%s%s" % (colorize, text, self.NO_COLOR), end=end)
    
    
    ## Returns [text] within a string, line breaks, etc. escaped.
    def jsQuote(text):
        result = '`'
        for char in text:
            if char == '`' or char == '\\':
                result += '\\'
            result += char
        result += '`'
    
        return result
    
    def exportVarDefs(js):
        VARDECL_LETTERS = { '', 'l', 'e', 't', 'c', 'o', 'n', 's', 't' }
        result = ''
    
        inQuote = None
        escaped = False
        buff = ''
        bracketLevel = 0
        inSingleLineComment = False
        inMultiLineComment = False
    
        def flushBuff(nextChar):
            nonlocal buff, result
            if nextChar == '\t' or nextChar == '\n' or nextChar == ' ':
                if buff.strip() == 'let' or buff.strip() == 'const':
                    if bracketLevel == 0 and not inSingleLineComment and not inMultiLineComment and inQuote is None:
                        buff = 'var'
            result += buff
            buff = ''
    
        for i in range(0, len(js)):
            char = js[i]
            nextChar = ''
    
            if i + 1 < len(js):
                nextChar = js[i + 1]
    
            inComment = inSingleLineComment or inMultiLineComment
    
            buff += char
            if escaped:
                escaped = False
                continue
            elif inSingleLineComment and char == '\n':
                inSingleLineComment = False
            elif char == '*' and nextChar == '/':
                inMultiLineComment = False
            elif inComment:
                continue
            elif char == '"' or char == "'" or char == '`':
                if char == inQuote:
                    inQuote = None
                else:
                    inQuote = char
            elif inQuote:
                continue
            elif char == '/' and nextChar == '/':
                inSingleLineComment = True
            elif char == '/' and nextChar == '*':
                inMultiLineComment = True
            elif char == '{':
                bracketLevel += 1
            elif char == '}':
                bracketLevel -= 1
            elif char == '\\':
                escaped = True
    
            if not nextChar in VARDECL_LETTERS or not char in VARDECL_LETTERS:
                flushBuff(nextChar)
    
        result += buff
        return result
    
    ## Evaluates [content] as JavaScript. If
    ##  [inTermContext], [content] is evaluated
    ##  in the same context as the terminal UI.
    ## Returns the output, if any, produced by [content].
    ##  If [collectPrints], any async/delayed print statements' output is also returned.
    def runJS(content, inTermContext=False, recurseDepth=1, sourceFormatter=SourceFormatter(), collectPrints = False):
        toRun = JS_UTILITIES
    
        if collectPrints:
            toRun += "_jsi.collectPrints();\n"
    
        # Don't re-print what was printed while jsc was running
        toRun += "_jsi.pauseSavingFuturePrints();\n"
        toRun += "print(_jsi.stringify_obj(eval(%s), %d));" % (jsQuote(exportVarDefs(content)), recurseDepth)
    
        # Save any async prints for later collection
        toRun += "_jsi.saveFuturePrints();"
    
        outFile = tempfile.NamedTemporaryFile(delete=False)
        outFile.write(bytes(toRun, "utf-8"))
        outFile.close()
    
        args = ["jsc"]
        if inTermContext:
            args.append("--in-window")
        args.append(os.path.relpath(outFile.name))
    
        output = subprocess.check_output(args)
        output = output.decode("utf-8").rstrip(" \r\n")
    
        os.unlink(outFile.name)
    
        ## The last line is always a format specifier, unless an error occurred.
        outLines = output.split('\n')
    
        if len(outLines) == 0:
            return "No output"
    
        lastLine = outLines[-1].split(" ")
        outLines = outLines[:-1]
    
        # If we don't have two words in the output's
        # format specification line, an error occurred.
        if len(lastLine) != 2 or not lastLine[1].isdecimal():
            return sourceFormatter.formatError(output)
    
        formatType = lastLine[0]
        formatLineCount = int(lastLine[1])
    
        formatLines = "\n".join(outLines[-formatLineCount:])
        unformatLines = "\n".join(outLines[:-formatLineCount])
    
        if formatType == 'js':
            formatLines = sourceFormatter.formatJavascript(formatLines)
        elif formatType == 'html':
            formatLines = sourceFormatter.formatHtml(formatLines)
        elif formatType == "undefined":
            formatLines = ""
    
            # Remove trailing linebreak from unformatted lines, we don't need it.
            if len(unformatLines) > 1 and unformatLines[-1] == '\n':
                unformatLines = unformatLines[:-1]
    
        result = unformatLines + formatLines
        return result
    
    ## Parse commandline arguments and take action on them, if necessary.
    ## Returns a map with the following keys:
    ## {
    ##  prompt: The prompt to use when interacting with the user.
    ##  omitIntro: False iff a brief introductory help message should be printed.
    ##  inTermWebView: True iff scripts should be run in the same WkWebView as the terminal UI.
    ##  noColor: True iff output should have no color formatting.
    ## }
    def parseCmdlineArguments():
        args = ArgumentParser(
                description="An interactive JavaScript shell",
                epilog=LONG_DESCRIPTION
        )
    
        isTTY = True
        omitIntro = False
        defaultPrompt = "% "
    
        # If we're getting input from a pipe (i.e. cat foo.js | jsi)
        # don't print a prompt (by default).
        if not sys.stdin.isatty() or not sys.stdout.isatty():
            defaultPrompt = ""
            omitIntro = True
            isTTY = False
    
        args.add_argument("--prompt", default=defaultPrompt)
        args.add_argument("--no-banner",
                default=omitIntro, action="store_true", dest="omitIntro")
        args.add_argument("--no-color",
                default=not isTTY,
                dest="noColor",
                action="store_true", help="Disable colorized output")
        args.add_argument("-d", "--inspect-depth",
                default=0,
                type=int,
                help="When printing the details of an object, how many levels of sub-objects to inspect. This should be a small number (e.g. 0, 1, or 2).",
                dest="inspectDepth")
        args.add_argument("-w", "--in-window",
                action="store_true", dest="inTermWebView",
                help="Run JavaScript in the same WkWebView as the terminal UI.")
    
        result = args.parse_args()
    
        if not "omitIntro" in result:
            result.omitIntro = omitIntro
    
        return result
    
    
    if __name__ == "__main__":
        args = parseCmdlineArguments()
        promptText = args.prompt
        response = ''
    
        formatter = SourceFormatter(args.noColor)
        execJS = lambda js, collectPrints=False: \
                runJS(js,
                        args.inTermWebView,
                        args.inspectDepth,
                        formatter,
                        collectPrints=collectPrints)
    
        out = Printer(args.noColor)
        readIn = InputReader(args.noColor, promptText, execJS)
    
        if not args.omitIntro:
            out.printPrimary(SHELL_BANNER)
    
        # If input is not directly from a user,
        # run input to this, then exit.
        if not sys.stdin.isatty():
            toRun = sys.stdin.read()
            out.print(execJS(toRun))
            sys.exit(0)
    
        while True:
            try:
                response = readIn.prompt()
            except EOFError:
                print()
                break
            except KeyboardInterrupt:
                print("quit")
                break
            if response == "quit":
                break
            elif response == "help":
                out.printPrimary(SHELL_HELP)
            else:
                out.print(execJS(response,  collectPrints=True))
    
    

    It would be great to have this in a-Shell! I'm not an iOS developer, so I'm not comfortable opening a pull request.

    opened by personalizedrefrigerator 36
  • Build 164: Closing and re-opening a-Shell -> black screen

    Build 164: Closing and re-opening a-Shell -> black screen

    Summary

    • After closing and re-opening a-Shell, the screen is black, and commands I enter seem to do nothing.
      • When no external keyboard is connected, the touch keyboard does not appear.

    Terminal settings

    • A rather large font-size (config -s 16, I think)
    • Black background (config -b black)
    • White foreground

    Thanks again for working on this!

    opened by personalizedrefrigerator 27
  • `~/Documents/bin`: `#!sh`, `#!bc`, ... -> not executable

    `~/Documents/bin`: `#!sh`, `#!bc`, ... -> not executable

    Summary

    • #!command_name at the top of an executable text file seems to only work if command_name is python or lua.

    Expected Behavior

    ~/Documents/bin/test1:

    #!sh
    echo "It works"
    

    or

    #!bc
    print 'a test'
    

    or

    #!almake_shell
    # ^ Part of almost_make pip package
    
    echo "It doesn't work"
    

    ~/Documents/bin/test2:

    #!python
    print("It works")
    
    $ chmod u+x ~/Documents/bin/test1
    $ chmod u+x ~/Documents/bin/test2
    $ test2
    It works
    $ test1
    test1: command not found
    

    test1 and test2 should produce the same output.

    Thank you for your time!

    fix committed 
    opened by personalizedrefrigerator 19
  • Is it possible to build this repository?

    Is it possible to build this repository?

    I'm trying to build a-Shell, but it seems that this repository is currently impossible to build because of some problems.

    The first problem is a conflict of dependencies. Specifically, both https://github.com/holzschu/Python-aux and https://github.com/holzschu/libssh2-for-iOS have openssl.framework, so Xcode outputs an error about that on build.

    The second one is using paths like /Users/holzschu/... or ../../../Library/Developer/Xcode/DerivedData/.... To avoid it, use their package's frameworks directly and don't have references to xcframework files. If the first problem is solved, I will be able to submit a PR to solve this if you want.

    Screen Shot 2020-09-25 at 6 43 15 AM

    Thanks.

    opened by kkk669 19
  • Clang fails to load on iOS 15.4

    Clang fails to load on iOS 15.4

    Clang doesn't seem to work on iOS 15.4. This issue can be reproduced on a-shell and other apps that uses https://github.com/holzschu/llvm-project.

    Looks like this has something to do with libc++.1.dylib being missing in iOS 15.4. I found a message from Xcode 13.3 RC release note that might relate to this (https://developer.apple.com/documentation/Xcode-Release-Notes/xcode-13_3-release-notes):

    Xcode no longer passes -stdlib=libstdc++ to Clang, because Clang no longer supports that library on Apple platforms. If your project defines the CLANG_CXX_LIBRARY build setting, remove it because it no longer does anything. (83768231)

    Error from Xcode:

    Failed loading clang from clang.framework/clang, cause = dlopen(clang.framework/clang, 0x0009): Library not loaded: @rpath/libc++.1.dylib
      Referenced from: /private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/clang.framework/clang
      Reason: tried: '/usr/lib/system/introspection/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/clang.framework/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/clang.framework/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/ios_system.framework/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file),
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file), 
    '/private/var/containers/Bundle/Application/860EA2C4-8F59-456A-A643-DBE4C1E23DB8/Code.app/Frameworks/libc++.1.dylib' (no such file)
    

    a-Shell screenshot: IMG_EFAB83A4E6EC-1

    fix committed 
    opened by bummoblizard 18
  • Vim layout is broken on iPadOS 14.5

    Vim layout is broken on iPadOS 14.5

    This problem may be fixed before the final build of iPadOS 14.5 if it is caused by OS or SDK, but I created this issue just in case.

    In iPadOS 14.5 Beta 1, the layout of Vim is broken on a-Shell. Please see the screenshots below.

    1. vim with my .vimrc on a-Shell 1.6.6 (iPadOS 14.5 Beta 1):

    IMG_0399

    1. vim with my .vimrc on a-Shell 1.6.6 (iPadOS 14.4):

    IMG_0247

    1. vim -u NONE on a-Shell 1.6.6 (iPadOS 14.5 Beta 1):

    IMG_0400

    opened by kkk669 18
  • Trouble reproducing the build

    Trouble reproducing the build

    Hi I am trying to follow the instructions to build , but somehow it does not work quite well for me :)

    I see a lot of messages like

    error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool: can't open file: build/lib.macosx-11.1-x86_64-3.9/Frameworks//python3_ios-_cffi_backend.framework/python3_ios-_cffi_backend (No such file or directory)
    Creating:  lib.darwin-arm64-3.9/Frameworks/_cffi_backend.framework
    

    While other frameworks seem to be built, the _cffi_backend framework dir is not even created before .

    Other problem is that the xcode proj seems to reference binaries from other parts of the filesystem (Folder referenced for ios_system, network_ios, lua_ios) . It seems to come from "../../../../../../../../src/Xcode_iPad/a-Shell" in a-Shell.xcodeproj/project.pbxproj . Am I supposed to built this prior to this project at that specific location ?

    opened by oflebbe 17
  • [iOS/iPadOS 15] Hide QuckType bar

    [iOS/iPadOS 15] Hide QuckType bar

    In iOS/iPadOS 15, a-Shell displays the QuickType bar on the bottom.

    F3A6C905-453A-47AF-B6F6-55C6BBAD7F3B

    If I remember correctly, it was hidden in iOS/iPadOS 14.x and earlier.

    Maybe, it relates to this:

    In iOS 14 and iPadOS 14 and earlier, when autocorrectionType is set to UITextAutocorrectionTypeNo, the QuickType bar is disabled. For apps linked against iOS 15 and iPadOS 15 or later, the QuickType bar is enabled and shows spellchecking candidates. If the new behavior isn’t desirable for your use case, set spellCheckingType to UITextSpellCheckingTypeNo to hide the QuickType bar. (68874861)

    opened by kkk669 14
  • `ssh`: Arrow keys not working in Vim?

    `ssh`: Arrow keys not working in Vim?

    Summary

    • sshing into a remote server, I find that while arrow keys move the cursor on the commandline, they do nothing in vim or man
    • Telling vim to log received keypresses,
    
      1 ^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[
        [D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C
        ^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[
        [C^[[C^[[C^[[C^[[C^[[B^[[B^[[B^[[C^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[C^[[A^[[A
        ^[[C^[[A^[[A^[[C^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[
        [A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[:q^M
    

    Edit: This may just be a configuration issue (I don't experience this with nano)...

    opened by personalizedrefrigerator 14
  • Perl broken in 215

    Perl broken in 215

    It seems like I can't run Perl scripts anymore in the latest version:

    Can't locate strict.pm in @INC (you may need to install the strict module) (@INC contains: /var/mobile/Containers/Data/Application/201ED9FE
    -05C0-4690-996E-440A9D9C06D0/Documents/perl5/lib/perl5 /private/var/containers/Bundle/Application/F9A869EA-5556-4139-9455-C42AEEA1CD82/a-Sh
    ell.app/Perl) at .normen/install.pl line 2.
    BEGIN failed--compilation aborted at .normen/install.pl line 2.
    
    opened by normen 12
  • ffmpeg with h264_videotoolbox cannot encode a video properly

    ffmpeg with h264_videotoolbox cannot encode a video properly

    When I run the command like the following, the playback time of out.mov becomes very long.

    ffmpeg -i Big_Buck_Bunny_1080_10s_5MB.mp4 -c:v h264_videotoolbox -an out.mov
    

    In this case, the playback time of Big_Buck_Bunny_1080_10s_5MB.mp4 is 00:00:10.00, however, the one of out.mov is 92:17:02.23 as you can see from the following results. if you test in your own environment, you can download the input video here.

    $ ffprobe Big_Buck_Bunny_1080_10s_5MB.mp4 
    ffprobe version n4.4-80-gbf87bdd3f6 Copyright (c) 2007-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.11)
      configuration: --cc=clang --arch=arm64 --disable-asm --enable-cross-compile --enable-pthreads --enable-videotoolbox --disable-audiotoolbox --enable-libfreetype --enable-libzimg --
    disable-network --disable-debug --disable-gpl --disable-nonfree
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Big_Buck_Bunny_1080_10s_5MB.mp4':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        title           : Big Buck Bunny, Sunflower version
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        composer        : Sacha Goedegebure
        encoder         : Lavf57.63.100
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        genre           : Animation
      Duration: 00:00:10.00, start: 0.000000, bitrate: 3948 kb/s
      Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 3945 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : [0][0][0][0]
    $ ffprobe out.mov 
    ffprobe version n4.4-80-gbf87bdd3f6 Copyright (c) 2007-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.11)
      configuration: --cc=clang --arch=arm64 --disable-asm --enable-cross-compile --enable-pthreads --enable-videotoolbox --disable-audiotoolbox --enable-libfreetype --enable-libzimg --
    disable-network --disable-debug --disable-gpl --disable-nonfree
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'out.mov':
      Metadata:
        major_brand     : qt  
        minor_version   : 512
        compatible_brands: qt  
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        title           : Big Buck Bunny, Sunflower version
        encoder         : Lavf58.76.100
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        genre           : Animation
      Duration: 92:17:02.23, start: 0.000000, bitrate: 0 kb/s
      Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 0 kb/s, SAR 1:1 DAR 16:9, 0.0009 fps, 30 tbr, 15360 tbn, 30720 tbc (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : FFMP
          encoder         : Lavc58.134.100 h264_videotoolbo
    

    I think that the warning messages like [mov @ 0x10905c400] Invalid DTS: 34132480 PTS: 17066496 in output stream 0:0, replacing by guess might relate to the issue because they didn't appear in the macOS version.

    $ ffmpeg -i Big_Buck_Bunny_1080_10s_5MB.mp4 -c:v h264_videotoolbox -an out.mov
    ffmpeg version n4.4-80-gbf87bdd3f6 Copyright (c) 2000-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.11)
      configuration: --cc=clang --arch=arm64 --disable-asm --enable-cross-compile --enable-pthreads --enable-videotoolbox --disable-audiotoolbox --enable-libfreetype --enable-libzimg --disable-network --disable-debug --disable-gpl --disable-nonfree
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Big_Buck_Bunny_1080_10s_5MB.mp4':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        title           : Big Buck Bunny, Sunflower version
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        composer        : Sacha Goedegebure
        encoder         : Lavf57.63.100
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        genre           : Animation
      Duration: 00:00:10.00, start: 0.000000, bitrate: 3948 kb/s
      Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 3945 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : [0][0][0][0]
    Stream mapping:
      Stream #0:0 -> #0:0 (h264 (native) -> h264 (h264_videotoolbox))
    Press [q] to stop, [?] for help
    frame=    0 fps=0.0 q=0.0 size=       0kB time=-577014:32:22.77 bitrate=  -0.0kbits/s speed=N/A    
    Output #0, mov, to 'out.mov':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        title           : Big Buck Bunny, Sunflower version
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        composer        : Sacha Goedegebure
        genre           : Animation
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        encoder         : Lavf58.76.100
      Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 30 fps, 15360 tbn (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : [0][0][0][0]
          encoder         : Lavc58.134.100 h264_videotoolbox
    [h264_videotoolbox @ 0x109107000] Color range not set for yuv420p. Using MPEG range.
    [mov @ 0x10905c400] Invalid DTS: 34132480 PTS: 17066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 68265984 PTS: 51200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 102399488 PTS: 85332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 136532480 PTS: 119466496 in output stream 0:0, replacing by guess
    frame=   12 fps=0.0 q=-0.0 size=     256kB time=02:09:37.76 bitrate=   0.3kbits/s speed=1.43e+04x    
    [mov @ 0x10905c400] Invalid DTS: 170665984 PTS: 153600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 204799488 PTS: 187732992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 255999488 PTS: 238932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 290132480 PTS: 273066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 324265984 PTS: 307200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 358399488 PTS: 341332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 392532480 PTS: 375466496 in output stream 0:0, replacing by guess
    frame=   28 fps= 26 q=-0.0 size=     256kB time=07:24:26.63 bitrate=   0.1kbits/s speed=2.45e+04x    
    [mov @ 0x10905c400] Invalid DTS: 443732480 PTS: 426666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 477865984 PTS: 460800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 511999488 PTS: 494932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 546132480 PTS: 529066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 580265984 PTS: 563200000 in output stream 0:0, replacing by guess
    frame=   39 fps= 24 q=-0.0 size=     256kB time=10:48:08.83 bitrate=   0.1kbits/s speed=2.44e+04x    
    [mov @ 0x10905c400] Invalid DTS: 614399488 PTS: 597332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 665599488 PTS: 648532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 699732480 PTS: 682666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 733865984 PTS: 716800000 in output stream 0:0, replacing by guess
    frame=   48 fps= 22 q=-0.0 size=     512kB time=13:34:48.83 bitrate=   0.1kbits/s speed=2.28e+04x    
    [mov @ 0x10905c400] Invalid DTS: 767999488 PTS: 750932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 802132480 PTS: 785066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 853332480 PTS: 836266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 887465984 PTS: 870400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 921599488 PTS: 904532992 in output stream 0:0, replacing by guess
    frame=   58 fps= 22 q=-0.0 size=     512kB time=16:21:28.86 bitrate=   0.1kbits/s speed=2.21e+04x    
    [mov @ 0x10905c400] Invalid DTS: 955732480 PTS: 938666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 989865984 PTS: 972800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1023999488 PTS: 1006932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1075199488 PTS: 1058132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1109332480 PTS: 1092266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1143465984 PTS: 1126400000 in output stream 0:0, replacing by guess
    frame=   71 fps= 22 q=-0.0 size=     512kB time=20:22:13.33 bitrate=   0.1kbits/s speed=2.31e+04x    
    [mov @ 0x10905c400] Invalid DTS: 1177599488 PTS: 1160532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1211732480 PTS: 1194666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1262932480 PTS: 1245866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1297065984 PTS: 1280000000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1331199488 PTS: 1314132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1365332480 PTS: 1348266496 in output stream 0:0, replacing by guess
    frame=   85 fps= 23 q=-0.0 size=     512kB time=24:59:59.96 bitrate=   0.0kbits/s speed=2.44e+04x    
    [mov @ 0x10905c400] Invalid DTS: 1399465984 PTS: 1382400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1433599488 PTS: 1416532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1484799488 PTS: 1467732992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1518932480 PTS: 1501866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1553065984 PTS: 1536000000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1587199488 PTS: 1570132992 in output stream 0:0, replacing by guess
    frame=   98 fps= 23 q=-0.0 size=     512kB time=29:00:44.40 bitrate=   0.0kbits/s speed=2.47e+04x    
    [mov @ 0x10905c400] Invalid DTS: 1621332480 PTS: 1604266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1672532480 PTS: 1655466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1706665984 PTS: 1689600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1740799488 PTS: 1723732992 in output stream 0:0, replacing by guess
    frame=  107 fps= 22 q=-0.0 size=     512kB time=31:47:24.40 bitrate=   0.0kbits/s speed=2.39e+04x    
    [mov @ 0x10905c400] Invalid DTS: 1774932480 PTS: 1757866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1809065984 PTS: 1792000000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1843199488 PTS: 1826132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1894399488 PTS: 1877332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1928532480 PTS: 1911466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1962665984 PTS: 1945600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 1996799488 PTS: 1979732992 in output stream 0:0, replacing by guess
    frame=  121 fps= 23 q=-0.0 size=     768kB time=35:48:08.86 bitrate=   0.0kbits/s speed=2.43e+04x    
    [mov @ 0x10905c400] Invalid DTS: 2030932480 PTS: 2013866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2082132480 PTS: 2065066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2116265984 PTS: 2099200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2150399488 PTS: 2133332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2184532480 PTS: 2167466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2218665984 PTS: 2201600000 in output stream 0:0, replacing by guess
    frame=  135 fps= 23 q=-0.0 size=     768kB time=40:25:55.50 bitrate=   0.0kbits/s speed=2.51e+04x    
    [mov @ 0x10905c400] Invalid DTS: 2252799488 PTS: 2235732992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2303999488 PTS: 2286932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2338132480 PTS: 2321066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2372265984 PTS: 2355200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2406399488 PTS: 2389332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2440532480 PTS: 2423466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2491732480 PTS: 2474666496 in output stream 0:0, replacing by guess
    frame=  151 fps= 24 q=-0.0 size=     768kB time=45:22:13.30 bitrate=   0.0kbits/s speed=2.59e+04x    
    [mov @ 0x10905c400] Invalid DTS: 2525865984 PTS: 2508800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2559999488 PTS: 2542932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2594132480 PTS: 2577066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2628265984 PTS: 2611200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2662399488 PTS: 2645332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2713599488 PTS: 2696532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2747732480 PTS: 2730666496 in output stream 0:0, replacing by guess
    frame=  166 fps= 24 q=-0.0 size=     768kB time=49:59:59.96 bitrate=   0.0kbits/s speed=2.62e+04x    
    [mov @ 0x10905c400] Invalid DTS: 2781865984 PTS: 2764800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2815999488 PTS: 2798932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2850132480 PTS: 2833066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2901332480 PTS: 2884266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2935465984 PTS: 2918400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 2969599488 PTS: 2952532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3003732480 PTS: 2986666496 in output stream 0:0, replacing by guess
    frame=  180 fps= 24 q=-0.0 size=     768kB time=54:00:44.43 bitrate=   0.0kbits/s speed=2.63e+04x    
    [mov @ 0x10905c400] Invalid DTS: 3037865984 PTS: 3020800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3071999488 PTS: 3054932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3123199488 PTS: 3106132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3157332480 PTS: 3140266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3191465984 PTS: 3174400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3225599488 PTS: 3208532992 in output stream 0:0, replacing by guess
    frame=  194 fps= 24 q=-0.0 size=     768kB time=58:38:31.06 bitrate=   0.0kbits/s speed=2.66e+04x    
    [mov @ 0x10905c400] Invalid DTS: 3259732480 PTS: 3242666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3310932480 PTS: 3293866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3345065984 PTS: 3328000000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3379199488 PTS: 3362132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3413332480 PTS: 3396266496 in output stream 0:0, replacing by guess
    frame=  205 fps= 24 q=-0.0 size=    1024kB time=62:02:13.30 bitrate=   0.0kbits/s speed=2.63e+04x    
    [mov @ 0x10905c400] Invalid DTS: 3447465984 PTS: 3430400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3481599488 PTS: 3464532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3532799488 PTS: 3515732992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3566932480 PTS: 3549866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3601065984 PTS: 3584000000 in output stream 0:0, replacing by guess
    frame=  216 fps= 24 q=-0.0 size=    1024kB time=65:25:55.50 bitrate=   0.0kbits/s speed=2.62e+04x    
    [mov @ 0x10905c400] Invalid DTS: 3635199488 PTS: 3618132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3669332480 PTS: 3652266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3720532480 PTS: 3703466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3754665984 PTS: 3737600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3788799488 PTS: 3771732992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3822932480 PTS: 3805866496 in output stream 0:0, replacing by guess
    frame=  229 fps= 24 q=-0.0 size=    1024kB time=69:26:39.96 bitrate=   0.0kbits/s speed=2.62e+04x    
    [mov @ 0x10905c400] Invalid DTS: 3857065984 PTS: 3840000000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3891199488 PTS: 3874132992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3942399488 PTS: 3925332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 3976532480 PTS: 3959466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4010665984 PTS: 3993600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4044799488 PTS: 4027732992 in output stream 0:0, replacing by guess
    frame=  242 fps= 24 q=-0.0 size=    1024kB time=73:27:24.40 bitrate=   0.0kbits/s speed=2.63e+04x    
    [mov @ 0x10905c400] Invalid DTS: 4078932480 PTS: 4061866496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4130132480 PTS: 4113066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4164265984 PTS: 4147200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4198399488 PTS: 4181332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4232532480 PTS: 4215466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4266665984 PTS: 4249600000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4300799488 PTS: 4283732992 in output stream 0:0, replacing by guess
    frame=  257 fps= 24 q=-0.0 size=    1024kB time=78:05:11.06 bitrate=   0.0kbits/s speed=2.66e+04x    
    [mov @ 0x10905c400] Invalid DTS: 4351999488 PTS: 4334932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4386132480 PTS: 4369066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4420265984 PTS: 4403200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4454399488 PTS: 4437332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4488532480 PTS: 4471466496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4539732480 PTS: 4522666496 in output stream 0:0, replacing by guess
    frame=  271 fps= 24 q=-0.0 size=    1024kB time=82:24:26.63 bitrate=   0.0kbits/s speed=2.68e+04x    
    [mov @ 0x10905c400] Invalid DTS: 4573865984 PTS: 4556800000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4607999488 PTS: 4590932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4642132480 PTS: 4625066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4676265984 PTS: 4659200000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4710399488 PTS: 4693332992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4761599488 PTS: 4744532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4795732480 PTS: 4778666496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4829865984 PTS: 4812800000 in output stream 0:0, replacing by guess
    frame=  287 fps= 25 q=-0.0 size=    1280kB time=87:02:13.33 bitrate=   0.0kbits/s speed=2.7e+04x    
    [mov @ 0x10905c400] Invalid DTS: 4863999488 PTS: 4846932992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4898132480 PTS: 4881066496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4949332480 PTS: 4932266496 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 4983465984 PTS: 4966400000 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 5017599488 PTS: 5000532992 in output stream 0:0, replacing by guess
    [mov @ 0x10905c400] Invalid DTS: 5051732480 PTS: 5034666496 in output stream 0:0, replacing by guess
    frame=  300 fps= 25 q=-0.0 size=    1280kB time=91:02:57.76 bitrate=   0.0kbits/s speed=2.71e+04x    
    [mov @ 0x10905c400] Invalid DTS: 5085865984 PTS: 5068800000 in output stream 0:0, replacing by guess
    FATAL error, file duration too long for timebase, this file will not be
    playable with QuickTime. Choose a different timebase with -video_track_timescale or a different container format
    frame=  300 fps= 25 q=-0.0 Lsize=    1377kB time=92:17:02.16 bitrate=   0.0kbits/s speed=2.72e+04x    
    video:1369kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.513921%
    

    Those warning messages didn't appear in the macOS version of ffmpeg, and the playback time of out.mov is also normal.

    $ ffmpeg -i Big_Buck_Bunny_1080_10s_5MB.mp4 -c:v h264_videotoolbox -an out.mov
    ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.9)
      configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libavresample   4.  0.  0 /  4.  0.  0
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
      libpostproc    55.  9.100 / 55.  9.100
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Big_Buck_Bunny_1080_10s_5MB.mp4':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        title           : Big Buck Bunny, Sunflower version
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        composer        : Sacha Goedegebure
        encoder         : Lavf57.63.100
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        genre           : Animation
      Duration: 00:00:10.00, start: 0.000000, bitrate: 3948 kb/s
      Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 3945 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : [0][0][0][0]
    Stream mapping:
      Stream #0:0 -> #0:0 (h264 (native) -> h264 (h264_videotoolbox))
    Press [q] to stop, [?] for help
    Output #0, mov, to 'out.mov':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        title           : Big Buck Bunny, Sunflower version
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        composer        : Sacha Goedegebure
        genre           : Animation
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        encoder         : Lavf58.76.100
      Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 30 fps, 15360 tbn (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : [0][0][0][0]
          encoder         : Lavc58.134.100 h264_videotoolbox
    [h264_videotoolbox @ 0x7fabae009a00] Color range not set for yuv420p. Using MPEG range.
    frame=  300 fps=165 q=-0.0 Lsize=    1526kB time=00:00:09.93 bitrate=1258.4kbits/s speed=5.47x                                                                                        
    video:1521kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.296806%
    $ ffprobe out.mov
    ffprobe version 4.4 Copyright (c) 2007-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.9)
      configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libavresample   4.  0.  0 /  4.  0.  0
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
      libpostproc    55.  9.100 / 55.  9.100
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'out.mov':
      Metadata:
        major_brand     : qt  
        minor_version   : 512
        compatible_brands: qt  
        artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
        title           : Big Buck Bunny, Sunflower version
        encoder         : Lavf58.76.100
        comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
        genre           : Animation
      Duration: 00:00:10.00, start: 0.000000, bitrate: 1250 kb/s
      Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 1246 kb/s, 30 fps, 30 tbr, 15360 tbn, 30720 tbc (default)
        Metadata:
          handler_name    : VideoHandler
          vendor_id       : FFMP
          encoder         : Lavc58.134.100 h264_videotoolbo
    

    The macOS version of ffmpeg I used was installed from Homebrew. The differences between the two are as follows.

    a-Shell 1.7.5 (185) (TestFlight) (12.9-inch iPad Pro 5th generation running iPadOS 15 Developer Beta 7 (19A5337a)):

    $ ffmpeg -version
    ffmpeg version n4.4-80-gbf87bdd3f6 Copyright (c) 2000-2021 the FFmpeg developers
    built with Apple clang version 12.0.5 (clang-1205.0.22.11)
    configuration: --cc=clang --arch=arm64 --disable-asm --enable-cross-compile --enable-pthreads --enable-videotoolbox --disable-audiotoolbox --enable-libfreetype --enable-libzimg --di
    sable-network --disable-debug --disable-gpl --disable-nonfree
    libavutil      56. 70.100 / 56. 70.100
    libavcodec     58.134.100 / 58.134.100
    libavformat    58. 76.100 / 58. 76.100
    libavdevice    58. 13.100 / 58. 13.100
    libavfilter     7.110.100 /  7.110.100
    libswscale      5.  9.100 /  5.  9.100
    libswresample   3.  9.100 /  3.  9.100
    $ ffmpeg -encoders | grep videotoolbox
      configuration: --cc=clang --arch=arm64 --disable-asm --enable-cross-compile --enable-pthreads --enable-videotoolbox --disable-audiotoolbox --enable-libfreetype --enable-libzimg --
    disable-network --disable-debug --disable-gpl --disable-nonfree
     V..... h264_videotoolbox    VideoToolbox H.264 Encoder (codec h264)
     V..... hevc_videotoolbox    VideoToolbox H.265 Encoder (codec hevc)
    

    macOS:

    $ ffmpeg -version
    ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers
    built with Apple clang version 12.0.5 (clang-1205.0.22.9)
    configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
    libavutil      56. 70.100 / 56. 70.100
    libavcodec     58.134.100 / 58.134.100
    libavformat    58. 76.100 / 58. 76.100
    libavdevice    58. 13.100 / 58. 13.100
    libavfilter     7.110.100 /  7.110.100
    libavresample   4.  0.  0 /  4.  0.  0
    libswscale      5.  9.100 /  5.  9.100
    libswresample   3.  9.100 /  3.  9.100
    libpostproc    55.  9.100 / 55.  9.100
    $ ffmpeg -encoders | grep videotoolbox
    ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers
      built with Apple clang version 12.0.5 (clang-1205.0.22.9)
      configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
      libavutil      56. 70.100 / 56. 70.100
      libavcodec     58.134.100 / 58.134.100
      libavformat    58. 76.100 / 58. 76.100
      libavdevice    58. 13.100 / 58. 13.100
      libavfilter     7.110.100 /  7.110.100
      libavresample   4.  0.  0 /  4.  0.  0
      libswscale      5.  9.100 /  5.  9.100
      libswresample   3.  9.100 /  3.  9.100
      libpostproc    55.  9.100 / 55.  9.100
     V..... h264_videotoolbox    VideoToolbox H.264 Encoder (codec h264)
     V..... hevc_videotoolbox    VideoToolbox H.265 Encoder (codec hevc)
    $ brew info ffmpeg
    ffmpeg: stable 4.4 (bottled), HEAD
    Play, record, convert, and stream audio and video
    https://ffmpeg.org/
    /usr/local/Cellar/ffmpeg/4.4_2 (276 files, 52.4MB) *
      Poured from bottle on 2021-08-28 at 18:53:41
    From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/ffmpeg.rb
    License: GPL-2.0-or-later
    ==> Dependencies
    Build: nasm ✘, pkg-config ✔
    Required: aom ✔, dav1d ✔, fontconfig ✔, freetype ✔, frei0r ✔, gnutls ✔, lame ✔, libass ✔, libbluray ✔, libsoxr ✔, libvidstab ✔, libvorbis ✔, libvpx ✔, opencore-amr ✔, openjpeg ✔, opus ✔, rav1e ✔, rubberband ✔, sdl2 ✔, snappy ✔, speex ✔, srt ✔, tesseract ✔, theora ✔, webp ✔, x264 ✔, x265 ✔, xvid ✔, xz ✔, zeromq ✔, zimg ✔
    ==> Options
    --HEAD
            Install HEAD version
    ==> Analytics
    install: 78,029 (30 days), 298,735 (90 days), 1,670,877 (365 days)
    install-on-request: 62,053 (30 days), 243,570 (90 days), 1,379,795 (365 days)
    build-error: 0 (30 days)
    
    opened by kkk669 12
  • credential helpers for lg2

    credential helpers for lg2

    Hi,

    I was experimenting with different ways to have credentials stored for lg2 to push without having to repeatedly enter a personal access token.

    I have not gotten any way to work, even in the simplest case of a helper shell script with all commands available, I could not avoid the prompt.

    [credential "https://github.com/michaelrommel/myrepo"]
            username = michaelrommel
            helper = "!f() { test \"$1\" = get && echo \"password=$(cat $HOME/Documents/.myrepo-cred)\"; }; f"
    

    Is there any way to achieve this with lg2?

    Thanks - I haven't found a solution in any of the documentation or issues. Did I overlook this?

    opened by michaelrommel 2
  • Add support for display GUI

    Add support for display GUI

    Just a thought suggestion, if it is possible to add the ability to display GUI binaries or possibly render 2D/3D GUI (say code compiled with OpenGL). From an experience standpoint, it might look like KMSDRM where you can render GUI directly from the shell.

    opened by falhumai96 1
  • Unable to install Git (lg2) on A-Shell

    Unable to install Git (lg2) on A-Shell

    When I run pkg install git, It outputs this:

    Downloading git
    The git command cannot be included with
    a-Shell. Would you like to create a
    script at $HOME/Documents/bin/git that
    wraps lg2 with the following contents?
    #!/bin/sh
    lg2 "$@"
    Keep in mind that git and lg2 options
    are not 100% compatible, and they also
    do not use the same configuration files
    or environment variables.
    Create $HOME/Documents/bin/git? (y/n [n]) y
    The $HOME/Documents/bin directory does not exist.
    Exiting without creating wrapper.
    Done
    
    opened by YousufSSyed 3
  • Feature Request: allowing for sending commands to A-Shell with URL schemes.

    Feature Request: allowing for sending commands to A-Shell with URL schemes.

    URL schemes are described here on the Apple Developer website. A-shell could have a URL scheme like this: ashell:command?command=command.

    Here's an example: ashell:command?command=jump%20folder%0Apython3%20file.py, which would run the commands jump folder and python3 file.py.

    opened by YousufSSyed 3
  • Feature request: allow settings background images on A-Shell.

    Feature request: allow settings background images on A-Shell.

    Users should be able to select a background from photos or files, and decide if they want it to show it in the original aspect ratio (and show black bars), or they can select a portion of the photo use (like in iOS settings).

    opened by YousufSSyed 0
Releases(cpython_05_22)
Owner
Nicolas Holzschuch
Nicolas Holzschuch
Lock a terminal command to the efficiency or performance cores on a big.LITTLE ARM processor

CPU-Lock Lock a terminal command to the efficiency or performance cores on a big.LITTLE ARM processor (Designed for Apple Silicon). Usage Download the

BitesPotatoBacks 0 Aug 11, 2022
XMachOViewer is a Mach-O viewer for Windows, Linux and MacOS

MachO file viewer/editor for Windows, Linux and macOS. Heuristic scan String viewer Hex viewer Disasm viewer(x86/64,ARM,PPC,m68k) Entropy viewer Hash

Hors 505 Dec 20, 2022
Ios-mod-extract - iOS mod extract with swift

DIOExtract Example To run the example project, clone the repo, and run pod insta

Victor Freitas 3 Jun 14, 2022
IOS-Bootcamp-Examples - Learn to Swift while building apps - With IOS Development Bootcamp

IOS-Bootcamp-Examples Learn to Swift while building apps - With IOS Development

Bilge Çakar 9 Dec 21, 2022
iOS helper library that contains commonly used code in Uptech iOS projects

iOS helper library that contains commonly used code in Uptech iOS projects.

Uptech 1 Apr 1, 2022
Ethereum Wallet Toolkit for iOS - You can implement an Ethereum wallet without a server and blockchain knowledge.

Introduction EtherWalletKit is an Ethereum Wallet Toolkit for iOS. I hope cryptocurrency and decentralized token economy become more widely adapted. H

Sung Woo Chang 136 Dec 25, 2022
Generate a privacy policy for your iOS app

PrivacyFlash Pro To easily run PrivacyFlash Pro get the latest packaged release. Learn more about PrivacyFlash Pro in our research paper. PrivacyFlash

privacy-tech-lab 141 Dec 22, 2022
Tweak your iOS app without recompiling!

SwiftTweaks Adjust your iOS app on the fly without waiting to re-compile! Your users won’t see your animation study, Sketch comps, or prototypes. What

Khan Academy 1.4k Dec 28, 2022
Updeto is a simple package that help update checker for iOS Apps

Updeto is a simple package that will help you to check if the currently installed version is the same as the latest one available on App Store.

Manuel Sánchez 8 Jul 8, 2022
SharkUtils is a collection of Swift extensions, handy methods and syntactical sugar that we use within our iOS projects at Gymshark.

SharkUtils is a collection of Swift extensions, handy methods and syntactical sugar that we use within our iOS projects at Gymshark.

Gymshark 1 Jul 6, 2021
Recreating a fully functional version of iOS 4 in SwiftUI.

Updates: Version 1.0 is currently available ?? While I work on fixing an issue in Xcode 12.5+ with LazyVGrid, I recommend you build using Xcode 12.4 a

null 2.9k Jan 1, 2023
Helpful extensions for iOS app development 🚀

ExtensionKit includes many extensions, from getting the user location with a deterministic Combine API to a shimmer loading animation, to keyboard notification updates, bottom sheet and much much more. Check out the docs below or install the library with SPM to try it out.

Gary Tokman 110 Oct 31, 2022
Experimental iOS Arm64 hooking

ClownHook Experimental hooking on Arm64. Status Experimenting Setup Install xcodegen to generate an Xcode project. brew install xcodegen Create the X

AeonLucid 8 Jun 15, 2022
Validate iOS, Android, and Mac localizations. Find errors in .strings, .stringsdict, and strings.xml files.

Locheck An Xcode and Android localization file validator. Make sure your .strings, .stringsdict, and strings.xml files do not have any errors! What do

Asana 73 Dec 13, 2022
An iOS app/Safari extension to automagically redirect AMP links to their normal counterpart. Comes with a trusty dog.

Amplosion ⚡️ Amplosion is an iOS 15 and greater app that automagically redirects AMP links to their normal counterpart. AMP links can be super annoyin

Christian Selig 46 Dec 11, 2022
Read iOS 15 privacy insight '.ndjson' file into your human brain.

Insight Read iOS 15 privacy insight '.ndjson' file into your human brain. Written in SwiftUI. Feature Compile records into app summary Relink app info

Lakr Aream 151 Nov 11, 2022
🟣 Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.

Verge is giving the power of state-management in muukii/Brightroom v2 development! Verge.swift ?? An effective state management architecture for iOS -

VergeGroup 478 Dec 29, 2022
Azure Maps iOS SDK binary distribution for Swift Package Manager

Azure Maps Control for iOS Installation In your Xcode iOS Project settings, under Project setting’s Package Dependencies, click on + button to add pac

Microsoft Azure 2 Nov 1, 2021