[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: tbodt\ngithub: tbodt\ncustom: https://www.paypal.me/tbodt\n"
  },
  {
    "path": ".github/workflows/autolabel.yml",
    "content": "name: Auto-Label\non:\n  issues:\n    types: [opened]\njobs:\n  label:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: satackey/action-js-inline@bf6fcaf35de1ed03bcfd25a0a8b1fa4c551ec908\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          script: |\n            const core = require('@actions/core');\n            const github = require('@actions/github');\n            const octokit = github.getOctokit(process.env.GITHUB_TOKEN);\n            const {repo, owner, number} = github.context.issue;\n            await octokit.rest.issues.addLabels({repo, owner, issue_number: number, labels: ['unconfirmed']});\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n      branches: [master]\n\njobs:\n  build-linux:\n    runs-on: ubuntu-20.04\n    strategy:\n      matrix:\n        cc: [clang, gcc]\n        kernel: [ish, linux]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n            submodules: true\n      - uses: actions/setup-python@v2\n        with:\n            python-version: '3.x'\n      - name: Install dependencies\n        run: |\n          sudo apt-get update\n          sudo apt-get install libarchive-dev\n          pip3 install meson ninja\n      - name: Clone Linux\n        if: matrix.kernel == 'linux'\n        run: deps/clone-linux.sh\n      - name: Build\n        run: |\n          meson build -Dengine=asbestos -Dkernel=${{matrix.kernel}}\n          ninja -C build\n        env:\n          CC: ${{matrix.cc}}\n      - name: Test\n        if: matrix.kernel == 'ish'\n        run: ninja -C build test\n\n  build-mac:\n    runs-on: macos-15\n    strategy:\n      matrix:\n        kernel: [ish, linux]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n            submodules: true\n      - name: Install dependencies\n        run: |\n          brew install llvm lld ninja libarchive meson\n      - name: Clone Linux\n        if: matrix.kernel == 'linux'\n        run: deps/clone-linux.sh\n      - name: Build\n        if: matrix.kernel == 'ish'\n        run: xcodebuild -project iSH.xcodeproj -scheme iSH -arch arm64 -sdk iphoneos CODE_SIGNING_ALLOWED=NO\n      - name: Build\n        if: matrix.kernel == 'linux'\n        run: xcodebuild -project iSH.xcodeproj -scheme iSH+Linux -arch x86_64 -sdk iphonesimulator CODE_SIGNING_ALLOWED=NO\n"
  },
  {
    "path": ".github/workflows/deploy-site.yml",
    "content": "name: Deploy Site\non:\n  release:\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Trigger Deploy\n      run: curl -d {} $NETLIFY_DEPLOY_HOOK\n      env:\n        NETLIFY_DEPLOY_HOOK: ${{ secrets.NETLIFY_DEPLOY_HOOK }}\n"
  },
  {
    "path": ".github/workflows/update-alpine-repo.yml",
    "content": "name: \"Update Repo\"\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 0 * * 6\"\n\njobs:\n  update:\n    if: github.repository == 'ish-app/ish'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Setup dependencies\n        run: |\n          curl https://rclone.org/install.sh | sudo bash\n          mkdir -p ~/.config/rclone\n          echo \"$RCLONE_CONFIG\" > ~/.config/rclone/rclone.conf\n        env:\n          RCLONE_CONFIG: ${{ secrets.RCLONE_CONFIG }}\n      - name: Update\n        run: |\n          deps/aports/sync-archive.sh v3.14 main/x86 deps/aports\n          deps/aports/sync-archive.sh v3.14 community/x86 deps/aports\n          deps/aports/sync-archive.sh v3.17 main/x86 deps/aports\n          deps/aports/sync-archive.sh v3.17 community/x86 deps/aports\n          deps/aports/sync-archive.sh v3.18 main/x86 deps/aports\n          deps/aports/sync-archive.sh v3.18 community/x86 deps/aports\n          deps/aports/sync-archive.sh v3.19 main/x86 deps/aports\n          deps/aports/sync-archive.sh v3.19 community/x86 deps/aports\n      - name: Commit\n        id: commit\n        run: |\n          git config user.name github-actions[bot]\n          git config user.email 41898282+github-actions[bot]@users.noreply.github.com\n          git add deps/aports\n          git commit -m \"Update Alpine repositories\"\n          # TODO add a summary of changes in the commit description\n        continue-on-error: true\n      - name: Push\n        if: ${{ steps.commit.outcome == 'success' }}\n        run: git push\n"
  },
  {
    "path": ".github/workflows/upload-build.yml",
    "content": "name: Upload Build\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 6 * * *\"\n\njobs:\n  upload-build:\n    if: github.repository == 'ish-app/ish'\n    runs-on: macos-15\n    timeout-minutes: 720\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: true\n          fetch-depth: 0\n\n      - name: Install deps\n        run: |\n          brew install ninja lld llvm meson\n          bundle install\n          git config user.name github-actions[bot]\n          git config user.email 41898282+github-actions[bot]@users.noreply.github.com\n\n      - name: Fastlane\n        timeout-minutes: 720\n        run: script fastlane.log bundle exec fastlane upload_build\n        env:\n          APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}\n          APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}\n          APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }}\n          MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GITHUB_AUTH }}\n          MATCH_PASSWORD: ${{ secrets.MATCH_PASSPHRASE }}\n          GH_TOKEN: ${{ secrets.GH_TOKEN }}\n          SLACK_URL: ${{ secrets.SLACK_URL }}\n          FASTLANE_SKIP_UPDATE_CHECK: 1\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: App\n          path: |\n            iSH.ipa\n            iSH.app.dSYM.zip\n\n      - uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: Fastlane Logs\n          path: |\n            fastlane.log\n            ~/Library/Logs/gym\n\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nxcuserdata/\n.floo\n\ncross-*.txt\nroot.tar.gz\n\nnode_modules\napp/xtermjs/xterm-dist\napp/xtermjs/.cache\n\nsubprojects/\n!subprojects/*.wrap\n\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots\nfastlane/test_output\n\ne2e_out/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"libapps\"]\n\tpath = deps/libapps\n\turl = https://github.com/ish-app/libapps\n[submodule \"deps/libarchive\"]\n\tpath = deps/libarchive\n\turl = https://github.com/libarchive/libarchive\n[submodule \"deps/linux\"]\n\tpath = deps/linux\n\turl = https://github.com/ish-app/linux\n\tupdate = none\n\tshallow = true\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"https://rubygems.org\"\ngem \"fastlane\"\ngem \"dotenv\"\ngem \"pry\"\n\ngem \"abbrev\"\ngem \"irb\"\ngem \"rdoc\"\ngem \"mutex_m\"\ngem \"ostruct\"\ngem \"logger\"\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "<!--\nIf you're reporting a crash, please include a crash dump. You can find them in Settings -> Privacy -> Analytics -> Analytics Data on iOS 12, or Settings -> Privacy -> Diagnostics and Usage on iOS 11.\nIf this is a \"Bad system call\", \"Illegal instruction\", or \"Segmentation fault\", run `dmesg` to get a dump of the log messages and include the relevant output.\n-->\n"
  },
  {
    "path": "LICENSE.IOS",
    "content": "The iSH developers are aware that the terms of service that apply to\napps distributed via Apple's App Store services may conflict with\nrights granted under the iSH license, the GNU General Public License,\nversion 2 or 3. The copyright holders of the iSH app do not wish this\nconflict to prevent the otherwise-compliant distribution of derived\napps via the App Store. Therefore, we have committed not to pursue\nany license violation that results solely from the conflict between\nthe GNU GPLv2 or v3 and the Apple App Store terms of service. In other\nwords, as long as you comply with the GPL in all other respects,\nincluding its requirements to provide users with source code and the\ntext of the license, we will not object to your distribution of the\niSH app through the App Store.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "iSH is licensed under the [GPLv3][]. The additional terms in LICENSE.IOS also apply.\n\nContributions made after commit 0e3a4144f93135c4fd618c8397d2cfd87194f69f are\nadditionally licensed under the [GPLv2][]. This is intended to allow linking\nwith GPLv2 licensed projects such as Linux and QEMU.\n\nThe following authors have agreed to relicense their past contributions under GPLv2:\n- Theodore Dubois <tblodt@icloud.com>\n- Saagar Jha <saagar@saagarjha.com>\n- Christoffer Tønnessen <christoffertonnessen@icloud.com> <christoffer.tonnessen@gmail.com>\n- Philipp Wallisch <philipp.wallisch@inode.at>\n- Ed Luff <beartechtalks@gmail.com>\n- David Southgate <d@davidsouthgate.co.uk>\n- Charlie Melbye <charles.melbye@gmail.com>\n- David <0b101@users.noreply.github.com>\n- [as@irc](https://gist.github.com/tbodt/45ccbea8d3c095258d63f611654f05b4)\n- asdfugil (name was \"Assfugil\" when last contributed) <towinchenmi@gmail.com> <42699250+Assfugil@users.noreply.github.com>\n- AngeloHYang <38714377+AngeloHYang@users.noreply.github.com>\n- Matthew Merrill <mattmerr47@gmail.com>\n- Siddharth Dushantha <siddharth.dushantha@gmail.com>\n- Lorenzo De Linares <lorenzo.linares@icloud.com>\n- Christopher Albert <albert@alumni.tugraz.at>\n- Stephen Leaf <stephenaleaf@gmail.com>\n- Noah Peeters <noah@noahpeeters.de>\n- Alexis Marquis <alexis@marquis.me>\n- Brian Almeida <bma@thunderkeys.net>\n- Viktor Oreshkin <imselfish@stek29.rocks>\n- Ryan Hileman <lunixbochs@gmail.com>\n- Christoforos Charalambous <chrischaralambous14@gmail.com>\n- Kenta Kubo <kabuto669@icloud.com>\n- Zhuowei Zhang\n- never_released <24752637+woachk@users.noreply.github.com>\n\n[GPLv3]: https://www.gnu.org/licenses/gpl-3.0.html\n[GPLv2]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html\n"
  },
  {
    "path": "README.md",
    "content": "# [iSH](https://ish.app)\n\n[![Build Status](https://github.com/ish-app/ish/actions/workflows/ci.yml/badge.svg)](https://github.com/ish-app/ish/actions)\n[![goto counter](https://img.shields.io/github/search/ish-app/ish/goto.svg)](https://github.com/ish-app/ish/search?q=goto)\n[![fuck counter](https://img.shields.io/github/search/ish-app/ish/fuck.svg)](https://github.com/ish-app/ish/search?q=fuck)\n[![shit counter](https://img.shields.io/github/search/ish-app/ish/shit.svg)](https://github.com/ish-app/ish/search?q=shit)\n\n<p align=\"center\">\n<a href=\"https://ish.app\">\n<img src=\"https://ish.app/assets/github-readme.png\">\n</a>\n</p>\n\nA project to get a Linux shell running on iOS, using usermode x86 emulation and syscall translation.\n\nFor the current status of the project, check the issues tab, and the commit logs.\n\n- [App Store page](https://apps.apple.com/us/app/ish-shell/id1436902243)\n- [TestFlight beta](https://testflight.apple.com/join/97i7KM8O)\n- [Discord server](https://discord.gg/HFAXj44)\n- [Wiki with help and tutorials](https://github.com/ish-app/ish/wiki)\n- [README中文](https://github.com/ish-app/ish/blob/master/README_ZH.md) (如若未能保持最新，请提交PR以更新)\n\n# Hacking\n\nThis project has a git submodule, make sure to clone with `--recurse-submodules` or run `git submodule update --init` after cloning.\n\nYou'll need these things to build the project:\n\n - Python 3\n   + Meson (`pip3 install meson`)\n - Ninja\n - Clang and LLD (on mac, `brew install llvm`, on linux, `sudo apt install clang lld` or `sudo pacman -S clang lld` or whatever)\n - sqlite3 (this is so common it may already be installed on linux and is definitely already installed on mac. if not, do something like `sudo apt install libsqlite3-dev`)\n - libarchive (`brew install libarchive`, `sudo port install libarchive`, `sudo apt install libarchive-dev`) TODO: bundle this dependency\n\n## Build for iOS\n\nOpen the project in Xcode, open iSH.xcconfig, and change `ROOT_BUNDLE_IDENTIFIER` to something unique. You'll also need to update the development team ID in the project (not target!) build settings. Then click Run. There are scripts that should do everything else automatically. If you run into any problems, open an issue and I'll try to help.\n\n## Build command line tool for testing\n\nTo set up your environment, cd to the project and run `meson build` to create a build directory in `build`. Then cd to the build directory and run `ninja`.\n\nTo set up a self-contained Alpine linux filesystem, download the Alpine minirootfs tarball for i386 from the [Alpine website](https://alpinelinux.org/downloads/) and run `./tools/fakefsify`, with the minirootfs tarball as the first argument and the name of the output directory as the second argument. Then you can run things inside the Alpine filesystem with `./ish -f alpine /bin/sh`, assuming the output directory is called `alpine`. If `tools/fakefsify` doesn't exist for you in your build directory, that might be because it couldn't find libarchive on your system (see above for ways to install it.)\n\nYou can replace `ish` with `tools/ptraceomatic` to run the program in a real process and single step and compare the registers at each step. I use it for debugging. Requires 64-bit Linux 4.11 or later.\n\n## Logging\n\niSH has several logging channels which can be enabled at build time. By default, all of them are disabled. To enable them:\n\n- In Xcode: Set the `ISH_LOG` setting in iSH.xcconfig to a space-separated list of log channels.\n- With Meson (command line tool for testing): Run `meson configure -Dlog=\"<space-separated list of log channels>\"`.\n\nAvailable channels:\n\n- `strace`: The most useful channel, logs the parameters and return value of almost every system call.\n- `instr`: Logs every instruction executed by the emulator. This slows things down a lot.\n- `verbose`: Debug logs that don't fit into another category.\n- Grep for `DEFAULT_CHANNEL` to see if more log channels have been added since this list was updated.\n\n# A note on the interpreter\n\nPossibly the most interesting thing I wrote as part of iSH is the interpreter. It's not quite a JIT since it doesn't target machine code. Instead it generates an array of pointers to functions called gadgets, and each gadget ends with a tailcall to the next function; like the threaded code technique used by some Forth interpreters. The result is a speedup of roughly 3-5x compared to emulation using a simpler switch dispatch.\n\nUnfortunately, I made the decision to write nearly all of the gadgets in assembly language. This was probably a good decision with regards to performance (though I'll never know for sure), but a horrible decision with regards to readability, maintainability, and my sanity. The amount of bullshit I've had to put up with from the compiler/assembler/linker is insane. It's like there's a demon in there that makes sure my code is sufficiently deformed, and if not, makes up stupid reasons why it shouldn't compile. In order to stay sane while writing this code, I've had to ignore best practices in code structure and naming. You'll find macros and variables with such descriptive names as `ss` and `s` and `a`. Assembler macros nested beyond belief. And to top it off, there are almost no comments.\n\nSo a warning: Long-term exposure to this code may cause loss of sanity, nightmares about GAS macros and linker errors, or any number of other debilitating side effects. This code is known to the State of California to cause cancer, birth defects, and reproductive harm.\n"
  },
  {
    "path": "README_JP.md",
    "content": "# [iSH](https://ish.app)\n\n[![Build Status](https://github.com/ish-app/ish/actions/workflows/ci.yml/badge.svg)](https://github.com/ish-app/ish/actions)\n[![goto counter](https://img.shields.io/github/search/ish-app/ish/goto.svg)](https://github.com/ish-app/ish/search?q=goto)\n[![fuck counter](https://img.shields.io/github/search/ish-app/ish/fuck.svg)](https://github.com/ish-app/ish/search?q=fuck)\n[![shit counter](https://img.shields.io/github/search/ish-app/ish/shit.svg)](https://github.com/ish-app/ish/search?q=shit)\n\n<p align=\"center\">\n<a href=\"https://ish.app\">\n<img src=\"https://ish.app/assets/github-readme.png\">\n</a>\n</p>\n\niSHは、ユーザーモードのx86エミュレーションとシステムコールの翻訳を使用して、iOS上でLinuxシェルを実行するプロジェクトです。\n\nプロジェクトの現状については、issueタブとコミットログを確認してください。\n\n- [App Storeページ](https://apps.apple.com/us/app/ish-shell/id1436902243)\n- [TestFlightベータ](https://testflight.apple.com/join/97i7KM8O)\n- [Discordサーバー](https://discord.gg/HFAXj44)\n- [ヘルプとチュートリアルのWiki](https://github.com/ish-app/ish/wiki)\n\n# ハッキング\n\nこのプロジェクトにはgitサブモジュールがあります。`--recurse-submodules`を使用してクローンするか、クローン後に`git submodule update --init`を実行してください。\n\nプロジェクトをビルドするには、以下のものが必要です：\n\n - Python 3\n   + Meson (`pip3 install meson`)\n - Ninja\n - ClangとLLD（macでは`brew install llvm`、linuxでは`sudo apt install clang lld`または`sudo pacman -S clang lld`など）\n - sqlite3（これは非常に一般的で、linuxではすでにインストールされているかもしれませんし、macでは確実にインストールされています。もしインストールされていない場合は、`sudo apt install libsqlite3-dev`などを実行してください）\n - libarchive（`brew install libarchive`、`sudo port install libarchive`、`sudo apt install libarchive-dev`など）\n\n## iOS用にビルドする\n\nプロジェクトをXcodeで開き、iSH.xcconfigを開いて、`ROOT_BUNDLE_IDENTIFIER`を一意の値に変更します。また、プロジェクト（ターゲットではなく！）のビルド設定で開発チームIDを更新する必要があります。その後、実行をクリックします。他のすべてを自動的に行うスクリプトがあります。問題が発生した場合は、issueを開いてください。お手伝いします。\n\n## テスト用のコマンドラインツールをビルドする\n\n環境を設定するには、プロジェクトディレクトリに移動し、`meson build`を実行して`build`ディレクトリを作成します。その後、buildディレクトリに移動し、`ninja`を実行します。\n\n自己完結型のAlpine linuxファイルシステムを設定するには、[Alpineウェブサイト](https://alpinelinux.org/downloads/)からi386用のAlpine minirootfs tarballをダウンロードし、`./tools/fakefsify`を実行します。minirootfs tarballを最初の引数として、出力ディレクトリの名前を2番目の引数として指定します。その後、`./ish -f alpine /bin/sh`を使用して、Alpineファイルシステム内でコマンドを実行できます。出力ディレクトリの名前が`alpine`であると仮定します。`tools/fakefsify`がbuildディレクトリに存在しない場合、それはシステム上でlibarchiveを見つけられなかったためかもしれません（インストール方法については上記を参照してください）。\n\n`ish`を`tools/ptraceomatic`に置き換えることで、実際のプロセスでプログラムを実行し、各ステップでレジスタを比較しながらシングルステップ実行できます。デバッグに使用します。64ビットLinux 4.11以降が必要です。\n\n## ロギング\n\niSHには、ビルド時に有効にできるいくつかのロギングチャネルがあります。デフォルトでは、すべて無効になっています。有効にするには：\n\n- Xcodeで：iSH.xcconfigの`ISH_LOG`設定をスペースで区切られたログチャネルのリストに設定します。\n- Meson（テスト用のコマンドラインツール）で：`meson configure -Dlog=\"<ログチャネルのスペース区切りリスト>\"`を実行します。\n\n利用可能なチャネル：\n\n- `strace`：最も有用なチャネルで、ほぼすべてのシステムコールのパラメータと戻り値をログに記録します。\n- `instr`：エミュレータが実行するすべての命令をログに記録します。これにより、実行速度が大幅に低下します。\n- `verbose`：他のカテゴリに該当しないデバッグログを記録します。\n- `DEFAULT_CHANNEL`をgrepして、このリストが更新された後に追加されたログチャネルがあるかどうかを確認します。\n\n# JITに関する注意事項\n\niSHの一部として書いた中で最も興味深いものの1つはJITです。実際には、マシンコードをターゲットにしていないため、実際のJITではありません。代わりに、ガジェットと呼ばれる関数へのポインタの配列を生成し、各ガジェットは次の関数へのテールコールで終了します。これは、一部のForthインタープリタが使用するスレッド化コード技術に似ています。その結果、純粋なエミュレーションと比較して、速度が約3〜5倍向上します。\n\n残念ながら、ほぼすべてのガジェットをアセンブリ言語で書くという決定を下しました。これは、パフォーマンスに関してはおそらく良い決定でしたが（確かではありませんが）、可読性、保守性、および私の正気に関してはひどい決定でした。コンパイラ、アセンブラ、リンカからのたくさんの問題に対処しなければなりませんでした。コードが十分に変形していることを確認し、そうでない場合は、コンパイルできない理由をでっち上げる悪魔がいるようなものです。このコードを書いている間に正気を保つために、コード構造と命名のベストプラクティスを無視しなければなりませんでした。`ss`、`s`、`a`などの説明的な名前を持つマクロや変数が見つかるでしょう。信じられないほどネストされたアセンブラマクロ。そして、ほとんどコメントがありません。\n\nしたがって、警告です：このコードに長期間さらされると、正気を失い、GASマクロやリンカエラーについての悪夢に悩まされる可能性があります。カリフォルニア州では、このコードが癌、先天性欠損症、および生殖障害を引き起こすことが知られています。\n"
  },
  {
    "path": "README_KO.md",
    "content": "# [iSH](https://ish.app)\n\n[![Build Status](https://github.com/ish-app/ish/actions/workflows/ci.yml/badge.svg)](https://github.com/ish-app/ish/actions)\n[![goto counter](https://img.shields.io/github/search/ish-app/ish/goto.svg)](https://github.com/ish-app/ish/search?q=goto)\n[![fuck counter](https://img.shields.io/github/search/ish-app/ish/fuck.svg)](https://github.com/ish-app/ish/search?q=fuck)\n[![shit counter](https://img.shields.io/github/search/ish-app/ish/shit.svg)](https://github.com/ish-app/ish/search?q=shit)\n\n<p align=\"center\">\n<a href=\"https://ish.app\">\n<img src=\"https://ish.app/assets/github-readme.png\">\n</a>\n</p>\n\n사용자 모드 x86 에뮬레이션과 시스템 call 번역을 사용하여 iOS 에서 리눅스 쉘을 실행할 수 있게 해줍니다.\n\n프로젝트의 현황을 알고 싶으시면 커밋 로그와 이슈 탭을 참고해주세요.\n\n- [애플 앱스토어](https://apps.apple.com/us/app/ish-shell/id1436902243)\n- [TestFlight beta](https://testflight.apple.com/join/97i7KM8O)\n- [Discord server](https://discord.gg/HFAXj44)\n- [도움 문서 Wiki](https://github.com/ish-app/ish/wiki)\n- [README중문](https://github.com/ish-app/ish/blob/master/README_ZH.md)\n\n\n# Hacking\n\n해당 프로젝트는 깃의 서브 모듈이 있습니다. 해당 저장소를 받은 후 `--recurse-submodules` 또는 `git submodule update --init` 을 입력하여 깃 서브 모듈을 클론하세요.\n\n아래 사항은 이 프로젝트를 빌드하기 위해 필요한 것들 입니다:\n\n - Python 3\n   + Meson (`pip3 install meson`)\n - Ninja\n - Clang and LLD (맥에서는, `brew install llvm`, 리눅스에서는, `sudo apt install clang lld` 또는 `sudo pacman -S clang lld` 을 실행하세요)\n - sqlite3 (맥에서는 이미 제공 되어 있을 확률이 높습니다. 만약 그렇지 않다면 `sudo apt install libsqlite3-dev`)\n - libarchive (`brew install libarchive`, `sudo port install libarchive`, `sudo apt install libarchive-dev`) TODO: 앞에 dependency를 번들링 하기\n\n## iOS 로 빌드하는 법 \n\nXcode로 프로젝트를 열고, iSH.xcconfig 연 후에 `ROOT_BUNDLE_IDENTIFIER`를 해당 프로젝트에 유일한 값으로 바꾸세요. 그후 실행을 누르면 자동으로 나머지를 세팅해줄 스크립트가 제공되어 있습니다. 만약 문제가 생긴다면, issue open을 해주시면 도와드리겠습니다.\n\n\n## 테스트를 위한 cli 도구 빌드하는 법\n\n환경을 세팅하기 위해서는 프로젝트 디렉토리로 이동하고 `meson build`를 커맨드 라인에 입력하세요. 그 후 빌드 된 디렉토리로 cd 후 `ninja` 커맨드를 입력해 실행하세요.\n\n자체적으로 컨테이너 화 된 Alpine 리눅스 파일 시스템으로 실행하고 싶다면, [Alpine 웹사이트](https://alpinelinux.org/downloads/) 에서 i386을 위한 Alpine minirootfs(Mini Root Filesystem) tarball 을 다운로드 받고 `./tools/fakefsify`으로 실행하세요. 매개인자로 다운로드 받은 minirootfs tarball 파일을 입력하고 출력 받을 디렉토리의 이름을 두번째 인자로 입력하면 됩니다. 그 후에는 `./ish -f {출력받을 디렉토리 이름} /bin/sh` 명령어를 사용하여 Alpine 시스템 내에서 원하는 것을 실행할 수 있습니다. 만약 `tools/fakefsify` 가 빌드 디렉토리에 존재하지 않는다면, libarchive를 찾을 수 없어서 그런 것일 수 있습니다. 위를 참고하여 시스템에 설치하는 방법을 참고해주세요.\n\n실제 프로세스로 프로그램을 실행하고 각 단계의 레지스터를 비교하기 위해서 `ish`를 `tools/ptraceomatic`로 바꿔 실행할 수 있습니다. 디버깅을 위해 저는 사용합니다. 64-bit Linux 4.11 이후 버전이 필요합니다.\n\n## 로깅\n\niSH 는 빌드 시간에 허용될 수 있는 다수의 로깅 채널을 갖고 있습니다. 기본 값으로는 모두 꺼놨는데, 사용을 위해서는:\n\n- Xcode에서: iSH.xcconfig에 있는 `ISH_LOG` 값을 스페이스로 나뉜 로그 채널 리스트로 설정해주세요.\n- Meson에서 (테스트를 위한 커맨드 라인 도구): `meson configure -Dlog=\"<스페이스로 나뉜 로그 채널 리스트>\"`을 실행하세요.\n\n제공되는 로그 채널:\n\n- `strace`: 가장 쓸모있는 채널입니다. 매개변수와 거의 모든 시스템 호출의 반환 값을 로깅합니다.\n- `instr`: 에뮬레이터에서 실행된 모든 명령어를 로깅합니다. 이로인해 성능저하가 일어날 수 있습니다.\n- `verbose`: 다른 카타고리에 들지 않는 로그를 디버깅합니다.\n- `DEFAULT_CHANNEL`을 찾아보면 리스트가 업데이트 이후 새로 추가된 로그 채널을 볼수 있습니다.\n\n# JIT(Just In Time 컴파일러)에 대한 추가사항\n\niSH에서 추가한 것 중 가장 흥미로운 것은 JIT 컴파일러 일 것입니다. 기계 코드를 목적으로 하지 않기 때문에 JIT 실질적으로는 아니긴 합니다. Gadget 이라고 불리는 포인터 배열을 생성하는데, 각각의 이것은 다음 함수를 호출하는 꼬리물기를 합니다. 몇몇 Forth 언어 인터프리터에서 사용된 스레드 코드처럼 말이죠. 결과적으로 순수 에뮬보다 3-5배 더 빨라졌습니다.\n\n불행하게도 저는 어셈블리어로 대부분의 이러한 gadget을 작성했습니다. 이것은 성능적으로는 좋은 선택이었을 지 몰라도(실제로는 알 도리가 없지만), 가독성, 유지보수, 그리고 제 정신상태에 대해서는 좋지 않은 선택이 되었습니다. 컴파일러/어셈블러/링커로 인한 여러 고충은 말도 할 수 없을 정도입니다. 거의 무슨 제 코드의 가독성을 해치지 않으면 컴파일을 막는 그러한 악마가 있는 것 같았습니다. 이 코드를 작성하는 도중 제정신을 유지하기 위해서 저는 네이밍과 코드 구조론을 따른 최적의 선택을 하지 못하였습니다. `ss`, `s` 그리고 `a`와 같은 매크로 그리고 변수 명을 찾을 수 있을 것입니다. 주석 또한 찾기 힘들 것입니다.\n\n그렇기에 주의 하세요: 해당 코드를 장기간 접할 경우 정신질환을 앓게되거나 GAS 매크로와 링커오류에 대한 악몽에 시달리고 또다른 부작용이 있을 수 있습니다. 암, 선천적 결함, 또는 생식기 질환을 야기한다고 질병관리청에서 인정했습니다. 암튼 그랬습니다.\n"
  },
  {
    "path": "README_ZH.md",
    "content": "# [iSH](https://ish.app)\n\n[![Build Status](https://github.com/ish-app/ish/actions/workflows/ci.yml/badge.svg)](https://github.com/ish-app/ish/actions)\n[![goto counter](https://img.shields.io/github/search/ish-app/ish/goto.svg)](https://github.com/ish-app/ish/search?q=goto)\n[![fuck counter](https://img.shields.io/github/search/ish-app/ish/fuck.svg)](https://github.com/ish-app/ish/search?q=fuck)\n[![shit counter](https://img.shields.io/github/search/ish-app/ish/shit.svg)](https://github.com/ish-app/ish/search?q=shit)\n\n<p align=\"center\">\n<a href=\"https://ish.app\">\n<img src=\"https://ish.app/assets/github-readme.png\">\n</a>\n</p>\n\niSH 是一个运行在 iOS 上的 Linux shell。本项目使用了 x86 用户模式仿真和系统调用翻译转换。\n\n请查看 issue 和提交记录以了解本项目当前的状态。\n\n- [App Store 页面](https://apps.apple.com/us/app/ish-shell/id1436902243)\n- [Testflight 测试](https://testflight.apple.com/join/97i7KM8O)\n- [Discord 服务器](https://discord.gg/HFAXj44)\n- [维基帮助与教程](https://github.com/ish-app/ish/wiki)\n\n# 上手\n\n本项目下包含了其他 git 项目作为子模块，请确保在克隆时使用参数`--recurse-submodules`，即 `git clone --recurse-submodules https://github.com/ish-app/ish.git`。或是在克隆好了之后执行 `git submodule update --init`。\n\n编译此项目需要以下依赖:\n\n - Python 3\n    + Meson (`pip3 install meson`)\n - Ninja 请查看[此处](https://ninja-build.org/)\n - Clang and LLD (在安装了 `brew` 的 macOS 系统上运行 `brew install llvm`。在 Linux 系统上请根据你的包管理器，选择运行相应的安装命令 `sudo apt install clang lld` 或者 `sudo pacman -S clang lld`)\n - sqlite3 (通常 sqlite3 在 macOS 上是预安装的，但它或许没有安装在你的 Linux 上，运行 `which sqlite3` 以查看它是否存在。如果没有，你可以根据你的包管理器运行 `sudo apt install libsqlite3-dev` 之类的安装命令)\n - libarchive (在 macOS 系统上使用 `brew install libarchive` 或 `sudo port install libarchive` 来安装。在 Linux 系统上请根据你的包管理器，选择运行相应的安装命令如 `sudo apt install libarchive-dev` 来安装)\n\n## 创建iOS应用\n\n使用 Xcode 打开项目，选择 iSH.xcconfig，并且修改 `ROOT_BUNDLE_IDENTIFIER` 为你的[唯一值](https://help.apple.com/xcode/mac/current/#/dev91fe7130a)。此外，还需要在项目（project）的构建设置（build settings）中更新开发团队 ID，注意这里指的不是目标（target）的构建设置（build settings）。然后点击 `运行`，之后应该有脚本帮你自动执行相关操作。如果遇到了任何问题，请提交 issue，我们会帮你解决。\n\n## 为测试构建命令行工具\n\n在项目目录中运行命令 `meson build`，之后 `build` 目录会被创建。进入到 `build` 目录并运行命令 `ninja`。\n\n为了建立一个自有的 Alpine linux 文件系统，请从 [Alpine 网站](https://alpinelinux.org/downloads/) 下载 `Alpine minirotfs tarball for i386` 并运行 `tools/fakefsify` 。将 minirotfs tarball 指定为第一个参数，将输出目录的名称（如`alpine`）指定为第二个参数，即 `tools/fakefsify $MinirotfsTarballFilename alpine` 然后在 Alpine 文件系统中运行 `/ish -f alpine/bin/sh`。如果 `build` 目录下找不到 `tools/fakefsify`，可能是系统上找不到 `libarchive` 的依赖（请参照前面的章节进行安装）。\n\n除了可以使用 `ish`，你也可以使用 `tools/ptraceomatic` 替代它，以便在某个真实进程中单步比较寄存器。我通常使用它来进行调试（需要 64 位 Linux 4.11 或更高版本）。\n\n## 日志\n\n在编译过程中，iSH 提供数种日志类型，默认情况下它们都被禁用，想要启用它们需要:\n\n- 在 Xcode 中将 iSH.xcconfig 中 `ISH_LOG` 设置为以空格分隔的日志类型列表。\n- 在 Meson (测试使用的命令行工具) 中执行命令 `meson configure -Dlog=\"<space-separated list of log channels>\"`。\n\n可用的日志类型:\n\n- `strace`: 最有用的类型，记录几乎每个系统调用的参数和返回值。\n- `instr`: 记录模拟器执行的每个指令，这会让所有执行变得很慢。\n- `verbose`: 记录不属于其他类别的调试日志。\n- 使用 `grep` 命令查看 `DEFAULT_CHANNEL` 变量，以确认在更新此列表后是否添加了更多日志频道。\n\n# 关于 JIT\n\n可能我在写 iSH 中最有趣的部分就是 JIT 了。实际上它不是真正的 JIT，因为它不并以机器代码为目标，而是生成一个称为 gadgets 的函数指针数组，并且每个 gadget 都以对下一个函数的尾调用结束，类似于一些 Forth 解释器使用的线程化代码技术。好处就是，与纯仿真相比，它的速度提高了 3-5 倍。\n\n但不幸的是，我最开始决定用汇编语言编写几乎所有的 gadgets。这可能从性能方面来说是一个好的决定（虽然我永远也无法确定），但是对可读性、可维护性和我的理智来说，这是一个可怕的决定。我承受了大量来自编译器、汇编程序以及链接器的乱七八糟的东西。那里面就像有一个魔鬼，把我的代码搞得畸形，就算没有畸形，也会编造一些愚蠢的理由说它不能够编译。为了在编写代码时保持理智，我不得不忽略代码结构和命名方面的最佳实践。你会发现宏和变量具有诸如 `ss`、`s` 和 `a` 等描述性的名称，并且汇编器的宏嵌套层数超乎你的想象。最重要的是，代码中几乎没有任何注释。\n\n所以这是一个警告: 长期接触此代码可能会使你失去理智，对 GAS 宏和链接器错误产生噩梦，或是任何其他使人虚弱的副作用。在加利福尼亚，众所周知这样的代码会导致癌症、生产缺陷和重复伤害。\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# iSH is not a security boundary!\n\nThe goal of this project is to support a Linux shell on iOS. As such, its security model assumes that the app is running in another sandbox and is used by a single user. The project is focused on compatibility, and very little thought has been put into internal security. Permissions are only loosely checked. Memory corruption in edge cases is common. Please do not use iSH for any sort of secure containerization or production use case.\n\nAs such, most types of bugs that are security issues in most projects are not security issues in iSH. Insufficient permission checks, memory corruption, and thread safety issues are generally considered correctness bugs and would be best filed as GitHub issues. We will prioritize bugs encountered by real programs in typical use.\n\nIn our security model, we expect real security bugs to be very rare. It's not completely impossible, e.g. a bug allowing remote code execution without user consent would be a security bug. If you think you found one, you can send it to security@ish.app. We'll work with you to resolve it appropriately.\n"
  },
  {
    "path": "app/AboutAppearanceViewController.h",
    "content": "//\n//  ThemeViewController.h\n//  iSH\n//\n//  Created by Charlie Melbye on 11/12/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface AboutAppearanceViewController : UITableViewController <UIFontPickerViewControllerDelegate>\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/AboutAppearanceViewController.m",
    "content": "//\n//  ThemeViewController.m\n//  iSH\n//\n//  Created by Charlie Melbye on 11/12/18.\n//\n\n#import \"AboutAppearanceViewController.h\"\n#import \"FontPickerViewController.h\"\n#import \"TerminalView.h\"\n#import \"ThemesViewController.h\"\n#import \"UserPreferences.h\"\n#import \"NSObject+SaneKVO.h\"\n\n@interface AboutAppearanceViewController ()\n@property (strong, nonatomic) IBOutlet UISwitch *blinkCursor;\n@property (strong, nonatomic) IBOutlet UISegmentedControl *cursorStyle;\n@property (strong, nonatomic) IBOutlet UISwitch *hideStatusBar;\n@property UIFontPickerViewController *fontPicker API_AVAILABLE(ios(13));\n@end\n\nchar *previewString = \"# cat /proc/ish/colors\\r\\n\"\n\"\\x1B[30m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[31m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[32m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[33m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[34m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[35m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[36m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[37m\" \"iSH\" \"\\x1B[39m\" \"\\r\\n\\x1B[7m\"\n\"\\x1B[40m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[41m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[42m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[43m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[44m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[45m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[46m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[47m\" \"iSH\" \"\\x1B[39m\" \"\\x1B[0m\\x1B[1m\\r\\n\"\n\"\\x1B[90m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[91m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[92m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[93m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[94m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[95m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[96m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[97m\" \"iSH\" \"\\x1B[39m\" \"\\r\\n\\x1B[7m\"\n\"\\x1B[100m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[101m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[102m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[103m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[104m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[105m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[106m\" \"iSH\" \"\\x1B[39m \"\n\"\\x1B[107m\" \"iSH\" \"\\x1B[39m\" \"\\x1B[0m\\r\\n\"\n\"# \";\n\n@implementation AboutAppearanceViewController {\n    TerminalView *_terminalView;\n    Terminal *_terminal;\n    struct tty *_tty;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    [UserPreferences.shared observe:@[@\"theme\", @\"fontSize\", @\"fontFamily\", @\"colorScheme\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self.tableView reloadData];\n        });\n    }];\n    \n    [UserPreferences.shared observe:@[@\"cursorStyle\", @\"blinkCursor\", @\"hideStatusBar\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self updateOtherControls];\n        });\n    }];\n    [self updateOtherControls];\n    \n#if !ISH_LINUX\n    if (![NSUserDefaults.standardUserDefaults boolForKey:@\"recovery\"]) {\n        _terminal = [Terminal createPseudoTerminal:&_tty];\n        [_terminal sendOutput:previewString length:(int)strlen(previewString)];\n    }\n#endif\n}\n\n- (void)viewDidAppear:(BOOL)animated {\n    if (@available(iOS 13, *)) {\n        // Initialize the font picker ASAP, as it takes about a quarter second to initialize (XPC crap) and appears invisible until then.\n        // Re-initialize it after navigating away from it, to reset the table view highlight.\n        UIFontPickerViewControllerConfiguration *config = [UIFontPickerViewControllerConfiguration new];\n        config.filteredTraits = UIFontDescriptorTraitMonoSpace;\n        self.fontPicker = [[UIFontPickerViewController alloc] initWithConfiguration:config];\n        // Prevent the font picker from resizing the popup when it appears\n        self.fontPicker.preferredContentSize = CGSizeZero;\n        self.fontPicker.navigationItem.title = @\"Font\";\n        self.fontPicker.delegate = self;\n        self.fontPicker.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@\"Reset\" style:UIBarButtonItemStylePlain target:self action:@selector(resetFont:)];\n    }\n}\n\n#pragma mark - Table view data source\n\nenum {\n    PreviewSection,\n    MainSection,\n    ColorSchemeSection,\n    CursorSection,\n    StatusBarSection,\n    NumberOfSections,\n};\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {\n    return NumberOfSections;\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    switch (section) {\n        case PreviewSection: return 2;\n        case MainSection: return 3;\n        case ColorSchemeSection: return 3;\n        case CursorSection: return 2;\n        case StatusBarSection: return 1;\n        default: NSAssert(NO, @\"unhandled section\"); return 0;\n    }\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {\n    switch (section) {\n        case PreviewSection: return @\"Preview\";\n        case ColorSchemeSection: return @\"Color Scheme\";\n        case CursorSection: return @\"Cursor\";\n        case StatusBarSection: return @\"Status Bar\";\n        default: return nil;\n    }\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    switch (section) {\n        case PreviewSection: return @\"Change the color scheme used for the preview.\";\n        default: return nil;\n    }\n}\n\n- (NSString *)reuseIdentifierForIndexPath:(NSIndexPath *)indexPath {\n    switch (indexPath.section) {\n        case PreviewSection: return @[@\"Preview\", @\"Color Scheme Preview\"][indexPath.row];\n        case MainSection: return @[@\"Theme Name\", @\"Font\", @\"Font Size\"][indexPath.row];\n        case ColorSchemeSection: return @\"Color Scheme\";\n        case CursorSection: return @[@\"Cursor Style\", @\"Blink Cursor\"][indexPath.row];\n        case StatusBarSection: return @\"Status Bar\";\n        default: return nil;\n    }\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (indexPath.section == PreviewSection && indexPath.row == 0) {\n        // Try a best-effort guess as to how big the preview should be.\n        return [@\"\\n\\n\\n\\n\\n\\n\" sizeWithAttributes:@{NSFontAttributeName: UserPreferences.shared.approximateFont}].height + 10;\n    } else {\n        return UITableViewAutomaticDimension;\n    }\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[self reuseIdentifierForIndexPath:indexPath] forIndexPath:indexPath];\n    cell.selectionStyle = UITableViewCellSelectionStyleDefault;\n    \n    switch (indexPath.section) {\n        case PreviewSection:\n            switch (indexPath.row) {\n                case 0:\n                    _terminalView = [cell viewWithTag:1];\n                    _terminalView.userInteractionEnabled = NO;\n                    _terminalView.terminal = _terminal;\n                    break;\n                case 1: {\n                    UISegmentedControl *segmentedControl = [cell viewWithTag:1];\n                    [segmentedControl addTarget:self action:@selector(changePreviewTheme:) forControlEvents:UIControlEventValueChanged];\n                    [self changePreviewTheme:segmentedControl];\n                    cell.selectionStyle = UITableViewCellSelectionStyleNone;\n                    break;\n                }\n            }\n            break;\n            \n        case MainSection:\n            switch (indexPath.row) {\n                case 0:\n                    cell.detailTextLabel.text = UserPreferences.shared.theme.name;\n                    break;\n                case 1:\n                    cell.detailTextLabel.text = UserPreferences.shared.fontFamilyUserFacingName;\n                    cell.detailTextLabel.font = [UIFont fontWithName:UserPreferences.shared.fontFamily size:cell.detailTextLabel.font.pointSize];\n                    break;\n                case 2: {\n                    UserPreferences *prefs = [UserPreferences shared];\n                    UILabel *label = [cell viewWithTag:1];\n                    UIStepper *stepper = [cell viewWithTag:2];\n                    label.text = prefs.fontSize.stringValue;\n                    stepper.value = prefs.fontSize.doubleValue;\n                    cell.selectionStyle = UITableViewCellSelectionStyleNone;\n                    break;\n                }\n            }\n            break;\n            \n        case ColorSchemeSection:\n            switch (indexPath.row) {\n                case 0:\n                    cell.textLabel.text = @\"Match System\";\n                    break;\n                case 1:\n                    cell.textLabel.text = @\"Light\";\n                    break;\n                case 2:\n                    cell.textLabel.text = @\"Dark\";\n                    break;\n            }\n            cell.accessoryType = indexPath.row == UserPreferences.shared.colorScheme ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;\n            break;\n            \n        case CursorSection:\n        case StatusBarSection:\n            cell.selectionStyle = UITableViewCellSelectionStyleNone;\n            break;\n    }\n    \n    return cell;\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\n    \n    switch (indexPath.section) {\n        case MainSection:\n            switch (indexPath.row) {\n                case 0: { // theme\n                    ThemesViewController *themesViewController = [self.storyboard instantiateViewControllerWithIdentifier:@\"Themes\"];\n                    [self.navigationController pushViewController:themesViewController animated:YES];\n                    break;\n                }\n                case 1: // font family\n                    [self selectFont:nil];\n                    break;\n            }\n            break;\n        case ColorSchemeSection:\n            [UserPreferences.shared setColorScheme:indexPath.row];\n    }\n}\n\n- (void)updateOtherControls {\n    self.hideStatusBar.on = UserPreferences.shared.hideStatusBar;\n    self.cursorStyle.selectedSegmentIndex = UserPreferences.shared.cursorStyle;\n    self.blinkCursor.on = UserPreferences.shared.blinkCursor;\n    [self setNeedsStatusBarAppearanceUpdate];\n}\n\n- (void)changePreviewTheme:(UISegmentedControl *)sender {\n    _terminalView.overrideAppearance = sender.selectedSegmentIndex ? OverrideAppearanceDark : OverrideAppearanceLight;\n    _terminalView.backgroundColor = [[UIColor alloc] ish_initWithHexString:(sender.selectedSegmentIndex ? UserPreferences.shared.theme.darkPalette : UserPreferences.shared.theme.lightPalette).backgroundColor];\n}\n\n- (void)selectFont:(id)sender {\n    if (@available(iOS 13, *)) {\n        [self.navigationController pushViewController:self.fontPicker animated:YES];\n        return;\n    }\n    \n    FontPickerViewController *fontPicker = [self.storyboard instantiateViewControllerWithIdentifier:@\"FontPicker\"];\n    [self.navigationController pushViewController:fontPicker animated:YES];\n}\n\n- (void)fontPickerViewControllerDidPickFont:(UIFontPickerViewController *)viewController API_AVAILABLE(ios(13.0)) {\n    UserPreferences.shared.fontFamily = viewController.selectedFontDescriptor.fontAttributes[UIFontDescriptorFamilyAttribute];\n    [self.navigationController popToViewController:self animated:YES];\n}\n\n- (IBAction)resetFont:(UIBarButtonItem *)sender API_AVAILABLE(ios(13)) {\n    UserPreferences.shared.fontFamily = nil;\n    [self.navigationController popToViewController:self animated:YES];\n}\n\n- (IBAction)fontSizeChanged:(UIStepper *)sender {\n    UserPreferences.shared.fontSize = @((int) sender.value);\n}\n\n- (IBAction)hideStatusBarChanged:(UISwitch *)sender {\n    UserPreferences.shared.hideStatusBar = sender.on;\n    [self setNeedsStatusBarAppearanceUpdate];\n}\n\n- (IBAction)cursorStyleChanged:(UISegmentedControl *)sender {\n    [UserPreferences.shared setCursorStyle:sender.selectedSegmentIndex];\n}\n\n- (IBAction)blinkCursorChanged:(UISwitch *)sender {\n    [UserPreferences.shared setBlinkCursor:sender.on];\n}\n@end\n"
  },
  {
    "path": "app/AboutExternalKeyboardViewController.h",
    "content": "//\n//  CapsLockMappingViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 12/2/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface AboutExternalKeyboardViewController : UITableViewController\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/AboutExternalKeyboardViewController.m",
    "content": "//\n//  CapsLockMappingViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 12/2/18.\n//\n\n#import \"AboutExternalKeyboardViewController.h\"\n#import \"UserPreferences.h\"\n#import \"NSObject+SaneKVO.h\"\n\nconst int kCapsLockMappingSection = 0;\n\n@interface AboutExternalKeyboardViewController ()\n\n@property (weak, nonatomic) IBOutlet UISwitch *optionMetaSwitch;\n@property (weak, nonatomic) IBOutlet UISwitch *backtickEscapeSwitch;\n@property (weak, nonatomic) IBOutlet UISwitch *overrideControlSpaceSwitch;\n@property (weak, nonatomic) IBOutlet UISwitch *hideExtraKeysWithExternalKeyboardSwitch;\n\n@end\n\n@implementation AboutExternalKeyboardViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    [UserPreferences.shared observe:@[@\"capsLockMapping\", @\"optionMapping\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self.tableView reloadData];\n        });\n    }];\n    [self _update];\n}\n\n- (void)_update {\n    self.optionMetaSwitch.on = UserPreferences.shared.optionMapping == OptionMapEsc;\n    self.backtickEscapeSwitch.on = UserPreferences.shared.backtickMapEscape;\n    self.overrideControlSpaceSwitch.on = UserPreferences.shared.overrideControlSpace;\n    self.hideExtraKeysWithExternalKeyboardSwitch.on = UserPreferences.shared.hideExtraKeysWithExternalKeyboard;\n}\n\n- (IBAction)optionMetaToggle:(UISwitch *)sender {\n    UserPreferences.shared.optionMapping = sender.on ? OptionMapEsc : OptionMapNone;\n}\n- (IBAction)backtickEscapeToggle:(UISwitch *)sender {\n    UserPreferences.shared.backtickMapEscape = sender.on;\n}\n- (IBAction)overrideControlSpaceToggle:(UISwitch *)sender {\n    UserPreferences.shared.overrideControlSpace = sender.on;\n}\n- (IBAction)hideExtraKeysToggle:(UISwitch *)sender {\n    UserPreferences.shared.hideExtraKeysWithExternalKeyboard = sender.on;\n}\n\n- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (indexPath.section == kCapsLockMappingSection && cell.tag == UserPreferences.shared.capsLockMapping)\n        cell.accessoryType = UITableViewCellAccessoryCheckmark;\n    else\n        cell.accessoryType = UITableViewCellAccessoryNone;\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (indexPath.section == kCapsLockMappingSection) {\n        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];\n        UserPreferences.shared.capsLockMapping = cell.tag;\n    }\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    if (section == 0 && ![self.class capsLockMappingSupported])\n        return 0;\n    return [super tableView:tableView numberOfRowsInSection:section];\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    if (section == 0 && ![self.class capsLockMappingSupported])\n        return @\"Caps Lock mapping is broken in iOS 13.\\n\\n\"\n        @\"Since iOS 13.4, Caps Lock can be remapped system-wide in Settings → General → Keyboard → Hardware Keyboard → Modifier Keys.\";\n    return [super tableView:tableView titleForFooterInSection:section];\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {\n    if (section == 0 && ![self.class capsLockMappingSupported])\n        return @\"\";\n    return [super tableView:tableView titleForHeaderInSection:section];\n}\n\n+ (BOOL)capsLockMappingSupported {\n    if (@available(iOS 13, *)) {\n        return NO;\n    }\n    return YES;\n}\n\n@end\n"
  },
  {
    "path": "app/AboutNavigationController.h",
    "content": "//\n//  AboutNavigationController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/6/19.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface AboutNavigationController : UINavigationController\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/AboutNavigationController.m",
    "content": "//\n//  AboutNavigationController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/6/19.\n//\n\n#import \"AboutNavigationController.h\"\n#import \"UserPreferences.h\"\n#import \"NSObject+SaneKVO.h\"\n\n@interface AboutNavigationController ()\n\n@end\n\n@implementation AboutNavigationController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    [UserPreferences.shared observe:@[@\"colorScheme\"] options:NSKeyValueObservingOptionInitial\n                              owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            if (@available(iOS 13, *)) {\n                self.overrideUserInterfaceStyle = UserPreferences.shared.userInterfaceStyle;\n            }\n        });\n    }];\n}\n\n@end\n"
  },
  {
    "path": "app/AboutViewController.h",
    "content": "//\n//  AboutViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface AboutViewController : UITableViewController\n\n@property BOOL includeDebugPanel;\n@property BOOL recoveryMode;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/AboutViewController.m",
    "content": "//\n//  AboutViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import \"AboutViewController.h\"\n#import \"AppDelegate.h\"\n#import \"CurrentRoot.h\"\n#import \"AppGroup.h\"\n#import \"UserPreferences.h\"\n#import \"iOSFS.h\"\n#import \"UIApplication+OpenURL.h\"\n#import \"NSObject+SaneKVO.h\"\n\n@interface AboutViewController ()\n@property (weak, nonatomic) IBOutlet UITableViewCell *capsLockMappingCell;\n@property (weak, nonatomic) IBOutlet UITableViewCell *themeCell;\n@property (weak, nonatomic) IBOutlet UISwitch *disableDimmingSwitch;\n@property (weak, nonatomic) IBOutlet UITextField *launchCommandField;\n@property (weak, nonatomic) IBOutlet UITextField *bootCommandField;\n\n@property (weak, nonatomic) IBOutlet UITableViewCell *sendFeedback;\n@property (weak, nonatomic) IBOutlet UITableViewCell *openGithub;\n@property (weak, nonatomic) IBOutlet UITableViewCell *openFediverse;\n@property (weak, nonatomic) IBOutlet UITableViewCell *openDiscord;\n\n@property (weak, nonatomic) IBOutlet UITableViewCell *upgradeApkCell;\n@property (weak, nonatomic) IBOutlet UILabel *upgradeApkLabel;\n@property (weak, nonatomic) IBOutlet UIView *upgradeApkBadge;\n@property (weak, nonatomic) IBOutlet UITableViewCell *exportContainerCell;\n@property (weak, nonatomic) IBOutlet UITableViewCell *resetMountsCell;\n\n@property (weak, nonatomic) IBOutlet UILabel *versionLabel;\n\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *saddamHussein;\n\n@end\n\n@implementation AboutViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    [self _updateUI];\n    if (self.recoveryMode) {\n        self.includeDebugPanel = YES;\n        self.navigationItem.title = @\"Recovery Mode\";\n        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@\"Exit\"\n                                                                                  style:UIBarButtonItemStyleDone\n                                                                                 target:self\n                                                                                 action:@selector(exitRecovery:)];\n        self.navigationItem.leftBarButtonItem = nil;\n    }\n    _versionLabel.text = [NSString stringWithFormat:@\"iSH %@ (Build %@)\",\n                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleShortVersionString\"],\n                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleVersion\"]];\n\n    [UserPreferences.shared observe:@[@\"capsLockMapping\", @\"fontSize\", @\"launchCommand\", @\"bootCommand\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self _updateUI];\n        });\n    }];\n    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_updateUI:) name:FsUpdatedNotification object:nil];\n}\n\n- (void)viewWillAppear:(BOOL)animated {\n    [super viewWillAppear:animated];\n    [self _updateUI];\n}\n\n- (void)updateViewConstraints {\n    self.saddamHussein.constant = UIEdgeInsetsInsetRect(self.tableView.frame, self.tableView.adjustedContentInset).size.height;\n    [super updateViewConstraints];\n}\n\n- (IBAction)dismiss:(id)sender {\n    [self dismissViewControllerAnimated:self completion:nil];\n}\n\n- (void)exitRecovery:(id)sender {\n    [NSUserDefaults.standardUserDefaults setBool:NO forKey:@\"recovery\"];\n    exit(0);\n}\n\n- (void)_updateUI:(NSNotification *)notification {\n    [self _updateUI];\n}\n\n- (void)_updateUI {\n    NSAssert(NSThread.isMainThread, @\"This method needs to be called on the main thread\");\n    self.disableDimmingSwitch.on = UserPreferences.shared.shouldDisableDimming;\n    self.launchCommandField.text = [UserPreferences.shared.launchCommand componentsJoinedByString:@\" \"];\n    self.bootCommandField.text = [UserPreferences.shared.bootCommand componentsJoinedByString:@\" \"];\n\n    self.upgradeApkCell.userInteractionEnabled = FsNeedsRepositoryUpdate();\n    self.upgradeApkLabel.enabled = FsNeedsRepositoryUpdate();\n    self.upgradeApkBadge.hidden = !FsNeedsRepositoryUpdate();\n    [self.tableView reloadData];\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];\n    if (cell == self.sendFeedback) {\n        [UIApplication openURL:@\"mailto:tblodt@icloud.com?subject=Feedback%20for%20iSH\"];\n    } else if (cell == self.openGithub) {\n        [UIApplication openURL:@\"https://github.com/ish-app/ish\"];\n    } else if (cell == self.openFediverse) {\n        [UIApplication openURL:@\"https://publ.ish.app/ish\"];\n    } else if (cell == self.openDiscord) {\n        [UIApplication openURL:@\"https://discord.gg/HFAXj44\"];\n    } else if (cell == self.exportContainerCell) {\n        // copy the files to the app container so they can be extracted from iTunes file sharing\n        NSURL *container = ContainerURL();\n        NSURL *documents = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];\n        [NSFileManager.defaultManager removeItemAtURL:[documents URLByAppendingPathComponent:@\"roots copy\"] error:nil];\n        [NSFileManager.defaultManager copyItemAtURL:[container URLByAppendingPathComponent:@\"roots\"]\n                                              toURL:[documents URLByAppendingPathComponent:@\"roots copy\"]\n                                              error:nil];\n    } else if (cell == self.resetMountsCell) {\n#if !ISH_LINUX\n        iosfs_clear_all_bookmarks();\n#endif\n    }\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    if (section == 1) { // filesystems / upgrade\n        if (!FsIsManaged()) {\n            return @\"The current filesystem is not managed by iSH.\";\n        } else if (!FsNeedsRepositoryUpdate()) {\n            return [NSString stringWithFormat:@\"The current filesystem is using %s, which is the latest version.\", CURRENT_APK_VERSION_STRING];\n        } else {\n            return [NSString stringWithFormat:@\"An upgrade to %s is available.\", CURRENT_APK_VERSION_STRING];\n        }\n    }\n    return [super tableView:tableView titleForFooterInSection:section];\n}\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {\n    NSInteger sections = [super numberOfSectionsInTableView:tableView];\n    if (!self.includeDebugPanel)\n        sections--;\n    return sections;\n}\n\n- (IBAction)disableDimmingChanged:(id)sender {\n    UserPreferences.shared.shouldDisableDimming = self.disableDimmingSwitch.on;\n}\n\n- (IBAction)textBoxSubmit:(id)sender {\n    [sender resignFirstResponder];\n}\n\n- (IBAction)launchCommandChanged:(id)sender {\n    UserPreferences.shared.launchCommand = [self.launchCommandField.text componentsSeparatedByString:@\" \"];\n}\n\n- (IBAction)bootCommandChanged:(id)sender {\n    UserPreferences.shared.bootCommand = [self.bootCommandField.text componentsSeparatedByString:@\" \"];\n}\n\n@end\n"
  },
  {
    "path": "app/AccessibilityFixes.m",
    "content": "//\n//  AccessibilityFixes.m\n//  iSH\n//\n//  Created by Saagar Jha on 12/31/22.\n//\n\n#import \"hook.h\"\n#import <UIKit/UIKit.h>\n#import <assert.h>\n#import <dlfcn.h>\n#import <objc/runtime.h>\n\n// Work around https://bugs.webkit.org/show_bug.cgi?id=249976, which causes\n// https://github.com/ish-app/ish/issues/1937.\n\nstatic void replacement(void) {\n    NSLog(@\"Hooked PageClientImpl::assistiveTechnologyMakeFirstResponder\");\n}\n\nstatic bool patched;\n\nstatic void patch_if_needed(void) {\n    if (!patched && UIAccessibilityIsVoiceOverRunning()) {\n        patched = true;\n        // This can take a little while.\n        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{\n            Dl_info info;\n            dladdr((__bridge void *)objc_getClass(\"WKWebView\"), &info);\n            void *symbol = find_symbol(info.dli_fbase, \"__ZN6WebKit14PageClientImpl37assistiveTechnologyMakeFirstResponderEv\");\n            bool hooked = hook((void *)symbol, (void *)replacement);\n            assert(hooked);\n        });\n    }\n}\n\n__attribute__((constructor)) void accessibilityfixes_init(void) {\n    if (@available(iOS 15.7, *)) {\n        [NSNotificationCenter.defaultCenter addObserverForName:UIAccessibilityVoiceOverStatusDidChangeNotification\n                                                        object:nil\n                                                         queue:nil\n                                                    usingBlock:^(NSNotification *notification) {\n            patch_if_needed();\n        }];\n        patch_if_needed();\n    }\n}\n"
  },
  {
    "path": "app/AltIconViewController.h",
    "content": "//\n//  IconViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 12/13/19.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface AltIconViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/AltIconViewController.m",
    "content": "//\n//  IconViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 12/13/19.\n//\n\n#import \"AltIconViewController.h\"\n#import \"UIApplication+OpenURL.h\"\n\n@interface AltIconViewController ()\n\n@property (weak) IBOutlet UICollectionView *collectionView;\n\n@property NSDictionary<NSString *, NSDictionary *> *altIcons;\n@property NSArray<NSString *> *altIconNames;\n\n@end\n\n@interface AltIconCell : UICollectionViewCell\n\n@property (weak, nonatomic) IBOutlet UIImageView *imageView;\n@property (weak, nonatomic) IBOutlet UIImageView *checkboxImageView;\n@property (weak, nonatomic) IBOutlet UIButton *authorButton;\n\n@property (nonatomic) NSString *link;\n\n- (void)updateImage:(UIImage *)image description:(NSString *)description author:(NSString *)author link:(NSURL *)link;\n\n@end\n\n@implementation AltIconViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \n    self.altIcons = [NSDictionary dictionaryWithContentsOfURL:\n                     [NSBundle.mainBundle URLForResource:@\"Icons\"\n                                           withExtension:@\"plist\"]];\n    self.altIconNames = [self.altIcons.allKeys sortedArrayUsingSelector:@selector(compare:)];\n    \n    NSString *iconName = UIApplication.sharedApplication.alternateIconName;\n    if (iconName == nil)\n        iconName = @\"\";\n    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.altIconNames indexOfObject:iconName]\n                                                inSection:0];\n    [self.collectionView selectItemAtIndexPath:indexPath\n                                      animated:NO\n                                scrollPosition:UICollectionViewScrollPositionTop];\n}\n\n- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {\n    return self.altIconNames.count;\n}\n\n- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {\n    return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@\"footer\" forIndexPath:indexPath];\n}\n\n- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {\n    AltIconCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@\"icon\" forIndexPath:indexPath];\n    NSString *iconName = self.altIconNames[indexPath.item];\n    [cell updateImage:[UIImage imageNamed:iconName.length == 0 ? @\"icon\" : iconName]\n          description:self.altIcons[iconName][@\"description\"]\n               author:self.altIcons[iconName][@\"author\"]\n                 link:self.altIcons[iconName][@\"link\"]];\n    return cell;\n}\n\n- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {\n    NSString *iconName = self.altIconNames[indexPath.item];\n    if (iconName.length == 0)\n        iconName = nil;\n    [UIApplication.sharedApplication setAlternateIconName:iconName completionHandler:^(NSError *err) {\n        if (err != nil)\n            NSLog(@\"%@\", err);\n    }];\n}\n\n- (IBAction)openSubmissions:(id)sender {\n    [UIApplication openURL:@\"https://github.com/tbodt/ish/issues/578\"];\n}\n\n- (CGFloat)sideInset:(UICollectionViewFlowLayout *)layout {\n    // For maximum aesthetics, there should be a decent amount of spacing between cells\n    static const CGFloat kMinSpacer = 20;\n    // The insets should be somewhat smaller than the spacer\n    static const CGFloat kInsetToSpacerRatio = 0.75;\n    \n    CGFloat total = layout.collectionView.frame.size.width;\n    CGFloat item = layout.itemSize.width;\n    NSUInteger count = (int) (total / item);\n    CGFloat spacer;\n    CGFloat inset;\n    do {\n        CGFloat slack = total - (item * count);\n        spacer = slack / (2 * kInsetToSpacerRatio + count - 1);\n        inset = spacer * kInsetToSpacerRatio;\n        count--;\n    } while (spacer < kMinSpacer);\n    return inset;\n}\n- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)layout insetForSectionAtIndex:(NSInteger)section {\n    CGFloat sideInset = [self sideInset:layout];\n    return UIEdgeInsetsMake(sideInset, sideInset, 20, sideInset);\n}\n\n@end\n\n@implementation AltIconCell\n\n- (void)awakeFromNib {\n    [super awakeFromNib];\n    \n    CAShapeLayer *iconMask = [CAShapeLayer new];\n    iconMask.frame = self.imageView.bounds;\n    iconMask.path = [UIBezierPath bezierPathWithRoundedRect:self.imageView.bounds\n                                               cornerRadius:self.imageView.bounds.size.width * 0.225].CGPath;\n    self.imageView.layer.mask = iconMask;\n    self.imageView.layer.minificationFilter = kCAFilterTrilinear;\n    \n    if (@available(iOS 13, *)) {\n        self.checkboxImageView.image = UIImage.checkmarkImage;\n    } else {\n//        self.checkboxImageView.backgroundColor = UIColor.whiteColor;\n//        self.checkboxImageView.layer.cornerRadius = self.checkboxImageView.bounds.size.width / 2;\n    }\n\n    self.authorButton.titleLabel.adjustsFontForContentSizeCategory = YES;\n\n    self.isAccessibilityElement = YES;\n    self.accessibilityCustomActions = @[[[UIAccessibilityCustomAction alloc] initWithName:@\"Open link\" target:self selector:@selector(openSource:)]];\n}\n\n- (void)updateImage:(UIImage *)image description:(NSString *)description author:(NSString *)author link:(NSString *)url {\n    self.imageView.image = image;\n    [self.authorButton setTitle:[NSString stringWithFormat:@\"by %@\", author] forState:UIControlStateNormal];\n    self.link = url;\n    self.accessibilityLabel = [NSString stringWithFormat:@\"%@ by %@\", description, author];\n}\n\n- (IBAction)openSource:(id)sender {\n    [UIApplication openURL:self.link];\n}\n\n- (void)setSelected:(BOOL)selected {\n    [super setSelected:selected];\n    self.checkboxImageView.hidden = !selected;\n    self.accessibilityTraits = selected ? UIAccessibilityTraitSelected : 0;\n}\n\n@end\n"
  },
  {
    "path": "app/App.xcconfig",
    "content": "#include \"iOS.xcconfig\"\n\nPRODUCT_NAME = iSH\nPRODUCT_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER)\nINFOPLIST_FILE = app/Info.plist\nINFOPLIST_PREPROCESS = YES\nINFOPLIST_PREFIX_HEADER = $(BUILT_PRODUCTS_DIR)/infoplisticons.h\nASSETCATALOG_COMPILER_APPICON_NAME = AppIcon\nCODE_SIGN_ENTITLEMENTS = app/iSH.entitlements\n\nHEADER_SEARCH_PATHS = $(inherited) $(SRCROOT) $(SRCROOT)/deps/libarchive/libarchive\n// AccessibilityFixes.m contains a static constructor that we don't want removed\nOTHER_LDFLAGS = -ObjC $(LINUX_APP_LDFLAGS) -u _accessibilityfixes_init\n"
  },
  {
    "path": "app/AppDelegate.h",
    "content": "//\n//  AppDelegate.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n@property (strong, nonatomic) UIWindow *window;\n- (void)exitApp;\n\n#if !ISH_LINUX\n+ (int)bootError;\n#endif\n\n+ (void)maybePresentStartupMessageOnViewController:(UIViewController *)vc;\n\n@end\n\n#if !ISH_LINUX\nextern NSString *const ProcessExitedNotification;\n#else\nextern NSString *const KernelPanicNotification;\n#endif\n\n"
  },
  {
    "path": "app/AppDelegate.m",
    "content": "//\n//  AppDelegate.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#include <resolv.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#import <SystemConfiguration/SystemConfiguration.h>\n#import \"AboutViewController.h\"\n#import \"AppDelegate.h\"\n#import \"AppGroup.h\"\n#import \"CurrentRoot.h\"\n#import \"ExceptionExfiltrator.h\"\n#import \"iOSFS.h\"\n#import \"SceneDelegate.h\"\n#import \"PasteboardDevice.h\"\n#import \"LocationDevice.h\"\n#import \"NSObject+SaneKVO.h\"\n#import \"Roots.h\"\n#import \"TerminalViewController.h\"\n#import \"UserPreferences.h\"\n#import \"UIApplication+OpenURL.h\"\n#include \"kernel/init.h\"\n#include \"kernel/calls.h\"\n#include \"fs/dyndev.h\"\n#include \"fs/devices.h\"\n#include \"fs/path.h\"\n\n#if ISH_LINUX\n#import \"LinuxInterop.h\"\n#endif\n\n@interface AppDelegate ()\n\n@property BOOL exiting;\n@property SCNetworkReachabilityRef reachability;\n\n@end\n\n#if !ISH_LINUX\nstatic void ios_handle_exit(struct task *task, int code) {\n    // we are interested in init and in children of init\n    // this is called with pids_lock as an implementation side effect, please do not cite as an example of good API design\n    if (task->parent != NULL && task->parent->parent != NULL)\n        return;\n    // pid should be saved now since task would be freed\n    pid_t pid = task->pid;\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [[NSNotificationCenter defaultCenter] postNotificationName:ProcessExitedNotification\n                                                            object:nil\n                                                          userInfo:@{@\"pid\": @(pid),\n                                                                     @\"code\": @(code)}];\n    });\n}\n\nstatic void ios_handle_die(const char *msg) {\n    NSString *message = [NSString stringWithFormat:@\"%s: %s\", __func__, msg];\n    iSHExceptionHandler([[NSException alloc] initWithName:NSGenericException reason:message userInfo:nil]);\n}\n#elif ISH_LINUX\nvoid ReportPanic(const char *message) {\n    [NSNotificationCenter.defaultCenter postNotificationName:KernelPanicNotification object:nil userInfo:@{@\"message\":@(message)}];\n}\n#endif\n\nstatic int bootError;\nstatic NSString *const kSkipStartupMessage = @\"Skip Startup Message\";\n\n@implementation AppDelegate\n\n- (int)boot {\n#if !ISH_LINUX\n    NSURL *root = [Roots.instance rootUrl:Roots.instance.defaultRoot];\n\n    int err = mount_root(&fakefs, [root URLByAppendingPathComponent:@\"data\"].fileSystemRepresentation);\n    if (err < 0)\n        return err;\n\n    fs_register(&iosfs);\n    fs_register(&iosfs_unsafe);\n\n    // need to do this first so that we can have a valid current for the generic_mknod calls\n    err = become_first_process();\n    if (err < 0)\n        return err;\n\n    FsInitialize();\n\n    // create some device nodes\n    // this will do nothing if they already exist\n    generic_mknodat(AT_PWD, \"/dev/tty1\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 1));\n    generic_mknodat(AT_PWD, \"/dev/tty2\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 2));\n    generic_mknodat(AT_PWD, \"/dev/tty3\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 3));\n    generic_mknodat(AT_PWD, \"/dev/tty4\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 4));\n    generic_mknodat(AT_PWD, \"/dev/tty5\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 5));\n    generic_mknodat(AT_PWD, \"/dev/tty6\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 6));\n    generic_mknodat(AT_PWD, \"/dev/tty7\", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 7));\n\n    generic_mknodat(AT_PWD, \"/dev/tty\", S_IFCHR|0666, dev_make(TTY_ALTERNATE_MAJOR, DEV_TTY_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/console\", S_IFCHR|0666, dev_make(TTY_ALTERNATE_MAJOR, DEV_CONSOLE_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/ptmx\", S_IFCHR|0666, dev_make(TTY_ALTERNATE_MAJOR, DEV_PTMX_MINOR));\n\n    generic_mknodat(AT_PWD, \"/dev/null\", S_IFCHR|0666, dev_make(MEM_MAJOR, DEV_NULL_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/zero\", S_IFCHR|0666, dev_make(MEM_MAJOR, DEV_ZERO_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/full\", S_IFCHR|0666, dev_make(MEM_MAJOR, DEV_FULL_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/random\", S_IFCHR|0666, dev_make(MEM_MAJOR, DEV_RANDOM_MINOR));\n    generic_mknodat(AT_PWD, \"/dev/urandom\", S_IFCHR|0666, dev_make(MEM_MAJOR, DEV_URANDOM_MINOR));\n    \n    generic_mkdirat(AT_PWD, \"/dev/pts\", 0755);\n    \n    // Permissions on / have been broken for a while, let's fix them\n    generic_setattrat(AT_PWD, \"/\", (struct attr) {.type = attr_mode, .mode = 0755}, false);\n    \n    // Register clipboard device driver and create device node for it\n    err = dyn_dev_register(&clipboard_dev, DEV_CHAR, DYN_DEV_MAJOR, DEV_CLIPBOARD_MINOR);\n    if (err != 0) {\n        return err;\n    }\n    generic_mknodat(AT_PWD, \"/dev/clipboard\", S_IFCHR|0666, dev_make(DYN_DEV_MAJOR, DEV_CLIPBOARD_MINOR));\n    \n    err = dyn_dev_register(&location_dev, DEV_CHAR, DYN_DEV_MAJOR, DEV_LOCATION_MINOR);\n    if (err != 0)\n        return err;\n    generic_mknodat(AT_PWD, \"/dev/location\", S_IFCHR|0666, dev_make(DYN_DEV_MAJOR, DEV_LOCATION_MINOR));\n\n    do_mount(&procfs, \"proc\", \"/proc\", \"\", 0);\n    do_mount(&devptsfs, \"devpts\", \"/dev/pts\", \"\", 0);\n\n    iosfs_init(); // let it mount any filesystems from user defaults\n\n    [self configureDns];\n    \n    exit_hook = ios_handle_exit;\n    die_handler = ios_handle_die;\n#if !TARGET_OS_SIMULATOR\n    NSString *sockTmp = [NSTemporaryDirectory() stringByAppendingString:@\"ishsock\"];\n    sock_tmp_prefix = strdup(sockTmp.UTF8String);\n#endif\n    \n    tty_drivers[TTY_CONSOLE_MAJOR] = &ios_console_driver;\n    set_console_device(TTY_CONSOLE_MAJOR, 1);\n    err = create_stdio(\"/dev/console\", TTY_CONSOLE_MAJOR, 1);\n    if (err < 0)\n        return err;\n    \n    NSArray<NSString *> *command;\n    command = UserPreferences.shared.bootCommand;\n    NSLog(@\"%@\", command);\n    char argv[4096];\n    [Terminal convertCommand:command toArgs:argv limitSize:sizeof(argv)];\n    const char *envp = \"TERM=xterm-256color\\0\";\n    err = do_execve(command[0].UTF8String, command.count, argv, envp);\n    if (err < 0)\n        return err;\n    task_start(current);\n\n#else\n    // On first launch, this will trigger the import of the default root. Make sure to do this before entering the kernel, because it needs to run something on the main thread, and that would deadlock.\n    [Roots instance];\n    NSArray<NSString *> *args = @[];\n    actuate_kernel([args componentsJoinedByString:@\" \"].UTF8String);\n#endif\n    \n    return 0;\n}\n\n#if ISH_LINUX\nconst char *DefaultRootPath() {\n    return [Roots.instance rootUrl:Roots.instance.defaultRoot].fileSystemRepresentation;\n}\n\nvoid SyncHostname(void) {\n    async_do_in_workqueue(^{\n        char hostname[256];\n        if (gethostname(hostname, sizeof(hostname)) < 0)\n            return;\n        linux_sethostname(hostname);\n    });\n}\n#endif\n\n- (void)configureDns {\n#if !ISH_LINUX\n    struct __res_state res;\n    if (EXIT_SUCCESS != res_ninit(&res)) {\n        exit(2);\n    }\n    NSMutableString *resolvConf = [NSMutableString new];\n    if (res.dnsrch[0] != NULL) {\n        [resolvConf appendString:@\"search\"];\n        for (int i = 0; res.dnsrch[i] != NULL; i++) {\n            [resolvConf appendFormat:@\" %s\", res.dnsrch[i]];\n        }\n        [resolvConf appendString:@\"\\n\"];\n    }\n    union res_sockaddr_union servers[NI_MAXSERV];\n    int serversFound = res_getservers(&res, servers, NI_MAXSERV);\n    char address[NI_MAXHOST];\n    for (int i = 0; i < serversFound; i ++) {\n        union res_sockaddr_union s = servers[i];\n        if (s.sin.sin_len == 0)\n            continue;\n        getnameinfo((struct sockaddr *) &s.sin, s.sin.sin_len,\n                    address, sizeof(address),\n                    NULL, 0, NI_NUMERICHOST);\n        [resolvConf appendFormat:@\"nameserver %s\\n\", address];\n    }\n    \n    current = pid_get_task(1);\n    struct fd *fd = generic_open(\"/etc/resolv.conf\", O_WRONLY_ | O_CREAT_ | O_TRUNC_, 0666);\n    if (!IS_ERR(fd)) {\n        fd->ops->write(fd, resolvConf.UTF8String, [resolvConf lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);\n        fd_close(fd);\n    }\n#endif\n}\n\n+ (int)bootError {\n    return bootError;\n}\n\n+ (void)maybePresentStartupMessageOnViewController:(UIViewController *)vc {\n    if ([NSUserDefaults.standardUserDefaults integerForKey:kSkipStartupMessage] >= 1)\n        return;\n    if (!FsIsManaged()) {\n        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@\"Install iSH’s built-in APK?\"\n                                                                       message:@\"iSH now includes the APK package manager, but it must be manually activated.\"\n                                                                preferredStyle:UIAlertControllerStyleAlert];\n        [alert addAction:[UIAlertAction actionWithTitle:@\"Show me how\"\n                                                  style:UIAlertActionStyleDefault\n                                                handler:^(UIAlertAction * _Nonnull action) {\n            [UIApplication openURL:@\"https://go.ish.app/get-apk\"];\n        }]];\n        [alert addAction:[UIAlertAction actionWithTitle:@\"Don't show again\"\n                                                  style:UIAlertActionStyleDefault\n                                                handler:nil]];\n        [vc presentViewController:alert animated:YES completion:nil];\n    }\n    [NSUserDefaults.standardUserDefaults setInteger:1 forKey:kSkipStartupMessage];\n}\n\n- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions {\n    NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;\n    if ([defaults boolForKey:@\"hail mary\"]) {\n        [defaults removeObjectForKey:kPreferenceBootCommandKey];\n        [defaults removeObjectForKey:kPreferenceLaunchCommandKey];\n        [defaults setBool:NO forKey:@\"hail mary\"];\n    }\n    if ([NSUserDefaults.standardUserDefaults boolForKey:@\"recovery\"])\n        return YES;\n\n    bootError = [self boot];\n\n#if ISH_LINUX\n    [NSNotificationCenter.defaultCenter addObserverForName:UIApplicationWillEnterForegroundNotification object:UIApplication.sharedApplication queue:nil usingBlock:^(NSNotification * _Nonnull note) {\n        SyncHostname();\n    }];\n    SyncHostname();\n#endif\n\n    return YES;\n}\n\nvoid NetworkReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {\n    AppDelegate *self = (__bridge AppDelegate *) info;\n    [self configureDns];\n}\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    // get the network permissions popup to appear on chinese devices\n    [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@\"http://captive.apple.com\"]] resume];\n\n    if ([NSUserDefaults.standardUserDefaults boolForKey:@\"FASTLANE_SNAPSHOT\"])\n        [UIView setAnimationsEnabled:NO];\n\n#if !ISH_LINUX\n    NSString *ishVersion = [NSString stringWithFormat:@\"iSH %@ (%@)\",\n                         [NSBundle.mainBundle objectForInfoDictionaryKey:@\"CFBundleShortVersionString\"],\n                         [NSBundle.mainBundle objectForInfoDictionaryKey:(NSString *) kCFBundleVersionKey]];\n    extern const char *proc_ish_version;\n    proc_ish_version = strdup(ishVersion.UTF8String);\n    // this defaults key is set when taking app store screenshots\n    extern const char *uname_hostname_override;\n    NSString *hostnameOverride = UserPreferences.shared._hostnameOverride;\n    if (@available(iOS 16.0, *)) { // Hostname obfuscation is in effect\n        hostnameOverride = hostnameOverride ? hostnameOverride : UserPreferences.shared.hostnameOverride;\n    }\n    if (hostnameOverride) {\n        uname_hostname_override = strdup(hostnameOverride.UTF8String);\n    }\n#endif\n    \n    [UserPreferences.shared observe:@[@\"shouldDisableDimming\"] options:NSKeyValueObservingOptionInitial\n                              owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            UIApplication.sharedApplication.idleTimerDisabled = UserPreferences.shared.shouldDisableDimming;\n        });\n    }];\n    \n    // This code is IPv4 and IPv6 aware: see https://developer.apple.com/library/archive/samplecode/Reachability/Listings/ReadMe_md.html\n    struct sockaddr_in address = {\n        .sin_len = sizeof(address),\n        .sin_family = AF_INET,\n    };\n    self.reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *) &address);\n    SCNetworkReachabilityContext context = {\n        .info = (__bridge void *) self,\n    };\n    SCNetworkReachabilitySetCallback(self.reachability, NetworkReachabilityCallback, &context);\n    SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);\n\n    if (self.window != nil) {\n        // For iOS <13, where the app delegate owns the window instead of the scene\n        if ([NSUserDefaults.standardUserDefaults boolForKey:@\"recovery\"]) {\n            UINavigationController *vc = [[UIStoryboard storyboardWithName:@\"About\" bundle:nil] instantiateInitialViewController];\n            AboutViewController *avc = (AboutViewController *) vc.topViewController;\n            avc.recoveryMode = YES;\n            self.window.rootViewController = vc;\n            return YES;\n        }\n        TerminalViewController *vc = (TerminalViewController *) self.window.rootViewController;\n        currentTerminalViewController = vc;\n        [vc startNewSession];\n    }\n    return YES;\n}\n\n- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions API_AVAILABLE(ios(13.0)) {\n    for (UISceneSession *sceneSession in sceneSessions) {\n        NSString *terminalUUID = sceneSession.stateRestorationActivity.userInfo[@\"TerminalUUID\"];\n        [[Terminal terminalWithUUID:[[NSUUID alloc] initWithUUIDString:terminalUUID]] destroy];\n    }\n}\n\n- (void)dealloc {\n    if (self.reachability != NULL) {\n        SCNetworkReachabilityUnscheduleFromRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);\n        CFRelease(self.reachability);\n    }\n}\n\n- (void)exitApp {\n    self.exiting = YES;\n    id app = [UIApplication sharedApplication];\n    [app suspend];\n}\n\n- (void)applicationDidEnterBackground:(UIApplication *)application {\n    if (self.exiting)\n        exit(0);\n}\n\n@end\n\n#if !ISH_LINUX\nNSString *const ProcessExitedNotification = @\"ProcessExitedNotification\";\n#else\nNSString *const KernelPanicNotification = @\"KernelPanicNotification\";\n#endif\n"
  },
  {
    "path": "app/AppGroup.h",
    "content": "//\n//  AppGroup.h\n//  iSH\n//\n//  Created by Theodore Dubois on 2/28/20.\n//\n\n#import <Foundation/Foundation.h>\n\nNSURL *ContainerURL(void);\n"
  },
  {
    "path": "app/AppGroup.m",
    "content": "//\n//  AppGroup.m\n//  iSH\n//\n//  Created by Theodore Dubois on 2/28/20.\n//\n\n#import \"AppGroup.h\"\n#import <Foundation/Foundation.h>\n#import <mach-o/ldsyms.h>\n#import <mach-o/loader.h>\n#import <mach-o/getsect.h>\n#import <dlfcn.h>\n\n#define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0\n#define CSMAGIC_EMBEDDED_ENTITLEMENTS 0xfade7171\n\nstruct cs_blob_index {\n    uint32_t type;\n    uint32_t offset;\n};\n\nstruct cs_superblob {\n    uint32_t magic;\n    uint32_t length;\n    uint32_t count;\n    struct cs_blob_index index[];\n};\n\nstruct cs_entitlements {\n    uint32_t magic;\n    uint32_t length;\n    char entitlements[];\n};\n\nstatic NSDictionary *AppEntitlements(void) {\n    static NSDictionary *entitlements;\n    if (entitlements != nil)\n        return entitlements;\n    \n    // Inspired by codesign.c in Darwin sources for Security.framework\n    \n    const struct mach_header_64 *header = &_mh_execute_header;\n    \n    // Simulator executables have fake entitlements in the code signature. The real entitlements can be found in an __entitlements section.\n    size_t entitlements_size;\n    char *entitlements_data = (char *) getsectiondata(header, \"__TEXT\", \"__entitlements\", &entitlements_size);\n    if (entitlements_data != NULL) {\n        NSData *data = [NSData dataWithBytesNoCopy:entitlements_data\n                                            length:entitlements_size\n                                      freeWhenDone:NO];\n        return entitlements = [NSPropertyListSerialization propertyListWithData:data\n                                                                        options:NSPropertyListImmutable\n                                                                         format:nil\n                                                                          error:nil];\n    }\n    \n    // Find the LC_CODE_SIGNATURE\n    struct load_command *lc = (void *) (header + 1);\n    struct linkedit_data_command *cs_lc = NULL;\n    for (uint32_t i = 0; i < header->ncmds; i++) {\n        if (lc->cmd == LC_CODE_SIGNATURE) {\n            cs_lc = (void *) lc;\n            break;\n        }\n        lc = (void *) ((char *) lc + lc->cmdsize);\n    }\n    if (cs_lc == NULL)\n        return nil;\n\n    // Read the code signature off disk, as it's apparently not loaded into memory\n    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:NSBundle.mainBundle.executableURL error:nil];\n    if (fileHandle == nil)\n        return nil;\n    [fileHandle seekToFileOffset:cs_lc->dataoff];\n    NSData *csData = [fileHandle readDataOfLength:cs_lc->datasize];\n    [fileHandle closeFile];\n    const struct cs_superblob *cs = csData.bytes;\n    if (ntohl(cs->magic) != CSMAGIC_EMBEDDED_SIGNATURE)\n        return nil;\n    \n    // Find the entitlements in the code signature\n    NSData *entitlementsData = nil;\n    for (uint32_t i = 0; i < ntohl(cs->count); i++) {\n        struct cs_entitlements *ents = (void *) ((char *) cs + ntohl(cs->index[i].offset));\n        if (ntohl(ents->magic) == CSMAGIC_EMBEDDED_ENTITLEMENTS) {\n            entitlementsData = [NSData dataWithBytes:ents->entitlements\n                                              length:ntohl(ents->length) - offsetof(struct cs_entitlements, entitlements)];\n        }\n    }\n    if (entitlementsData == nil)\n        return nil;\n    \n    return entitlements = [NSPropertyListSerialization propertyListWithData:entitlementsData\n                                                                    options:NSPropertyListImmutable\n                                                                     format:nil\n                                                                      error:nil];\n}\n\nNSArray<NSString *> *CurrentAppGroups(void) {\n    return AppEntitlements()[@\"com.apple.security.application-groups\"];\n}\n\nNSURL *ContainerURL(void) {\n    NSString *appGroup = CurrentAppGroups()[0];\n    return [NSFileManager.defaultManager containerURLForSecurityApplicationGroupIdentifier:appGroup];\n}\n"
  },
  {
    "path": "app/AppLib.xcconfig",
    "content": "#include \"StaticLib.xcconfig\"\nHEADER_SEARCH_PATHS = $(inherited) $(SRCROOT) $(SRCROOT)/deps/libarchive/libarchive\n"
  },
  {
    "path": "app/ArrowBarButton.h",
    "content": "//\n//  ArrowBarButton.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\ntypedef enum : NSUInteger {\n    ArrowNone = 0,\n    ArrowUp,\n    ArrowDown,\n    ArrowLeft,\n    ArrowRight,\n} ArrowDirection;\n\n@interface ArrowBarButton : UIControl\n\n@property (nonatomic, readonly) ArrowDirection direction;\n@property (nonatomic) UIKeyboardAppearance keyAppearance;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/ArrowBarButton.m",
    "content": "//\n//  ArrowBarButton.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import \"ArrowBarButton.h\"\n\n\n@interface ArrowBarButton () <CALayerDelegate> {\n    CALayer *arrowLayers[5];\n}\n\n@property CGPoint startPoint;\n@property (nonatomic) ArrowDirection direction;\n@property BOOL accessibilityUpDown;\n@property NSTimer *timer;\n\n@end\n\n@implementation ArrowBarButton\n\n- (instancetype)initWithFrame:(CGRect)frame {\n    if (self = [super initWithFrame:frame]) {\n        [self setup];\n    }\n    return self;\n}\n- (instancetype)initWithCoder:(NSCoder *)aDecoder {\n    if (self = [super initWithCoder:aDecoder]) {\n        [self setup];\n    }\n    return self;\n}\n\nstatic CGPoint anchors[] = {\n    {},\n    {.5, .95}, // ArrowUp\n    {.5, .05}, // ArrowDown\n    {1.05, .5}, // ArrowLeft\n    {-.05, .5}, // ArrowRight\n};\n\n- (void)setup {\n    self.layer.delegate = self;\n    [self addTextLayer:@\"↑\" direction:ArrowUp];\n    [self addTextLayer:@\"↓\" direction:ArrowDown];\n    [self addTextLayer:@\"←\" direction:ArrowLeft];\n    [self addTextLayer:@\"→\" direction:ArrowRight];\n    [self layoutSublayersOfLayer:self.layer];\n    \n    self.layer.cornerRadius = 5;\n    self.layer.shadowOffset = CGSizeMake(0, 1);\n    self.layer.shadowOpacity = 0.4;\n    self.layer.shadowRadius = 0;\n    \n    self.accessibilityUpDown = YES;\n}\n\n- (BOOL)isAccessibilityElement {\n    return YES;\n}\n- (UIAccessibilityTraits)accessibilityTraits {\n    return UIAccessibilityTraitAdjustable;\n}\n- (NSString *)accessibilityLabel {\n    return self.accessibilityUpDown ? @\"Arrow Keys Up or Down\" : @\"Arrow Keys Left or Right\";\n}\n- (NSString *)accessibilityHint {\n    return @\"Double tap to toggle direction\";\n}\n\n- (BOOL)accessibilityActivate {\n    self.accessibilityUpDown = !self.accessibilityUpDown;\n    return TRUE;\n}\n\n- (void)accessibilityIncrement {\n    if (self.accessibilityUpDown)\n        self.direction = ArrowUp;\n    else\n        self.direction = ArrowLeft; // this might be wrong in RTL\n    self.direction = ArrowNone;\n}\n- (void)accessibilityDecrement {\n    if (self.accessibilityUpDown)\n        self.direction = ArrowDown;\n    else\n        self.direction = ArrowRight;\n    self.direction = ArrowNone;\n}\n\n- (void)addTextLayer:(NSString *)text direction:(ArrowDirection)direction {\n    CATextLayer *layer = [CATextLayer new];\n    layer.contentsScale = UIScreen.mainScreen.scale;\n    layer.string = text;\n    layer.fontSize = 15;\n    UIFont *font = [UIFont systemFontOfSize:layer.fontSize];\n    layer.font = (__bridge CFTypeRef _Nullable) font;\n    CGSize textSize = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}].size;\n    layer.bounds = CGRectMake(0, 0, textSize.width, textSize.height);\n    layer.foregroundColor = UIColor.blackColor.CGColor;\n    \n    layer.alignmentMode = kCAAlignmentCenter;\n    layer.anchorPoint = anchors[direction];\n    [self.layer addSublayer:layer];\n    self->arrowLayers[direction] = layer;\n}\n\n- (void)layoutSublayersOfLayer:(CALayer *)superlayer {\n    NSParameterAssert(superlayer == self.layer);\n    for (CALayer *layer in superlayer.sublayers) {\n        layer.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);\n    }\n}\n\n- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {\n    [super beginTrackingWithTouch:touch withEvent:event];\n    self.startPoint = [touch locationInView:self];\n    self.selected = YES;\n    return YES;\n}\n\n- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {\n    [super continueTrackingWithTouch:touch withEvent:event];\n    CGPoint currentPoint = [touch locationInView:self];\n    CGPoint diff = CGPointMake(currentPoint.x - self.startPoint.x, currentPoint.y - self.startPoint.y);\n    if (hypot(diff.x, diff.y) < 20) {\n        self.direction = ArrowNone;\n    } else if (fabs(diff.x) > fabs(diff.y)) {\n        // more to the side\n        if (diff.x > 0)\n            self.direction = ArrowRight;\n        else\n            self.direction = ArrowLeft;\n    } else {\n        // more up and down\n        if (diff.y > 0)\n            self.direction = ArrowDown;\n        else\n            self.direction = ArrowUp;\n    }\n    return YES;\n}\n\n- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {\n    [super endTrackingWithTouch:touch withEvent:event];\n    self.selected = NO;\n    self.direction = ArrowNone;\n}\n- (void)cancelTrackingWithEvent:(UIEvent *)event {\n    [super cancelTrackingWithEvent:event];\n    self.selected = NO;\n    self.direction = ArrowNone;\n}\n\n- (void)animateLayerUpdates {\n    [UIView animateWithDuration:0.25 animations:^{\n        for (int d = ArrowUp; d <= ArrowRight; d++) {\n            CATextLayer *layer = (CATextLayer *) self->arrowLayers[d];\n            if (self.direction == ArrowNone || self.direction != d) {\n                layer.opacity = self.selected ? 0.25 : 1;\n            } else {\n                layer.opacity = 1;\n            }\n        }\n    }];\n}\n\n- (void)setDirection:(ArrowDirection)direction {\n    ArrowDirection oldDirection = _direction;\n    _direction = direction;\n    if (direction != oldDirection) {\n        [self animateLayerUpdates];\n        [self.timer invalidate];\n        if (direction != ArrowNone) {\n            [self sendActionsForControlEvents:UIControlEventValueChanged];\n            self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:NO block:^(NSTimer *timer) {\n                [self sendActionsForControlEvents:UIControlEventValueChanged];\n                self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {\n                    [self sendActionsForControlEvents:UIControlEventValueChanged];\n                }];\n            }];\n        }\n    }\n}\n\n- (UIColor *)textColor {\n    if (self.keyAppearance == UIKeyboardAppearanceLight)\n        return UIColor.blackColor;\n    else\n        return UIColor.whiteColor;\n}\n\n// copy pasted code :dab:\n- (UIColor *)defaultColor {\n    if (self.keyAppearance == UIKeyboardAppearanceLight)\n        return UIColor.whiteColor;\n    else\n        return [UIColor colorWithRed:1 green:1 blue:1 alpha:77/255.];\n}\n- (UIColor *)highlightedColor {\n    if (self.keyAppearance == UIKeyboardAppearanceLight)\n        return [UIColor colorWithRed:172/255. green:180/255. blue:190/255. alpha:1];\n    else\n        return [UIColor colorWithRed:147/255. green:147/255. blue:147/255. alpha:66/255.];\n}\n\n- (void)setColors {\n    if (self.selected) {\n        self.backgroundColor = self.highlightedColor;\n    } else {\n        [UIView animateWithDuration:0 delay:0.1 options:UIViewAnimationOptionAllowUserInteraction animations:^{\n            self.backgroundColor = self.defaultColor;\n        } completion:nil];\n    }\n    for (int d = ArrowUp; d <= ArrowRight; d++) {\n        CATextLayer *layer = (CATextLayer *) arrowLayers[d];\n        if (self.keyAppearance == UIKeyboardAppearanceLight)\n            layer.foregroundColor = UIColor.blackColor.CGColor;\n        else\n            layer.foregroundColor = UIColor.whiteColor.CGColor;\n    }\n    [self animateLayerUpdates];\n}\n\n- (void)setSelected:(BOOL)selected {\n    [super setSelected:selected];\n    [self setColors];\n}\n\n- (void)setKeyAppearance:(UIKeyboardAppearance)keyAppearance {\n    _keyAppearance = keyAppearance;\n    [self setColors];\n}\n\n@end\n"
  },
  {
    "path": "app/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon20x20@2x-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon29x29@2x-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon40x40@2x-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"App Store.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "app/Assets.xcassets/Checkbox.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"checkbox.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"template\",\n    \"preserves-vector-representation\" : true\n  }\n}"
  },
  {
    "path": "app/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "app/Assets.xcassets/Hide Keyboard.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Hide Keyboard.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"template\"\n  }\n}"
  },
  {
    "path": "app/Assets.xcassets/Paste.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Paste.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"template\"\n  }\n}"
  },
  {
    "path": "app/Assets.xcassets/Saddam Hussein.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"satanusen.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "app/Assets.xcassets/X.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"xmark.circle.fill.regular.large.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "app/BarButton.h",
    "content": "//\n//  AccessoryButton.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/22/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface BarButton : UIButton\n\n@property (nonatomic) UIKeyboardAppearance keyAppearance;\n@property IBInspectable BOOL secondary;\n@property IBInspectable BOOL toggleable;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/BarButton.m",
    "content": "//\n//  AccessoryButton.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/22/18.\n//\n\n#import \"BarButton.h\"\n\n@interface BarButton ()\n@end\n\nextern UIAccessibilityTraits UIAccessibilityTraitToggle;\n\n@implementation BarButton\n\n- (void)awakeFromNib {\n    [super awakeFromNib];\n    self.layer.cornerRadius = 5;\n    self.layer.shadowOffset = CGSizeMake(0, 1);\n    self.layer.shadowOpacity = 0.4;\n    self.layer.shadowRadius = 0;\n    self.backgroundColor = self.defaultColor;\n    self.keyAppearance = UIKeyboardAppearanceLight;\n    self.accessibilityTraits |= UIAccessibilityTraitKeyboardKey;\n    if (self.toggleable) {\n        self.accessibilityTraits |= 0x20000000000000;\n    }\n}\n\n- (UIColor *)primaryColor {\n    if (self.keyAppearance == UIKeyboardAppearanceLight)\n        return UIColor.whiteColor;\n    else\n        return [UIColor colorWithRed:1 green:1 blue:1 alpha:77/255.];\n}\n- (UIColor *)secondaryColor {\n    if (self.keyAppearance == UIKeyboardAppearanceLight)\n        return [UIColor colorWithRed:172/255. green:180/255. blue:190/255. alpha:1];\n    else\n        return [UIColor colorWithRed:147/255. green:147/255. blue:147/255. alpha:66/255.];\n}\n- (UIColor *)defaultColor {\n    if (self.secondary)\n        return self.secondaryColor;\n    return self.primaryColor;\n}\n- (UIColor *)highlightedColor {\n    if (!self.secondary)\n        return self.secondaryColor;\n    return self.primaryColor;\n}\n\n- (void)chooseBackground {\n    if (self.selected || self.highlighted) {\n        self.backgroundColor = self.highlightedColor;\n    } else {\n        [UIView animateWithDuration:0 delay:0.1 options:UIViewAnimationOptionAllowUserInteraction animations:^{\n            self.backgroundColor = self.defaultColor;\n        } completion:nil];\n    }\n    if (self.keyAppearance == UIKeyboardAppearanceLight) {\n        self.tintColor = UIColor.blackColor;\n    } else {\n        self.tintColor = UIColor.whiteColor;\n    }\n    [self setTitleColor:self.tintColor forState:UIControlStateNormal];\n}\n\n- (void)setHighlighted:(BOOL)highlighted {\n    [super setHighlighted:highlighted];\n    [self chooseBackground];\n}\n- (void)setSelected:(BOOL)selected {\n    [super setSelected:selected];\n    [self chooseBackground];\n}\n\n- (void)setKeyAppearance:(UIKeyboardAppearance)keyAppearance {\n    _keyAppearance = keyAppearance;\n    [self chooseBackground];\n}\n\n- (NSString *)accessibilityValue {\n    if (self.toggleable) {\n        return self.selected ? @\"1\" : @\"0\";\n    }\n    return nil;\n}\n\n@end\n"
  },
  {
    "path": "app/Base.lproj/About.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"22505\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"0yy-Uo-cZb\">\n    <device id=\"retina4_0\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"22504\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"collection view cell content view\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Settings-->\n        <scene sceneID=\"gzB-oj-TRf\">\n            <objects>\n                <tableViewController id=\"yJz-O7-jlW\" customClass=\"AboutViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"static\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"fxP-eK-TyP\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <view key=\"tableFooterView\" contentMode=\"scaleToFill\" id=\"vHu-FA-YnK\">\n                            <rect key=\"frame\" x=\"0.0\" y=\"856.5\" width=\"320\" height=\"44\"/>\n                            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                            <subviews>\n                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"iSH unknown (Build unknown)\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"Xil-tm-WGD\" userLabel=\"versionLabel\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"44\"/>\n                                    <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" flexibleMaxY=\"YES\"/>\n                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"16\"/>\n                                    <color key=\"textColor\" systemColor=\"secondaryLabelColor\"/>\n                                    <nil key=\"highlightedColor\"/>\n                                </label>\n                                <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"Saddam Hussein\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"7wB-HV-c14\">\n                                    <rect key=\"frame\" x=\"233\" y=\"44\" width=\"79\" height=\"17\"/>\n                                    <constraints>\n                                        <constraint firstAttribute=\"width\" constant=\"79\" id=\"pby-mm-KlE\"/>\n                                        <constraint firstAttribute=\"height\" constant=\"17\" id=\"qax-kw-1lB\"/>\n                                    </constraints>\n                                </imageView>\n                            </subviews>\n                            <constraints>\n                                <constraint firstItem=\"7wB-HV-c14\" firstAttribute=\"top\" secondItem=\"vHu-FA-YnK\" secondAttribute=\"bottom\" id=\"bcR-YQ-PkC\"/>\n                                <constraint firstAttribute=\"trailing\" secondItem=\"7wB-HV-c14\" secondAttribute=\"trailing\" constant=\"8\" id=\"f9L-RV-ToU\"/>\n                            </constraints>\n                        </view>\n                        <sections>\n                            <tableViewSection footerTitle=\"When this option is enabled, the screen will not dim when iSH is in the foreground.\" id=\"tuR-Og-SE8\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"1Co-nG-Sij\" style=\"IBUITableViewCellStyleDefault\" id=\"dqn-Cd-cNm\" userLabel=\"Theme\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"18\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"dqn-Cd-cNm\" id=\"i9s-qj-CxN\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Appearance\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"1Co-nG-Sij\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <segue destination=\"ItN-wa-djf\" kind=\"show\" id=\"Kbq-17-aS2\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"xvW-ah-hsy\" style=\"IBUITableViewCellStyleDefault\" id=\"31N-0g-RhF\" userLabel=\"External Keyboard\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"61.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"31N-0g-RhF\" id=\"VWO-ke-dtY\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"External Keyboard\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"xvW-ah-hsy\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <segue destination=\"Zzy-IN-laJ\" kind=\"show\" id=\"3W3-8y-gVF\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"6Qv-kp-ISr\" style=\"IBUITableViewCellStyleDefault\" id=\"LVQ-SX-dAA\" userLabel=\"Caps Lock\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"105\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"LVQ-SX-dAA\" id=\"plU-fp-Ybd\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"App Icon\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"6Qv-kp-ISr\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <segue destination=\"cfv-rk-mN1\" kind=\"show\" id=\"0jv-rd-adN\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"3CC-Gj-X6f\" userLabel=\"Disable Dimming\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"148.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"3CC-Gj-X6f\" id=\"6Sy-Y9-UdH\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Keep Screen Turned On\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Iov-xn-a0L\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"181\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cPj-sQ-qsh\">\n                                                    <rect key=\"frame\" x=\"255\" y=\"6.5\" width=\"51\" height=\"31\"/>\n                                                    <connections>\n                                                        <action selector=\"disableDimmingChanged:\" destination=\"yJz-O7-jlW\" eventType=\"valueChanged\" id=\"usF-cn-Kbp\"/>\n                                                    </connections>\n                                                </switch>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"cPj-sQ-qsh\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"Iov-xn-a0L\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"9uc-hi-DmQ\"/>\n                                                <constraint firstItem=\"Iov-xn-a0L\" firstAttribute=\"leading\" secondItem=\"6Sy-Y9-UdH\" secondAttribute=\"leadingMargin\" id=\"aLB-ta-oqS\"/>\n                                                <constraint firstItem=\"cPj-sQ-qsh\" firstAttribute=\"centerY\" secondItem=\"6Sy-Y9-UdH\" secondAttribute=\"centerY\" id=\"eCa-AP-AqQ\"/>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"cPj-sQ-qsh\" secondAttribute=\"trailing\" id=\"jaj-Bb-Tg1\"/>\n                                                <constraint firstItem=\"Iov-xn-a0L\" firstAttribute=\"centerY\" secondItem=\"6Sy-Y9-UdH\" secondAttribute=\"centerY\" id=\"sI8-H8-9M6\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection id=\"u3N-3a-97w\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"dVs-7k-zeJ\" style=\"IBUITableViewCellStyleDefault\" id=\"HHU-Ju-BtM\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"256\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"HHU-Ju-BtM\" id=\"CqF-qp-HgQ\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Filesystems\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"dVs-7k-zeJ\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <segue destination=\"gsu-Hv-LDC\" kind=\"show\" id=\"tif-za-oNY\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" id=\"Huf-xT-ejJ\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"299.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"Huf-xT-ejJ\" id=\"44W-gn-26V\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Upgrade Repositories\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"L09-wA-jCg\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"165\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"l5o-Y7-j4W\">\n                                                    <rect key=\"frame\" x=\"275.5\" y=\"17\" width=\"10\" height=\"10\"/>\n                                                    <color key=\"backgroundColor\" systemColor=\"systemRedColor\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"10\" id=\"28W-HE-QpJ\"/>\n                                                        <constraint firstAttribute=\"width\" constant=\"10\" id=\"iaA-gZ-0Pn\"/>\n                                                    </constraints>\n                                                    <userDefinedRuntimeAttributes>\n                                                        <userDefinedRuntimeAttribute type=\"number\" keyPath=\"layer.cornerRadius\">\n                                                            <integer key=\"value\" value=\"5\"/>\n                                                        </userDefinedRuntimeAttribute>\n                                                    </userDefinedRuntimeAttributes>\n                                                </view>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"L09-wA-jCg\" firstAttribute=\"centerY\" secondItem=\"44W-gn-26V\" secondAttribute=\"centerY\" id=\"4He-R9-ToQ\"/>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"l5o-Y7-j4W\" secondAttribute=\"trailing\" id=\"8b0-UB-r6d\"/>\n                                                <constraint firstItem=\"L09-wA-jCg\" firstAttribute=\"leading\" secondItem=\"44W-gn-26V\" secondAttribute=\"leadingMargin\" id=\"l0q-Rr-W6r\"/>\n                                                <constraint firstItem=\"l5o-Y7-j4W\" firstAttribute=\"centerY\" secondItem=\"44W-gn-26V\" secondAttribute=\"centerY\" id=\"ppV-Dx-99y\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <segue destination=\"9eV-Jd-Xng\" kind=\"show\" id=\"R7o-uj-fFN\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection footerTitle=\"\" id=\"DVR-sH-TdL\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"g6W-FX-yYa\" style=\"IBUITableViewCellStyleDefault\" id=\"gMm-4C-5X3\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"379\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"gMm-4C-5X3\" id=\"mVT-2h-5kM\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Send Feedback\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"g6W-FX-yYa\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"Fe5-tr-fWm\" style=\"IBUITableViewCellStyleDefault\" id=\"F4i-eC-hQ6\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"422.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"F4i-eC-hQ6\" id=\"gU6-0E-hOf\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"iSH on GitHub\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"Fe5-tr-fWm\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"xMw-wC-igF\" style=\"IBUITableViewCellStyleDefault\" id=\"K5r-jy-Dzl\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"466\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"K5r-jy-Dzl\" id=\"UIr-IB-yu1\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"iSH Discord Server\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"xMw-wC-igF\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" textLabel=\"wyO-AY-ccm\" style=\"IBUITableViewCellStyleDefault\" id=\"bge-GA-6p8\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"509.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"bge-GA-6p8\" id=\"JXA-Ff-hkB\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"iSH on the Fediverse\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"wyO-AY-ccm\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"269.5\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection headerTitle=\"Secret Advanced Debugging Options\" footerTitle=\"Warning: Changing these can break everything! If that happens, there's a reset switch in the Settings app.\" id=\"d0T-DL-SuP\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"RKb-ed-FOs\" userLabel=\"Init Command\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"620.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"RKb-ed-FOs\" id=\"I4F-81-mWS\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" horizontalHuggingPriority=\"1000\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Launch cmd\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"G8D-8d-aOp\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"94\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" text=\"/bin/login -f root\" adjustsFontSizeToFit=\"NO\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gDx-hv-hhV\">\n                                                    <rect key=\"frame\" x=\"122\" y=\"13\" width=\"182\" height=\"18\"/>\n                                                    <fontDescription key=\"fontDescription\" name=\"Menlo-Regular\" family=\"Menlo\" pointSize=\"14\"/>\n                                                    <textInputTraits key=\"textInputTraits\" autocorrectionType=\"no\" spellCheckingType=\"no\" keyboardType=\"alphabet\" returnKeyType=\"done\" smartDashesType=\"no\" smartInsertDeleteType=\"no\" smartQuotesType=\"no\"/>\n                                                    <connections>\n                                                        <action selector=\"launchCommandChanged:\" destination=\"yJz-O7-jlW\" eventType=\"editingDidEnd\" id=\"y9m-tQ-j9F\"/>\n                                                        <action selector=\"textBoxSubmit:\" destination=\"yJz-O7-jlW\" eventType=\"primaryActionTriggered\" id=\"Dyo-E6-Kdv\"/>\n                                                    </connections>\n                                                </textField>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"gDx-hv-hhV\" secondAttribute=\"trailing\" id=\"D9N-xY-fFU\"/>\n                                                <constraint firstItem=\"gDx-hv-hhV\" firstAttribute=\"centerY\" secondItem=\"I4F-81-mWS\" secondAttribute=\"centerY\" id=\"cX0-Gk-kRS\"/>\n                                                <constraint firstItem=\"G8D-8d-aOp\" firstAttribute=\"leading\" secondItem=\"I4F-81-mWS\" secondAttribute=\"leadingMargin\" id=\"fgO-5O-vR0\"/>\n                                                <constraint firstItem=\"gDx-hv-hhV\" firstAttribute=\"leading\" secondItem=\"G8D-8d-aOp\" secondAttribute=\"trailing\" constant=\"12\" id=\"hBx-u1-IKQ\"/>\n                                                <constraint firstItem=\"G8D-8d-aOp\" firstAttribute=\"centerY\" secondItem=\"I4F-81-mWS\" secondAttribute=\"centerY\" id=\"uj3-2p-3Sp\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"Xc7-9V-RXm\" userLabel=\"Boot Command\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"664\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"Xc7-9V-RXm\" id=\"IEt-PC-8fZ\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" horizontalHuggingPriority=\"1000\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Boot cmd\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Ivg-bB-NKL\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"74\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" text=\"/bin/login -f root\" adjustsFontSizeToFit=\"NO\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bKw-tV-nX1\">\n                                                    <rect key=\"frame\" x=\"102\" y=\"13\" width=\"202\" height=\"18\"/>\n                                                    <fontDescription key=\"fontDescription\" name=\"Menlo-Regular\" family=\"Menlo\" pointSize=\"14\"/>\n                                                    <textInputTraits key=\"textInputTraits\" autocorrectionType=\"no\" spellCheckingType=\"no\" keyboardType=\"alphabet\" returnKeyType=\"done\" smartDashesType=\"no\" smartInsertDeleteType=\"no\" smartQuotesType=\"no\"/>\n                                                    <connections>\n                                                        <action selector=\"bootCommandChanged:\" destination=\"yJz-O7-jlW\" eventType=\"editingDidEnd\" id=\"ejb-CC-17T\"/>\n                                                        <action selector=\"textBoxSubmit:\" destination=\"yJz-O7-jlW\" eventType=\"primaryActionTriggered\" id=\"yMs-ke-b76\"/>\n                                                    </connections>\n                                                </textField>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"bKw-tV-nX1\" secondAttribute=\"trailing\" id=\"CvM-TM-j0U\"/>\n                                                <constraint firstItem=\"Ivg-bB-NKL\" firstAttribute=\"centerY\" secondItem=\"IEt-PC-8fZ\" secondAttribute=\"centerY\" id=\"Exg-3t-5dt\"/>\n                                                <constraint firstItem=\"Ivg-bB-NKL\" firstAttribute=\"leading\" secondItem=\"IEt-PC-8fZ\" secondAttribute=\"leadingMargin\" id=\"Qck-mT-DsJ\"/>\n                                                <constraint firstItem=\"bKw-tV-nX1\" firstAttribute=\"centerY\" secondItem=\"IEt-PC-8fZ\" secondAttribute=\"centerY\" id=\"RgS-ZD-0gA\"/>\n                                                <constraint firstItem=\"bKw-tV-nX1\" firstAttribute=\"leading\" secondItem=\"Ivg-bB-NKL\" secondAttribute=\"trailing\" constant=\"12\" id=\"o1M-2S-D93\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"blue\" indentationWidth=\"10\" textLabel=\"83j-2z-XRR\" style=\"IBUITableViewCellStyleDefault\" id=\"OIE-9g-Btx\" userLabel=\"Export\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"707.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"OIE-9g-Btx\" id=\"uY3-tT-dwz\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Export container\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"83j-2z-XRR\">\n                                                    <rect key=\"frame\" x=\"8\" y=\"0.0\" width=\"304\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"blue\" indentationWidth=\"10\" textLabel=\"Ozh-OX-eF1\" style=\"IBUITableViewCellStyleDefault\" id=\"Bqt-ud-CI0\" userLabel=\"Reset mounts\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"751\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"Bqt-ud-CI0\" id=\"K2f-8z-let\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Reset ios mounts\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"Ozh-OX-eF1\">\n                                                    <rect key=\"frame\" x=\"8\" y=\"0.0\" width=\"304\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                        </sections>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"yJz-O7-jlW\" id=\"UX1-BB-BA1\"/>\n                            <outlet property=\"delegate\" destination=\"yJz-O7-jlW\" id=\"sDs-T2-0bL\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Settings\" id=\"bpe-0g-cWF\">\n                        <barButtonItem key=\"rightBarButtonItem\" style=\"done\" systemItem=\"done\" id=\"vOn-qw-XSp\">\n                            <connections>\n                                <action selector=\"dismiss:\" destination=\"yJz-O7-jlW\" id=\"q2h-Xl-XBU\"/>\n                            </connections>\n                        </barButtonItem>\n                    </navigationItem>\n                    <connections>\n                        <outlet property=\"bootCommandField\" destination=\"bKw-tV-nX1\" id=\"MVz-gk-QE7\"/>\n                        <outlet property=\"capsLockMappingCell\" destination=\"31N-0g-RhF\" id=\"UjF-21-3df\"/>\n                        <outlet property=\"disableDimmingSwitch\" destination=\"cPj-sQ-qsh\" id=\"Kjp-xA-A0Y\"/>\n                        <outlet property=\"exportContainerCell\" destination=\"OIE-9g-Btx\" id=\"qxo-l5-0Mq\"/>\n                        <outlet property=\"launchCommandField\" destination=\"gDx-hv-hhV\" id=\"RQn-gk-Dx8\"/>\n                        <outlet property=\"openDiscord\" destination=\"K5r-jy-Dzl\" id=\"b4l-Vr-6l8\"/>\n                        <outlet property=\"openFediverse\" destination=\"bge-GA-6p8\" id=\"ghM-X8-itm\"/>\n                        <outlet property=\"openGithub\" destination=\"F4i-eC-hQ6\" id=\"66M-sQ-RJR\"/>\n                        <outlet property=\"resetMountsCell\" destination=\"Bqt-ud-CI0\" id=\"2AS-2S-XCr\"/>\n                        <outlet property=\"saddamHussein\" destination=\"bcR-YQ-PkC\" id=\"mUA-6I-CB9\"/>\n                        <outlet property=\"sendFeedback\" destination=\"gMm-4C-5X3\" id=\"nhX-ZH-zyP\"/>\n                        <outlet property=\"themeCell\" destination=\"dqn-Cd-cNm\" id=\"dfh-et-0o7\"/>\n                        <outlet property=\"upgradeApkBadge\" destination=\"l5o-Y7-j4W\" id=\"AI2-IM-NVw\"/>\n                        <outlet property=\"upgradeApkCell\" destination=\"Huf-xT-ejJ\" id=\"CBX-wc-11e\"/>\n                        <outlet property=\"upgradeApkLabel\" destination=\"L09-wA-jCg\" id=\"wMj-ht-DsI\"/>\n                        <outlet property=\"versionLabel\" destination=\"Xil-tm-WGD\" id=\"ZzC-E6-VER\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"OPJ-Ps-ATb\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"243.75\" y=\"157.3943661971831\"/>\n        </scene>\n        <!--External Keyboard-->\n        <scene sceneID=\"VbH-40-Znm\">\n            <objects>\n                <tableViewController title=\"External Keyboard\" id=\"Zzy-IN-laJ\" customClass=\"AboutExternalKeyboardViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"static\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"ZlS-Ig-a8Z\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <sections>\n                            <tableViewSection headerTitle=\"Map Caps Lock\" id=\"oTd-4i-TSa\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" tag=\"1\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"zuo-ZW-n37\" style=\"IBUITableViewCellStyleDefault\" id=\"8ZS-Wp-1TX\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"55.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"8ZS-Wp-1TX\" id=\"GMp-XT-GYq\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Control\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"zuo-ZW-n37\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" tag=\"2\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"NR7-h5-BZD\" style=\"IBUITableViewCellStyleDefault\" id=\"IMH-6g-rLD\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"99\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"IMH-6g-rLD\" id=\"Qea-lO-LfG\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Escape\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"NR7-h5-BZD\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"CXm-1h-hy1\" style=\"IBUITableViewCellStyleDefault\" id=\"zqH-hq-4Oi\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"142.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"zqH-hq-4Oi\" id=\"Wpu-8A-Fbd\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"None\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"CXm-1h-hy1\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection id=\"zbj-zP-Xeq\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"cA4-62-G0Z\" userLabel=\"Option to Meta\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"222\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"cA4-62-G0Z\" id=\"ZYe-JS-rbD\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Option → Meta\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"qax-I0-tqo\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"114\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"OEG-YY-LTw\">\n                                                    <rect key=\"frame\" x=\"255\" y=\"6.5\" width=\"51\" height=\"31\"/>\n                                                    <connections>\n                                                        <action selector=\"optionMetaToggle:\" destination=\"Zzy-IN-laJ\" eventType=\"valueChanged\" id=\"HgW-4j-jIU\"/>\n                                                    </connections>\n                                                </switch>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"OEG-YY-LTw\" secondAttribute=\"trailing\" id=\"Gs0-yB-zcq\"/>\n                                                <constraint firstItem=\"OEG-YY-LTw\" firstAttribute=\"centerY\" secondItem=\"ZYe-JS-rbD\" secondAttribute=\"centerY\" id=\"MSx-We-wjF\"/>\n                                                <constraint firstItem=\"qax-I0-tqo\" firstAttribute=\"centerY\" secondItem=\"ZYe-JS-rbD\" secondAttribute=\"centerY\" id=\"dct-90-ZT3\"/>\n                                                <constraint firstItem=\"OEG-YY-LTw\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"qax-I0-tqo\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"hQM-OL-TER\"/>\n                                                <constraint firstItem=\"qax-I0-tqo\" firstAttribute=\"leading\" secondItem=\"ZYe-JS-rbD\" secondAttribute=\"leadingMargin\" id=\"rrF-nN-OZe\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"NQy-68-Suu\" userLabel=\"Backtick to Escape\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"265.5\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"NQy-68-Suu\" id=\"9xb-BP-fQ4\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Backtick → Escape\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zWx-Iz-vdm\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"144.5\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8xc-Rx-eue\">\n                                                    <rect key=\"frame\" x=\"255\" y=\"6.5\" width=\"51\" height=\"31\"/>\n                                                    <connections>\n                                                        <action selector=\"backtickEscapeToggle:\" destination=\"Zzy-IN-laJ\" eventType=\"valueChanged\" id=\"p9Y-6t-nCW\"/>\n                                                    </connections>\n                                                </switch>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"8xc-Rx-eue\" secondAttribute=\"trailing\" id=\"eUW-U2-ocF\"/>\n                                                <constraint firstItem=\"zWx-Iz-vdm\" firstAttribute=\"centerY\" secondItem=\"9xb-BP-fQ4\" secondAttribute=\"centerY\" id=\"rCn-c3-wFO\"/>\n                                                <constraint firstItem=\"8xc-Rx-eue\" firstAttribute=\"centerY\" secondItem=\"9xb-BP-fQ4\" secondAttribute=\"centerY\" id=\"rsO-uq-LfO\"/>\n                                                <constraint firstItem=\"zWx-Iz-vdm\" firstAttribute=\"leading\" secondItem=\"9xb-BP-fQ4\" secondAttribute=\"leadingMargin\" id=\"rzg-p7-z1r\"/>\n                                                <constraint firstItem=\"8xc-Rx-eue\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"zWx-Iz-vdm\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"vLw-yG-Xlc\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection footerTitle=\"If this is off, ctrl-space switches keyboard layouts.\" id=\"Ucf-cb-qkd\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"9gO-wR-cmz\" userLabel=\"Backtick to Escape\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"345\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"9gO-wR-cmz\" id=\"v3f-Ht-Vrg\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Send ctrl-space to terminal\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"KvX-Y2-vlm\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"207.5\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"554-OF-h41\">\n                                                    <rect key=\"frame\" x=\"255\" y=\"6.5\" width=\"51\" height=\"31\"/>\n                                                    <connections>\n                                                        <action selector=\"overrideControlSpaceToggle:\" destination=\"Zzy-IN-laJ\" eventType=\"valueChanged\" id=\"IxB-9q-539\"/>\n                                                    </connections>\n                                                </switch>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"KvX-Y2-vlm\" firstAttribute=\"leading\" secondItem=\"v3f-Ht-Vrg\" secondAttribute=\"leadingMargin\" id=\"O30-Pm-qhg\"/>\n                                                <constraint firstItem=\"554-OF-h41\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"KvX-Y2-vlm\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"WXv-yr-I8K\"/>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"554-OF-h41\" secondAttribute=\"trailing\" id=\"fXw-Jh-WJU\"/>\n                                                <constraint firstItem=\"KvX-Y2-vlm\" firstAttribute=\"centerY\" secondItem=\"v3f-Ht-Vrg\" secondAttribute=\"centerY\" id=\"nMZ-KF-V5c\"/>\n                                                <constraint firstItem=\"554-OF-h41\" firstAttribute=\"centerY\" secondItem=\"v3f-Ht-Vrg\" secondAttribute=\"centerY\" id=\"ud3-BX-Rdt\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection headerTitle=\"Extra keys\" footerTitle=\"When extra keys are hidden, Settings can be opened with the ⌘, shortcut\" id=\"Kaj-9x-pfF\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"TM9-T2-EDa\" userLabel=\"Backtick to Escape\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"480\" width=\"320\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"TM9-T2-EDa\" id=\"K8q-us-D80\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Hide with external keyboard\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"PWB-d9-Zhd\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"212\" height=\"21\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"IcN-9M-x1K\">\n                                                    <rect key=\"frame\" x=\"255\" y=\"6.5\" width=\"51\" height=\"31\"/>\n                                                    <connections>\n                                                        <action selector=\"hideExtraKeysToggle:\" destination=\"Zzy-IN-laJ\" eventType=\"valueChanged\" id=\"kQA-Pi-MZM\"/>\n                                                    </connections>\n                                                </switch>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"PWB-d9-Zhd\" firstAttribute=\"leading\" secondItem=\"K8q-us-D80\" secondAttribute=\"leadingMargin\" id=\"FGk-hn-hnq\"/>\n                                                <constraint firstItem=\"PWB-d9-Zhd\" firstAttribute=\"centerY\" secondItem=\"K8q-us-D80\" secondAttribute=\"centerY\" id=\"I9n-5H-Jr4\"/>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"IcN-9M-x1K\" secondAttribute=\"trailing\" id=\"fig-KE-i9e\"/>\n                                                <constraint firstItem=\"IcN-9M-x1K\" firstAttribute=\"centerY\" secondItem=\"K8q-us-D80\" secondAttribute=\"centerY\" id=\"wZF-ty-HqH\"/>\n                                                <constraint firstItem=\"IcN-9M-x1K\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"PWB-d9-Zhd\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"xr3-MR-x1Q\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                        </sections>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"Zzy-IN-laJ\" id=\"NPA-6F-jMj\"/>\n                            <outlet property=\"delegate\" destination=\"Zzy-IN-laJ\" id=\"g26-vd-TC5\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"External Keyboard\" id=\"s64-vm-Jln\"/>\n                    <connections>\n                        <outlet property=\"backtickEscapeSwitch\" destination=\"8xc-Rx-eue\" id=\"jkl-oc-V9N\"/>\n                        <outlet property=\"hideExtraKeysWithExternalKeyboardSwitch\" destination=\"IcN-9M-x1K\" id=\"Lw3-4l-9R6\"/>\n                        <outlet property=\"optionMetaSwitch\" destination=\"OEG-YY-LTw\" id=\"b2K-Ug-Jij\"/>\n                        <outlet property=\"overrideControlSpaceSwitch\" destination=\"554-OF-h41\" id=\"js8-8D-1Ua\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"XlH-jX-0dL\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1057.5\" y=\"429.92957746478874\"/>\n        </scene>\n        <!--App Icon-->\n        <scene sceneID=\"jc8-Gv-xpK\">\n            <objects>\n                <viewController title=\"App Icon\" id=\"cfv-rk-mN1\" customClass=\"AltIconViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"bZC-Ac-YDX\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <collectionView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" dataMode=\"prototypes\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wGQ-0K-Rcg\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"64\" width=\"320\" height=\"504\"/>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                <collectionViewFlowLayout key=\"collectionViewLayout\" automaticEstimatedItemSize=\"YES\" minimumLineSpacing=\"20\" minimumInteritemSpacing=\"0.0\" id=\"zz1-Bs-CFW\">\n                                    <size key=\"itemSize\" width=\"120\" height=\"120\"/>\n                                    <size key=\"headerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                                    <size key=\"footerReferenceSize\" width=\"50\" height=\"50\"/>\n                                    <inset key=\"sectionInset\" minX=\"30\" minY=\"30\" maxX=\"30\" maxY=\"30\"/>\n                                    <userDefinedRuntimeAttributes>\n                                        <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"sectionFootersPinToVisibleBounds\" value=\"NO\"/>\n                                    </userDefinedRuntimeAttributes>\n                                </collectionViewFlowLayout>\n                                <cells>\n                                    <collectionViewCell opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" reuseIdentifier=\"icon\" id=\"S3m-Be-XtT\" customClass=\"AltIconCell\">\n                                        <rect key=\"frame\" x=\"30\" y=\"30\" width=\"120\" height=\"147\"/>\n                                        <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                        <collectionViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"IWa-a1-skf\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"120\" height=\"147\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                            <subviews>\n                                                <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"icon.png\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9aw-Kk-k95\">\n                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"120\" height=\"120\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"width\" constant=\"120\" id=\"2sn-wy-ZoN\"/>\n                                                        <constraint firstAttribute=\"height\" constant=\"120\" id=\"Zde-qc-BM8\"/>\n                                                    </constraints>\n                                                </imageView>\n                                                <imageView hidden=\"YES\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"Checkbox\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cAC-0O-uY6\">\n                                                    <rect key=\"frame\" x=\"74\" y=\"74\" width=\"38\" height=\"38\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"width\" constant=\"38\" id=\"WO8-mB-47S\"/>\n                                                        <constraint firstAttribute=\"height\" constant=\"38\" id=\"fG2-qK-0uJ\"/>\n                                                    </constraints>\n                                                </imageView>\n                                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"WNa-qg-ZN5\">\n                                                    <rect key=\"frame\" x=\"0.0\" y=\"120\" width=\"120\" height=\"27\"/>\n                                                    <fontDescription key=\"fontDescription\" style=\"UICTFontTextStyleCaption1\"/>\n                                                    <state key=\"normal\" title=\"by @author\"/>\n                                                    <connections>\n                                                        <action selector=\"openSource:\" destination=\"S3m-Be-XtT\" eventType=\"primaryActionTriggered\" id=\"YSV-x5-8bZ\"/>\n                                                    </connections>\n                                                </button>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"9aw-Kk-k95\" firstAttribute=\"bottom\" secondItem=\"cAC-0O-uY6\" secondAttribute=\"bottom\" constant=\"8\" id=\"1Nx-lw-WeM\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"WNa-qg-ZN5\" secondAttribute=\"trailing\" id=\"1QP-qJ-bqg\"/>\n                                                <constraint firstItem=\"9aw-Kk-k95\" firstAttribute=\"top\" secondItem=\"IWa-a1-skf\" secondAttribute=\"top\" id=\"79p-Wm-2JF\"/>\n                                                <constraint firstItem=\"9aw-Kk-k95\" firstAttribute=\"leading\" secondItem=\"IWa-a1-skf\" secondAttribute=\"leading\" id=\"aFx-qK-TC2\"/>\n                                                <constraint firstAttribute=\"bottom\" secondItem=\"WNa-qg-ZN5\" secondAttribute=\"bottom\" id=\"aGh-9T-AGA\"/>\n                                                <constraint firstItem=\"WNa-qg-ZN5\" firstAttribute=\"leading\" secondItem=\"IWa-a1-skf\" secondAttribute=\"leading\" id=\"cdk-fK-xdr\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"9aw-Kk-k95\" secondAttribute=\"trailing\" id=\"jeT-pz-ZFy\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"cAC-0O-uY6\" secondAttribute=\"trailing\" constant=\"8\" id=\"l0V-Kb-Ul1\"/>\n                                                <constraint firstItem=\"WNa-qg-ZN5\" firstAttribute=\"top\" secondItem=\"9aw-Kk-k95\" secondAttribute=\"bottom\" id=\"oF2-JH-EmI\"/>\n                                            </constraints>\n                                        </collectionViewCellContentView>\n                                        <connections>\n                                            <outlet property=\"authorButton\" destination=\"WNa-qg-ZN5\" id=\"3mb-95-R06\"/>\n                                            <outlet property=\"checkboxImageView\" destination=\"cAC-0O-uY6\" id=\"9Jd-uf-T9P\"/>\n                                            <outlet property=\"imageView\" destination=\"9aw-Kk-k95\" id=\"tW1-tN-k8G\"/>\n                                        </connections>\n                                    </collectionViewCell>\n                                </cells>\n                                <collectionReusableView key=\"sectionFooterView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" reuseIdentifier=\"footer\" id=\"lNw-1e-UqP\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"207\" width=\"320\" height=\"50\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <visualEffectView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"L98-Nf-4Wv\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"50\"/>\n                                            <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"qL6-vf-qc3\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"50\"/>\n                                                <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                                <subviews>\n                                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"E9e-hb-zQw\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"50\"/>\n                                                        <state key=\"normal\" title=\"Submit your own icon!\"/>\n                                                        <connections>\n                                                            <action selector=\"openSubmissions:\" destination=\"cfv-rk-mN1\" eventType=\"primaryActionTriggered\" id=\"d9r-k6-6Vz\"/>\n                                                        </connections>\n                                                    </button>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstItem=\"E9e-hb-zQw\" firstAttribute=\"leading\" secondItem=\"qL6-vf-qc3\" secondAttribute=\"leading\" id=\"07g-r1-yQe\"/>\n                                                    <constraint firstAttribute=\"bottom\" secondItem=\"E9e-hb-zQw\" secondAttribute=\"bottom\" id=\"1Nw-tH-oGL\"/>\n                                                    <constraint firstAttribute=\"trailing\" secondItem=\"E9e-hb-zQw\" secondAttribute=\"trailing\" id=\"Mr4-0C-09T\"/>\n                                                    <constraint firstItem=\"E9e-hb-zQw\" firstAttribute=\"top\" secondItem=\"qL6-vf-qc3\" secondAttribute=\"top\" id=\"wcu-3V-6BT\"/>\n                                                </constraints>\n                                            </view>\n                                            <blurEffect style=\"regular\"/>\n                                        </visualEffectView>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstItem=\"L98-Nf-4Wv\" firstAttribute=\"leading\" secondItem=\"lNw-1e-UqP\" secondAttribute=\"leading\" id=\"6Rs-ht-afI\"/>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"L98-Nf-4Wv\" secondAttribute=\"trailing\" id=\"F9n-YU-6bs\"/>\n                                        <constraint firstItem=\"L98-Nf-4Wv\" firstAttribute=\"top\" secondItem=\"lNw-1e-UqP\" secondAttribute=\"top\" id=\"b2D-gK-294\"/>\n                                        <constraint firstAttribute=\"bottom\" secondItem=\"L98-Nf-4Wv\" secondAttribute=\"bottom\" id=\"yy8-dN-Lqd\"/>\n                                    </constraints>\n                                </collectionReusableView>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"cfv-rk-mN1\" id=\"Ejj-y8-k4C\"/>\n                                    <outlet property=\"delegate\" destination=\"cfv-rk-mN1\" id=\"LaN-3H-ht3\"/>\n                                </connections>\n                            </collectionView>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"G6v-G0-r5a\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"wGQ-0K-Rcg\" firstAttribute=\"top\" secondItem=\"G6v-G0-r5a\" secondAttribute=\"top\" id=\"ECg-8n-6xl\"/>\n                            <constraint firstItem=\"wGQ-0K-Rcg\" firstAttribute=\"trailing\" secondItem=\"G6v-G0-r5a\" secondAttribute=\"trailing\" id=\"JXu-0v-zd6\"/>\n                            <constraint firstItem=\"wGQ-0K-Rcg\" firstAttribute=\"leading\" secondItem=\"G6v-G0-r5a\" secondAttribute=\"leading\" id=\"o7s-il-U4s\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"wGQ-0K-Rcg\" secondAttribute=\"bottom\" id=\"pTK-0F-HyJ\"/>\n                        </constraints>\n                    </view>\n                    <navigationItem key=\"navigationItem\" title=\"App Icon\" id=\"5QX-R3-V8P\"/>\n                    <connections>\n                        <outlet property=\"collectionView\" destination=\"wGQ-0K-Rcg\" id=\"GJB-nY-8gE\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"wE1-cE-kl7\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1057.5\" y=\"1132.3943661971832\"/>\n        </scene>\n        <!--Upgrade-->\n        <scene sceneID=\"D8q-Fv-Sfr\">\n            <objects>\n                <viewController title=\"Upgrade\" id=\"9eV-Jd-Xng\" customClass=\"UpgradeRootViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"GV8-4O-0fU\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"6OQ-mF-Mur\" customClass=\"TerminalView\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"64\" width=\"320\" height=\"504\"/>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                <userDefinedRuntimeAttributes>\n                                    <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"canBecomeFirstResponder\" value=\"NO\"/>\n                                </userDefinedRuntimeAttributes>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"1GV-qG-uh7\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"1GV-qG-uh7\" firstAttribute=\"trailing\" secondItem=\"6OQ-mF-Mur\" secondAttribute=\"trailing\" id=\"ZCA-Q1-Odv\"/>\n                            <constraint firstItem=\"6OQ-mF-Mur\" firstAttribute=\"leading\" secondItem=\"1GV-qG-uh7\" secondAttribute=\"leading\" id=\"gbg-DM-mLG\"/>\n                            <constraint firstItem=\"6OQ-mF-Mur\" firstAttribute=\"top\" secondItem=\"1GV-qG-uh7\" secondAttribute=\"top\" id=\"jGK-vN-hUm\"/>\n                            <constraint firstItem=\"1GV-qG-uh7\" firstAttribute=\"bottom\" secondItem=\"6OQ-mF-Mur\" secondAttribute=\"bottom\" id=\"mK6-ba-K1i\"/>\n                        </constraints>\n                    </view>\n                    <navigationItem key=\"navigationItem\" title=\"Upgrade\" id=\"uXA-OB-CN7\">\n                        <barButtonItem key=\"rightBarButtonItem\" title=\"Start\" style=\"done\" id=\"R2f-RB-uga\">\n                            <connections>\n                                <action selector=\"upgrade:\" destination=\"9eV-Jd-Xng\" id=\"LNT-XH-Qvi\"/>\n                            </connections>\n                        </barButtonItem>\n                    </navigationItem>\n                    <connections>\n                        <outlet property=\"terminalView\" destination=\"6OQ-mF-Mur\" id=\"mGv-Yc-HQL\"/>\n                        <outlet property=\"upgradeButton\" destination=\"R2f-RB-uga\" id=\"n2L-Vs-nDF\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"Ncd-Qn-RO3\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1057.5\" y=\"1810.5633802816901\"/>\n        </scene>\n        <!--Appearance-->\n        <scene sceneID=\"Sh1-9Z-h4V\">\n            <objects>\n                <tableViewController title=\"Appearance\" id=\"ItN-wa-djf\" customClass=\"AboutAppearanceViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"mE8-Gn-C1h\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Preview\" id=\"X0d-0Q-g9Z\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"55.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"X0d-0Q-g9Z\" id=\"vGk-di-wEa\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <view tag=\"1\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8Hp-uz-xk8\" customClass=\"TerminalView\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                            <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                            <constraints>\n                                                <constraint firstAttribute=\"width\" constant=\"10000\" id=\"cS3-3L-ORC\"/>\n                                            </constraints>\n                                        </view>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstItem=\"8Hp-uz-xk8\" firstAttribute=\"leading\" secondItem=\"vGk-di-wEa\" secondAttribute=\"leading\" id=\"IvO-5q-Ir8\"/>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"8Hp-uz-xk8\" secondAttribute=\"trailing\" id=\"Mi3-jN-uSP\"/>\n                                        <constraint firstItem=\"8Hp-uz-xk8\" firstAttribute=\"top\" secondItem=\"vGk-di-wEa\" secondAttribute=\"top\" id=\"ays-3b-poh\"/>\n                                        <constraint firstAttribute=\"bottom\" secondItem=\"8Hp-uz-xk8\" secondAttribute=\"bottom\" id=\"xt8-nS-iIx\"/>\n                                    </constraints>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Color Scheme Preview\" id=\"7Dn-Oz-2zq\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"99\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"7Dn-Oz-2zq\" id=\"jj6-87-x30\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <segmentedControl opaque=\"NO\" tag=\"1\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aLu-k6-NCW\">\n                                            <rect key=\"frame\" x=\"20\" y=\"6.5\" width=\"280\" height=\"32\"/>\n                                            <segments>\n                                                <segment title=\"Light\"/>\n                                                <segment title=\"Dark\"/>\n                                            </segments>\n                                        </segmentedControl>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"aLu-k6-NCW\" secondAttribute=\"trailing\" constant=\"20\" symbolic=\"YES\" id=\"ZsL-av-hVD\"/>\n                                        <constraint firstItem=\"aLu-k6-NCW\" firstAttribute=\"centerY\" secondItem=\"jj6-87-x30\" secondAttribute=\"centerY\" id=\"e45-2q-GEK\"/>\n                                        <constraint firstItem=\"aLu-k6-NCW\" firstAttribute=\"leading\" secondItem=\"jj6-87-x30\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"rgN-F3-Eni\"/>\n                                    </constraints>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" reuseIdentifier=\"Theme Name\" textLabel=\"6jC-28-tzS\" detailTextLabel=\"5o9-8G-nwe\" style=\"IBUITableViewCellStyleValue1\" id=\"4dw-XR-CAv\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"142.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"4dw-XR-CAv\" id=\"OG6-NN-pG6\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Theme\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"6jC-28-tzS\">\n                                            <rect key=\"frame\" x=\"16\" y=\"12\" width=\"53\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Default\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"5o9-8G-nwe\">\n                                            <rect key=\"frame\" x=\"230.5\" y=\"12\" width=\"55\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <color key=\"textColor\" systemColor=\"secondaryLabelColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" reuseIdentifier=\"Font\" textLabel=\"gtJ-eb-DvQ\" detailTextLabel=\"5wC-O1-dx3\" style=\"IBUITableViewCellStyleValue1\" id=\"qEy-gj-mmp\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"186\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"qEy-gj-mmp\" id=\"lIf-2w-14C\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"293.5\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Font\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"gtJ-eb-DvQ\">\n                                            <rect key=\"frame\" x=\"16\" y=\"12\" width=\"34.5\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Menlo\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"5wC-O1-dx3\">\n                                            <rect key=\"frame\" x=\"238.5\" y=\"12\" width=\"47\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <color key=\"textColor\" systemColor=\"secondaryLabelColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" reuseIdentifier=\"Font Size\" id=\"CTf-zd-xjl\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"229.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"CTf-zd-xjl\" id=\"udT-7d-Xzj\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Font Size\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"4j6-te-7NU\">\n                                            <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"71\" height=\"21\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" tag=\"1\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"12\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Fa7-Ao-v3L\">\n                                            <rect key=\"frame\" x=\"176\" y=\"11.5\" width=\"18\" height=\"21\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <stepper opaque=\"NO\" tag=\"2\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"1\" minimumValue=\"1\" maximumValue=\"72\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YM7-vq-8vn\">\n                                            <rect key=\"frame\" x=\"210\" y=\"6\" width=\"94\" height=\"32\"/>\n                                            <connections>\n                                                <action selector=\"fontSizeChanged:\" destination=\"ItN-wa-djf\" eventType=\"valueChanged\" id=\"tFw-AQ-SyM\"/>\n                                            </connections>\n                                        </stepper>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstItem=\"YM7-vq-8vn\" firstAttribute=\"leading\" secondItem=\"Fa7-Ao-v3L\" secondAttribute=\"trailing\" constant=\"16\" id=\"1yZ-wH-OqE\"/>\n                                        <constraint firstItem=\"YM7-vq-8vn\" firstAttribute=\"centerY\" secondItem=\"udT-7d-Xzj\" secondAttribute=\"centerY\" id=\"AtZ-sJ-V0A\"/>\n                                        <constraint firstItem=\"Fa7-Ao-v3L\" firstAttribute=\"centerY\" secondItem=\"udT-7d-Xzj\" secondAttribute=\"centerY\" id=\"JqA-AW-PFg\"/>\n                                        <constraint firstItem=\"4j6-te-7NU\" firstAttribute=\"centerY\" secondItem=\"udT-7d-Xzj\" secondAttribute=\"centerY\" id=\"ZUT-zc-ZLF\"/>\n                                        <constraint firstItem=\"Fa7-Ao-v3L\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"4j6-te-7NU\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"lje-Nf-IAt\"/>\n                                        <constraint firstItem=\"YM7-vq-8vn\" firstAttribute=\"centerY\" secondItem=\"udT-7d-Xzj\" secondAttribute=\"centerY\" id=\"pXK-iA-Ur3\"/>\n                                        <constraint firstItem=\"4j6-te-7NU\" firstAttribute=\"leading\" secondItem=\"udT-7d-Xzj\" secondAttribute=\"leadingMargin\" id=\"tcp-Qd-vkJ\"/>\n                                        <constraint firstAttribute=\"trailingMargin\" secondItem=\"YM7-vq-8vn\" secondAttribute=\"trailing\" id=\"v09-GB-ZI2\"/>\n                                    </constraints>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Status Bar\" textLabel=\"RGb-hU-8nK\" style=\"IBUITableViewCellStyleDefault\" id=\"n5A-k0-80S\" userLabel=\"Status Bar\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"273\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"n5A-k0-80S\" id=\"QY8-dK-WOj\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Always Hide Status Bar\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"RGb-hU-8nK\">\n                                            <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <outlet property=\"accessoryView\" destination=\"kfK-Qx-RWv\" id=\"nGe-KW-7kV\"/>\n                                </connections>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Cursor Style\" textLabel=\"yfo-7h-YqG\" style=\"IBUITableViewCellStyleDefault\" id=\"lbs-O2-wKs\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"316.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"lbs-O2-wKs\" id=\"f7M-Tf-A2w\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Cursor Style\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"yfo-7h-YqG\">\n                                            <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <outlet property=\"accessoryView\" destination=\"9gc-hC-CxN\" id=\"kkU-wz-LwO\"/>\n                                </connections>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Blink Cursor\" textLabel=\"sL0-e1-fm7\" style=\"IBUITableViewCellStyleDefault\" id=\"4wf-k8-4BX\" userLabel=\"Blink Cursor\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"360\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"4wf-k8-4BX\" id=\"1T7-9v-vE8\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Blink Cursor\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"sL0-e1-fm7\">\n                                            <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <outlet property=\"accessoryView\" destination=\"9MJ-yl-Bep\" id=\"w1t-9n-609\"/>\n                                </connections>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Color Scheme\" textLabel=\"zJ4-1C-43Q\" style=\"IBUITableViewCellStyleDefault\" id=\"KYD-A1-zbp\" userLabel=\"Color Scheme\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"403.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"KYD-A1-zbp\" id=\"R94-P7-bvw\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Color Scheme\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"zJ4-1C-43Q\">\n                                            <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                        </prototypes>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"ItN-wa-djf\" id=\"FaS-xD-YJg\"/>\n                            <outlet property=\"delegate\" destination=\"ItN-wa-djf\" id=\"tN2-nE-bIM\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Appearance\" id=\"dzn-ZK-t3V\"/>\n                    <connections>\n                        <outlet property=\"blinkCursor\" destination=\"9MJ-yl-Bep\" id=\"nV7-iO-uEp\"/>\n                        <outlet property=\"cursorStyle\" destination=\"9gc-hC-CxN\" id=\"1rv-We-D01\"/>\n                        <outlet property=\"hideStatusBar\" destination=\"kfK-Qx-RWv\" id=\"pLq-6h-lGf\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"qup-pC-GMP\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n                <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" apportionsSegmentWidthsByContent=\"YES\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" id=\"9gc-hC-CxN\">\n                    <rect key=\"frame\" x=\"0.0\" y=\"-1\" width=\"212\" height=\"33\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                    <segments>\n                        <segment title=\"Block\"/>\n                        <segment title=\"Beam\"/>\n                        <segment title=\"Underline\"/>\n                    </segments>\n                    <connections>\n                        <action selector=\"cursorStyleChanged:\" destination=\"ItN-wa-djf\" eventType=\"valueChanged\" id=\"sjl-J8-0qA\"/>\n                    </connections>\n                </segmentedControl>\n                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" id=\"9MJ-yl-Bep\">\n                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"51\" height=\"31\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                    <connections>\n                        <action selector=\"blinkCursorChanged:\" destination=\"ItN-wa-djf\" eventType=\"valueChanged\" id=\"JMh-Pw-K6z\"/>\n                    </connections>\n                </switch>\n                <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" id=\"kfK-Qx-RWv\">\n                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"51\" height=\"31\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                    <connections>\n                        <action selector=\"hideStatusBarChanged:\" destination=\"ItN-wa-djf\" eventType=\"valueChanged\" id=\"a9g-K6-qiy\"/>\n                    </connections>\n                </switch>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1058\" y=\"-274\"/>\n        </scene>\n        <!--Font-->\n        <scene sceneID=\"qlT-r4-5Rb\">\n            <objects>\n                <tableViewController storyboardIdentifier=\"FontPicker\" title=\"Font\" id=\"aZY-tt-Itk\" customClass=\"FontPickerViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" id=\"Ad9-H3-d4a\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Font\" textLabel=\"hLA-gT-NrL\" style=\"IBUITableViewCellStyleDefault\" id=\"CzI-Ye-AQO\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"50\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"CzI-Ye-AQO\" id=\"6lJ-UP-joy\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Font\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontForContentSizeCategory=\"YES\" adjustsFontSizeToFit=\"NO\" id=\"hLA-gT-NrL\">\n                                            <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"288\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" style=\"UICTFontTextStyleBody\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                        </prototypes>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"aZY-tt-Itk\" id=\"vd7-PV-hpB\"/>\n                            <outlet property=\"delegate\" destination=\"aZY-tt-Itk\" id=\"lUL-qd-sbG\"/>\n                        </connections>\n                    </tableView>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"LW4-gI-8cW\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1766\" y=\"-275\"/>\n        </scene>\n        <!--Themes-->\n        <scene sceneID=\"fEF-OG-8Vs\">\n            <objects>\n                <tableViewController storyboardIdentifier=\"Themes\" id=\"Kph-uQ-Zpl\" customClass=\"ThemesViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"grouped\" separatorStyle=\"default\" allowsSelectionDuringEditing=\"YES\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"Mja-N2-5Xn\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"Theme\" id=\"nOp-PA-cgc\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"55.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"nOp-PA-cgc\" id=\"tW1-Cy-6C4\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                        </prototypes>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"Kph-uQ-Zpl\" id=\"iGc-AF-uLS\"/>\n                            <outlet property=\"delegate\" destination=\"Kph-uQ-Zpl\" id=\"BVM-WU-KFk\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Themes\" id=\"HIV-IH-MNM\"/>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"NP1-io-Ex0\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"2505\" y=\"-275\"/>\n        </scene>\n        <!--Theme View Controller-->\n        <scene sceneID=\"JQd-cE-pvj\">\n            <objects>\n                <tableViewController storyboardIdentifier=\"Theme\" id=\"3pK-7p-Qt4\" customClass=\"ThemeViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"grouped\" separatorStyle=\"default\" allowsSelection=\"NO\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" estimatedSectionHeaderHeight=\"-1\" sectionFooterHeight=\"18\" estimatedSectionFooterHeight=\"-1\" id=\"opY-Hr-EYZ\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"568\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"ThemeSetting\" textLabel=\"7Wo-VW-69j\" detailTextLabel=\"yhq-lV-Pc3\" style=\"IBUITableViewCellStyleValue1\" id=\"Sgf-G3-hZ4\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"55.5\" width=\"320\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"Sgf-G3-hZ4\" id=\"IaH-Ie-1r0\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Title\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"7Wo-VW-69j\">\n                                            <rect key=\"frame\" x=\"16\" y=\"12\" width=\"33\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Detail\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"yhq-lV-Pc3\">\n                                            <rect key=\"frame\" x=\"260\" y=\"12\" width=\"44\" height=\"20.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                            </tableViewCell>\n                        </prototypes>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"3pK-7p-Qt4\" id=\"zec-nC-DNc\"/>\n                            <outlet property=\"delegate\" destination=\"3pK-7p-Qt4\" id=\"On5-Yd-kEe\"/>\n                        </connections>\n                    </tableView>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"x6v-aO-0db\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"3293\" y=\"-275\"/>\n        </scene>\n        <!--About Navigation Controller-->\n        <scene sceneID=\"Vga-Xh-1SC\">\n            <objects>\n                <navigationController modalPresentationStyle=\"formSheet\" id=\"0yy-Uo-cZb\" customClass=\"AboutNavigationController\" sceneMemberID=\"viewController\">\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"q93-rv-TwM\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"20\" width=\"320\" height=\"44\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <toolbar key=\"toolbar\" opaque=\"NO\" clearsContextBeforeDrawing=\"NO\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"iud-fF-geX\">\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </toolbar>\n                    <connections>\n                        <segue destination=\"yJz-O7-jlW\" kind=\"relationship\" relationship=\"rootViewController\" id=\"xMq-rt-IoW\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"b5y-3s-wrw\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-527\" y=\"158\"/>\n        </scene>\n        <!--Roots-->\n        <scene sceneID=\"g3o-GK-9cH\">\n            <objects>\n                <viewControllerPlaceholder storyboardName=\"Roots\" id=\"gsu-Hv-LDC\" sceneMemberID=\"viewController\">\n                    <navigationItem key=\"navigationItem\" id=\"fgZ-7w-Ieb\"/>\n                </viewControllerPlaceholder>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"h2H-l2-gZ6\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"292\" y=\"641\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"Checkbox\" width=\"117\" height=\"119\"/>\n        <image name=\"Saddam Hussein\" width=\"79\" height=\"17\"/>\n        <image name=\"icon.png\" width=\"1024\" height=\"1024\"/>\n        <systemColor name=\"groupTableViewBackgroundColor\">\n            <color red=\"0.94901960784313721\" green=\"0.94901960784313721\" blue=\"0.96862745098039216\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n        <systemColor name=\"secondaryLabelColor\">\n            <color red=\"0.23529411759999999\" green=\"0.23529411759999999\" blue=\"0.26274509800000001\" alpha=\"0.59999999999999998\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n        <systemColor name=\"systemRedColor\">\n            <color red=\"1\" green=\"0.23137254900000001\" blue=\"0.18823529410000001\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "app/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21701\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_12\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"21678\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"393\" height=\"852\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "app/Base.lproj/Terminal.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21701\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_5\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"21678\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Terminal View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"TerminalViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"FQi-r8-odu\" customClass=\"TerminalView\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"852\"/>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <userDefinedRuntimeAttributes>\n                                    <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"canBecomeFirstResponder\" value=\"YES\"/>\n                                </userDefinedRuntimeAttributes>\n                                <connections>\n                                    <outlet property=\"controlKey\" destination=\"W9v-cj-FWz\" id=\"HgQ-u7-5IL\"/>\n                                    <outlet property=\"inputAccessoryView\" destination=\"GyN-ob-WFz\" id=\"rBB-FQ-HiM\"/>\n                                </connections>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"IVs-Oh-AhK\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                        <gestureRecognizers/>\n                        <constraints>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"FQi-r8-odu\" secondAttribute=\"bottom\" id=\"TjH-PI-NBO\"/>\n                            <constraint firstItem=\"FQi-r8-odu\" firstAttribute=\"top\" secondItem=\"IVs-Oh-AhK\" secondAttribute=\"top\" id=\"WI3-Ir-VlB\"/>\n                            <constraint firstItem=\"FQi-r8-odu\" firstAttribute=\"leading\" secondItem=\"IVs-Oh-AhK\" secondAttribute=\"leading\" id=\"qb1-5r-6hA\"/>\n                            <constraint firstItem=\"FQi-r8-odu\" firstAttribute=\"trailing\" secondItem=\"IVs-Oh-AhK\" secondAttribute=\"trailing\" id=\"rki-R2-KeY\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"bar\" destination=\"4pc-Ei-ihz\" id=\"iy4-s8-7yt\"/>\n                        <outlet property=\"barBottom\" destination=\"OK7-xG-JFg\" id=\"vWW-y5-fIo\"/>\n                        <outlet property=\"barButtonWidth\" destination=\"1Ax-5U-2CI\" id=\"Wlc-g8-GEs\"/>\n                        <outlet property=\"barHeight\" destination=\"3cy-zm-9Qp\" id=\"OcF-FY-cym\"/>\n                        <outlet property=\"barLeading\" destination=\"Ha9-7V-oYm\" id=\"o53-7B-f32\"/>\n                        <outlet property=\"barTop\" destination=\"gtD-Rx-Gdo\" id=\"zXm-Bb-ZyO\"/>\n                        <outlet property=\"barTrailing\" destination=\"RAD-x5-aAI\" id=\"Ele-Ty-xAK\"/>\n                        <outlet property=\"barView\" destination=\"GyN-ob-WFz\" id=\"lVV-s6-cQt\"/>\n                        <outlet property=\"bottomConstraint\" destination=\"TjH-PI-NBO\" id=\"ZSq-o1-EC4\"/>\n                        <outlet property=\"controlKey\" destination=\"W9v-cj-FWz\" id=\"c9i-VC-j7f\"/>\n                        <outlet property=\"escapeKey\" destination=\"IPm-l0-f25\" id=\"QBn-lt-GPd\"/>\n                        <outlet property=\"hideKeyboardButton\" destination=\"7qw-ie-p4d\" id=\"n5f-XX-i98\"/>\n                        <outlet property=\"infoButton\" destination=\"5dl-UV-6qh\" id=\"jaF-Xd-IYp\"/>\n                        <outlet property=\"pasteButton\" destination=\"hmm-tv-z35\" id=\"mgi-xs-C8r\"/>\n                        <outlet property=\"settingsBadge\" destination=\"QyP-z0-o0M\" id=\"B0a-GT-d21\"/>\n                        <outlet property=\"tabKey\" destination=\"cJt-oG-BnW\" id=\"YaE-o4-lKm\"/>\n                        <outlet property=\"termView\" destination=\"FQi-r8-odu\" id=\"QIa-yI-fmG\"/>\n                        <outletCollection property=\"barButtons\" destination=\"cJt-oG-BnW\" id=\"Yjx-uz-RbI\"/>\n                        <outletCollection property=\"barButtons\" destination=\"W9v-cj-FWz\" id=\"OFf-FH-2Xy\"/>\n                        <outletCollection property=\"barButtons\" destination=\"IPm-l0-f25\" id=\"dHH-gh-Tzq\"/>\n                        <outletCollection property=\"barButtons\" destination=\"VCr-TW-PIZ\" id=\"NlS-hh-ara\"/>\n                        <outletCollection property=\"barControls\" destination=\"7qw-ie-p4d\" id=\"fDV-Yg-Dec\"/>\n                        <outletCollection property=\"barControls\" destination=\"hmm-tv-z35\" id=\"EEv-5m-po5\"/>\n                        <outletCollection property=\"barControls\" destination=\"5dl-UV-6qh\" id=\"ocU-st-RdO\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n                <view contentMode=\"scaleToFill\" id=\"GyN-ob-WFz\" customClass=\"BarView\">\n                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"500\" height=\"108\"/>\n                    <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                    <subviews>\n                        <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"6\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"4pc-Ei-ihz\">\n                            <rect key=\"frame\" x=\"6\" y=\"50\" width=\"488\" height=\"50\"/>\n                            <subviews>\n                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cJt-oG-BnW\" customClass=\"BarButton\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <accessibility key=\"accessibilityConfiguration\" label=\"Tab\">\n                                        <accessibilityTraits key=\"traits\" button=\"YES\"/>\n                                    </accessibility>\n                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"20\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\" title=\"⇥\">\n                                        <color key=\"titleColor\" systemColor=\"darkTextColor\"/>\n                                    </state>\n                                    <connections>\n                                        <action selector=\"pressTab:\" destination=\"BYZ-38-t0r\" eventType=\"primaryActionTriggered\" id=\"Cbz-tT-Cre\"/>\n                                    </connections>\n                                </button>\n                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"W9v-cj-FWz\" customClass=\"BarButton\">\n                                    <rect key=\"frame\" x=\"37\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <accessibility key=\"accessibilityConfiguration\" label=\"Control\">\n                                        <accessibilityTraits key=\"traits\" button=\"YES\"/>\n                                    </accessibility>\n                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"20\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\" title=\"⌃\">\n                                        <color key=\"titleColor\" systemColor=\"darkTextColor\"/>\n                                    </state>\n                                    <userDefinedRuntimeAttributes>\n                                        <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"toggleable\" value=\"YES\"/>\n                                    </userDefinedRuntimeAttributes>\n                                    <connections>\n                                        <action selector=\"pressControl:\" destination=\"BYZ-38-t0r\" eventType=\"primaryActionTriggered\" id=\"c21-jR-m4I\"/>\n                                    </connections>\n                                </button>\n                                <button contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"IPm-l0-f25\" customClass=\"BarButton\">\n                                    <rect key=\"frame\" x=\"74\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <accessibility key=\"accessibilityConfiguration\" label=\"Escape\">\n                                        <accessibilityTraits key=\"traits\" button=\"YES\"/>\n                                    </accessibility>\n                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"24\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\" title=\"⎋\">\n                                        <color key=\"titleColor\" systemColor=\"darkTextColor\"/>\n                                    </state>\n                                    <connections>\n                                        <action selector=\"pressEscape:\" destination=\"BYZ-38-t0r\" eventType=\"primaryActionTriggered\" id=\"Z7i-gl-kzw\"/>\n                                    </connections>\n                                </button>\n                                <button contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"VCr-TW-PIZ\" userLabel=\"Arrows\" customClass=\"ArrowBarButton\">\n                                    <rect key=\"frame\" x=\"111\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"24\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\">\n                                        <color key=\"titleColor\" systemColor=\"darkTextColor\"/>\n                                    </state>\n                                    <connections>\n                                        <action selector=\"pressArrow:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"SBI-0w-nSu\"/>\n                                    </connections>\n                                </button>\n                                <view contentMode=\"scaleToFill\" horizontalHuggingPriority=\"100\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"qmg-M1-7W8\" userLabel=\"Spacer\">\n                                    <rect key=\"frame\" x=\"148\" y=\"0.0\" width=\"229\" height=\"50\"/>\n                                </view>\n                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QqP-xI-1vv\">\n                                    <rect key=\"frame\" x=\"383\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <subviews>\n                                        <button contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"infoLight\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"5dl-UV-6qh\" userLabel=\"Info\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                            <accessibility key=\"accessibilityConfiguration\" label=\"Settings\"/>\n                                            <constraints>\n                                                <constraint firstAttribute=\"width\" constant=\"31\" id=\"1Ax-5U-2CI\"/>\n                                            </constraints>\n                                            <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                            <state key=\"normal\">\n                                                <color key=\"titleColor\" systemColor=\"darkTextColor\"/>\n                                            </state>\n                                            <connections>\n                                                <action selector=\"showAbout:\" destination=\"BYZ-38-t0r\" eventType=\"primaryActionTriggered\" id=\"xbI-EN-avC\"/>\n                                                <outletCollection property=\"gestureRecognizers\" destination=\"reB-As-gYu\" appends=\"YES\" id=\"ehk-hz-UIY\"/>\n                                            </connections>\n                                        </button>\n                                        <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QyP-z0-o0M\" userLabel=\"Badge\">\n                                            <rect key=\"frame\" x=\"20\" y=\"6.6666666666666643\" width=\"12\" height=\"12\"/>\n                                            <color key=\"backgroundColor\" systemColor=\"systemRedColor\"/>\n                                            <constraints>\n                                                <constraint firstAttribute=\"width\" constant=\"12\" id=\"BA0-yo-Kjp\"/>\n                                                <constraint firstAttribute=\"height\" constant=\"12\" id=\"omS-5z-6nP\"/>\n                                            </constraints>\n                                            <userDefinedRuntimeAttributes>\n                                                <userDefinedRuntimeAttribute type=\"number\" keyPath=\"layer.cornerRadius\">\n                                                    <integer key=\"value\" value=\"6\"/>\n                                                </userDefinedRuntimeAttribute>\n                                            </userDefinedRuntimeAttributes>\n                                        </view>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstItem=\"5dl-UV-6qh\" firstAttribute=\"top\" secondItem=\"QqP-xI-1vv\" secondAttribute=\"top\" id=\"Bmc-yy-8n3\"/>\n                                        <constraint firstAttribute=\"bottom\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"bottom\" id=\"HF9-Df-jMg\"/>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"trailing\" id=\"V23-Wf-LRF\"/>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"QyP-z0-o0M\" secondAttribute=\"trailing\" constant=\"-1\" id=\"bKG-3n-DTG\"/>\n                                        <constraint firstItem=\"5dl-UV-6qh\" firstAttribute=\"leading\" secondItem=\"QqP-xI-1vv\" secondAttribute=\"leading\" id=\"cU3-kZ-7ce\"/>\n                                        <constraint firstItem=\"QyP-z0-o0M\" firstAttribute=\"centerY\" secondItem=\"QqP-xI-1vv\" secondAttribute=\"centerY\" multiplier=\"0.5\" id=\"o5C-lE-of0\"/>\n                                        <constraint firstItem=\"5dl-UV-6qh\" firstAttribute=\"width\" secondItem=\"QqP-xI-1vv\" secondAttribute=\"width\" id=\"v7m-bU-plD\"/>\n                                    </constraints>\n                                </view>\n                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hmm-tv-z35\" userLabel=\"Paste\">\n                                    <rect key=\"frame\" x=\"420\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <accessibility key=\"accessibilityConfiguration\" label=\"Paste\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\" image=\"Paste\"/>\n                                    <connections>\n                                        <action selector=\"paste:\" destination=\"dkx-z0-nzr\" eventType=\"primaryActionTriggered\" id=\"cfw-0C-OAd\"/>\n                                    </connections>\n                                </button>\n                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"7qw-ie-p4d\">\n                                    <rect key=\"frame\" x=\"457\" y=\"0.0\" width=\"31\" height=\"50\"/>\n                                    <accessibility key=\"accessibilityConfiguration\" label=\"Hide Keyboard\"/>\n                                    <color key=\"tintColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                    <state key=\"normal\" image=\"Hide Keyboard\"/>\n                                    <connections>\n                                        <action selector=\"loseFocus:\" destination=\"dkx-z0-nzr\" eventType=\"primaryActionTriggered\" id=\"T2t-j9-oFz\"/>\n                                    </connections>\n                                </button>\n                            </subviews>\n                            <constraints>\n                                <constraint firstAttribute=\"height\" constant=\"43\" id=\"3cy-zm-9Qp\"/>\n                                <constraint firstItem=\"7qw-ie-p4d\" firstAttribute=\"width\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"width\" id=\"5aW-YN-2vX\"/>\n                                <constraint firstItem=\"5dl-UV-6qh\" firstAttribute=\"width\" secondItem=\"hmm-tv-z35\" secondAttribute=\"width\" id=\"Nyk-On-Tyt\"/>\n                                <constraint firstItem=\"W9v-cj-FWz\" firstAttribute=\"width\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"width\" id=\"Xdt-z5-Zdb\"/>\n                                <constraint firstItem=\"IPm-l0-f25\" firstAttribute=\"width\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"width\" id=\"ebo-yF-ffX\"/>\n                                <constraint firstItem=\"5dl-UV-6qh\" firstAttribute=\"width\" secondItem=\"VCr-TW-PIZ\" secondAttribute=\"width\" id=\"fXl-3w-1Zj\"/>\n                                <constraint firstItem=\"cJt-oG-BnW\" firstAttribute=\"width\" secondItem=\"5dl-UV-6qh\" secondAttribute=\"width\" id=\"oQb-HX-lTG\"/>\n                            </constraints>\n                        </stackView>\n                    </subviews>\n                    <viewLayoutGuide key=\"safeArea\" id=\"hgN-L1-K1S\"/>\n                    <color key=\"tintColor\" systemColor=\"darkTextColor\"/>\n                    <constraints>\n                        <constraint firstItem=\"4pc-Ei-ihz\" firstAttribute=\"leading\" secondItem=\"hgN-L1-K1S\" secondAttribute=\"leading\" constant=\"6\" id=\"Ha9-7V-oYm\"/>\n                        <constraint firstItem=\"hgN-L1-K1S\" firstAttribute=\"bottom\" secondItem=\"4pc-Ei-ihz\" secondAttribute=\"bottom\" constant=\"6\" id=\"OK7-xG-JFg\"/>\n                        <constraint firstItem=\"hgN-L1-K1S\" firstAttribute=\"trailing\" secondItem=\"4pc-Ei-ihz\" secondAttribute=\"trailing\" constant=\"6\" id=\"RAD-x5-aAI\"/>\n                        <constraint firstItem=\"4pc-Ei-ihz\" firstAttribute=\"top\" secondItem=\"hgN-L1-K1S\" secondAttribute=\"top\" constant=\"6\" id=\"gtD-Rx-Gdo\"/>\n                    </constraints>\n                    <userDefinedRuntimeAttributes>\n                        <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"allowsSelfSizing\" value=\"YES\"/>\n                    </userDefinedRuntimeAttributes>\n                    <connections>\n                        <outlet property=\"terminalViewController\" destination=\"BYZ-38-t0r\" id=\"Pog-Oc-1za\"/>\n                    </connections>\n                </view>\n                <pongPressGestureRecognizer allowableMovement=\"20\" minimumPressDuration=\"5\" id=\"reB-As-gYu\" userLabel=\"Debug Panel Trigger\">\n                    <connections>\n                        <action selector=\"showAbout:\" destination=\"BYZ-38-t0r\" id=\"UDK-Ol-mdv\"/>\n                    </connections>\n                </pongPressGestureRecognizer>\n            </objects>\n            <point key=\"canvasLocation\" x=\"31.875\" y=\"32.74647887323944\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"Hide Keyboard\" width=\"32\" height=\"32\"/>\n        <image name=\"Paste\" width=\"20\" height=\"21\"/>\n        <systemColor name=\"darkTextColor\">\n            <color white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n        <systemColor name=\"systemRedColor\">\n            <color red=\"1\" green=\"0.23137254901960785\" blue=\"0.18823529411764706\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "app/CLI.xcconfig",
    "content": "CODE_SIGN_IDENTITY = -\nENABLE_HARDENED_RUNTIME = YES\nHEADER_SEARCH_PATHS = $(SRCROOT)\nPRODUCT_NAME = $(TARGET_NAME)\nSDKROOT = macosx\nSUPPORTED_PLATFORMS = macosx\n"
  },
  {
    "path": "app/CurrentRoot.h",
    "content": "//\n//  CurrentRoot.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/4/21.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\nextern int fs_ish_version;\nextern int fs_ish_apk_version;\n\nvoid FsInitialize(void);\nbool FsIsManaged(void);\nbool FsNeedsRepositoryUpdate(void);\nvoid FsUpdateOnlyRepositoriesFile(void);\nvoid FsUpdateRepositories(void);\n\n/// An integer representing the current major version of the apk repositories. An upgrade will be run if the number in /ish/apk-version is smaller. After a successful upgrade, the newer number is copied into /ish/apk-version.\n/// To upgrade:\n/// - update the default rootfs to the same version\n/// - update gen_apk_repositories.py to generate the new version of /etc/apk/repositories\n/// - set both of the following constants appropriately, making sure to use a larger number than the previous one\n#define CURRENT_APK_VERSION 31900\n#define CURRENT_APK_VERSION_STRING \"Alpine v3.19\"\n\nextern NSString *const FsUpdatedNotification;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/CurrentRoot.m",
    "content": "//\n//  CurrentRoot.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/4/21.\n//\n\n#import \"CurrentRoot.h\"\n#include \"kernel/calls.h\"\n#include \"fs/path.h\"\n\n#ifdef ISH_LINUX\n#import \"LinuxInterop.h\"\n#endif\n\nint fs_ish_version;\nint fs_ish_apk_version;\n\n#if !ISH_LINUX\nstatic ssize_t read_file(const char *path, char *buf, size_t size) {\n    struct fd *fd = generic_open(path, O_RDONLY_, 0);\n    if (IS_ERR(fd))\n        return PTR_ERR(fd);\n    ssize_t n = fd->ops->read(fd, buf, size);\n    fd_close(fd);\n    if (n == size)\n        return _ENAMETOOLONG;\n    return n;\n}\n\nstatic ssize_t write_file(const char *path, const char *buf, size_t size) {\n    struct fd *fd = generic_open(path, O_WRONLY_|O_CREAT_|O_TRUNC_, 0644);\n    if (IS_ERR(fd))\n        return PTR_ERR(fd);\n    ssize_t n = fd->ops->write(fd, buf, size);\n    fd_close(fd);\n    return n;\n}\nstatic int remove_directory(const char *path) {\n    return generic_rmdirat(AT_PWD, path);\n}\n#else\n#define read_file linux_read_file\n#define write_file linux_write_file\n#define remove_directory linux_remove_directory\n#endif\n\nvoid FsInitialize(void) {\n    // /ish/version is the last ish version that opened this root. Used to migrate the filesystem.\n    char buf[1000];\n    ssize_t n = read_file(\"/ish/version\", buf, sizeof(buf));\n    if (n >= 0) {\n        NSString *currentVersion = NSBundle.mainBundle.infoDictionary[(__bridge NSString *) kCFBundleVersionKey];\n        NSString *currentVersionFile = [NSString stringWithFormat:@\"%@\\n\", currentVersion];\n\n        NSString *version = [[NSString alloc] initWithBytesNoCopy:buf length:n encoding:NSUTF8StringEncoding freeWhenDone:NO];\n        version = [version stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];\n        fs_ish_version = version.intValue;\n\n        version = nil;\n\n        n = read_file(\"/ish/apk-version\", buf, sizeof(buf));\n        if (n >= 0) {\n            NSString *version = [[NSString alloc] initWithBytesNoCopy:buf length:n encoding:NSUTF8StringEncoding freeWhenDone:NO];\n            version = [version stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];\n            fs_ish_apk_version = version.intValue;\n        }\n\n        // If no newer value for CURRENT_APK_VERSION, do silent update.\n        if (fs_ish_apk_version >= CURRENT_APK_VERSION)\n            FsUpdateRepositories();\n\n        if (currentVersion.intValue > fs_ish_version) {\n            fs_ish_version = currentVersion.intValue;\n            write_file(\"/ish/version\", currentVersionFile.UTF8String, [currentVersionFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);\n        }\n    }\n}\n\nbool FsIsManaged(void) {\n    return fs_ish_version != 0;\n}\n\nbool FsNeedsRepositoryUpdate(void) {\n    return FsIsManaged() && fs_ish_apk_version < CURRENT_APK_VERSION;\n}\n\nvoid FsUpdateOnlyRepositoriesFile(void) {\n    NSURL *repositories = [NSBundle.mainBundle URLForResource:@\"repositories\" withExtension:@\"txt\"];\n    if (repositories != nil) {\n        NSMutableData *repositoriesData = [@\"# This file contains pinned repositories managed by iSH. If the /ish directory\\n\"\n                                           @\"# exists, iSH uses the metadata stored in it to keep this file up to date (by\\n\"\n                                           @\"# overwriting the contents on boot.)\\n\" dataUsingEncoding:NSUTF8StringEncoding].mutableCopy;\n        [repositoriesData appendData:[NSData dataWithContentsOfURL:repositories]];\n        write_file(\"/etc/apk/repositories\", repositoriesData.bytes, repositoriesData.length);\n    }\n}\n\nvoid FsUpdateRepositories(void) {\n    FsUpdateOnlyRepositoriesFile();\n    fs_ish_apk_version = CURRENT_APK_VERSION;\n    NSString *currentVersionFile = [NSString stringWithFormat:@\"%d\\n\", fs_ish_apk_version];\n    write_file(\"/ish/apk-version\", currentVersionFile.UTF8String, [currentVersionFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);\n    remove_directory(\"/ish/apk\");\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [NSNotificationCenter.defaultCenter postNotificationName:FsUpdatedNotification object:nil];\n    });\n}\n\nNSString *const FsUpdatedNotification = @\"FsUpdatedNotification\";\n"
  },
  {
    "path": "app/DelayedUITask.h",
    "content": "//\n//  DelayedUITask.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/8/17.\n//\n\n#import <Foundation/Foundation.h>\n\n@interface DelayedUITask : NSObject\n\n- (instancetype)initWithTarget:(id)target action:(SEL)action;\n- (void)schedule;\n\n@end\n"
  },
  {
    "path": "app/DelayedUITask.m",
    "content": "//\n//  DelayedUITask.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/8/17.\n//\n\n#import \"DelayedUITask.h\"\n\n@interface DelayedUITask ()\n\n@property id target;\n@property SEL action;\n@property NSTimer *timer;\n\n@end\n\n@implementation DelayedUITask\n\n- (instancetype)initWithTarget:(id)target action:(SEL)action {\n    if (self = [super init]) {\n        self.target = target;\n        self.action = action;\n    }\n    return self;\n}\n\n- (void)schedule {\n    if (!self.timer.valid) {\n        self.timer = [NSTimer timerWithTimeInterval:1./60 repeats:NO block:^(NSTimer * _Nonnull timer) {\n            self.timer = nil;\n            ((void (*)(id, SEL)) [self.target methodForSelector:self.action])(self.target, self.action);\n        }];\n        [NSRunLoop.mainRunLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];\n    }\n}\n\n@end\n"
  },
  {
    "path": "app/ExceptionExfiltrator.h",
    "content": "//\n//  ExceptionExfiltrator.h\n//  iSH\n//\n//  Created by Saagar Jha on 5/5/23.\n//\n\n#ifndef ExceptionExfiltrator_h\n#define ExceptionExfiltrator_h\n\n#import <Foundation/Foundation.h>\n\nvoid iSHExceptionHandler(NSException *exception);\n\n#endif /* ExceptionExfiltrator_h */\n"
  },
  {
    "path": "app/ExceptionExfiltrator.m",
    "content": "//\n//  ExceptionExfiltrator.m\n//  libiSHApp\n//\n//  Created by Saagar Jha on 5/5/23.\n//\n\n#import \"ExceptionExfiltrator.h\"\n#import <Foundation/Foundation.h>\n#import <execinfo.h>\n#import <pthread.h>\n\n#define f(name, character)         \\\n\t__asm__(\"\\\" \" name \"\\\": nop\"); \\\n\tvoid ish_exception_exfiltrate_##character(void) __asm__(\" \" name)\n\nf(\"unprintable\", unprintable);\n\nf(\" \", space);\nf(\"!\", exclamation_mark);\nf(\"quotation_mark\", quotation_mark);\nf(\"#\", number_sign);\nf(\"$\", dollar_sign);\nf(\"%\", percent_sign);\nf(\"&\", ampersand);\nf(\"'\", apostrophe);\nf(\"(\", left_parenthesis);\nf(\")\", right_parenthesis);\nf(\"*\", asterisk);\nf(\"+\", plus_sign);\nf(\",\", comma);\nf(\"-\", hyphen_minus);\nf(\".\", full_stop);\nf(\"/\", solidus);\nf(\"0\", 0);\nf(\"1\", 1);\nf(\"2\", 2);\nf(\"3\", 3);\nf(\"4\", 4);\nf(\"5\", 5);\nf(\"6\", 6);\nf(\"7\", 7);\nf(\"8\", 8);\nf(\"9\", 9);\nf(\":\", colon);\nf(\";\", semicolon);\nf(\"<\", less_than_sign);\nf(\"=\", equals_sign);\nf(\">\", greater_than_sign);\nf(\"?\", question_mark);\nf(\"@\", commercial_at);\nf(\"A\", A);\nf(\"B\", B);\nf(\"C\", C);\nf(\"D\", D);\nf(\"E\", E);\nf(\"F\", F);\nf(\"G\", G);\nf(\"H\", H);\nf(\"I\", I);\nf(\"J\", J);\nf(\"K\", K);\nf(\"L\", L);\nf(\"M\", M);\nf(\"N\", N);\nf(\"O\", O);\nf(\"P\", P);\nf(\"Q\", Q);\nf(\"R\", R);\nf(\"S\", S);\nf(\"T\", T);\nf(\"U\", U);\nf(\"V\", V);\nf(\"W\", W);\nf(\"X\", X);\nf(\"Y\", Y);\nf(\"Z\", Z);\nf(\"[\", left_square_bracket);\nf(\"reverse_solidus\", reverse_solidus);\nf(\"]\", right_square_bracket);\nf(\"^\", circumflex_accent);\nf(\"_\", low_line);\nf(\"`\", grave_accent);\nf(\"a\", a);\nf(\"b\", b);\nf(\"c\", c);\nf(\"d\", d);\nf(\"e\", e);\nf(\"f\", f);\nf(\"g\", g);\nf(\"h\", h);\nf(\"i\", i);\nf(\"j\", j);\nf(\"k\", k);\nf(\"l\", l);\nf(\"m\", m);\nf(\"n\", n);\nf(\"o\", o);\nf(\"p\", p);\nf(\"q\", q);\nf(\"r\", r);\nf(\"s\", s);\nf(\"t\", t);\nf(\"u\", u);\nf(\"v\", v);\nf(\"w\", w);\nf(\"x\", x);\nf(\"y\", y);\nf(\"z\", z);\nf(\"{\", left_curly_bracket);\nf(\"|\", vertical_line);\nf(\"}\", right_curly_bracket);\nf(\"~\", tilde);\n\n#undef f\n\n#define f(character, name) [character] = ish_exception_exfiltrate_##name\n\nstatic void (*character2function[256])(void) = {\n    f(' ', space),\n    f('!', exclamation_mark),\n    f('\"', quotation_mark),\n    f('#', number_sign),\n    f('$', dollar_sign),\n    f('%', percent_sign),\n    f('&', ampersand),\n    f('\\'', apostrophe),\n    f('(', left_parenthesis),\n    f(')', right_parenthesis),\n    f('*', asterisk),\n    f('+', plus_sign),\n    f(',', comma),\n    f('-', hyphen_minus),\n    f('.', full_stop),\n    f('/', solidus),\n    f('0', 0),\n    f('1', 1),\n    f('2', 2),\n    f('3', 3),\n    f('4', 4),\n    f('5', 5),\n    f('6', 6),\n    f('7', 7),\n    f('8', 8),\n    f('9', 9),\n    f(':', colon),\n    f(';', semicolon),\n    f('<', less_than_sign),\n    f('=', equals_sign),\n    f('>', greater_than_sign),\n    f('?', question_mark),\n    f('@', commercial_at),\n    f('A', A),\n    f('B', B),\n    f('C', C),\n    f('D', D),\n    f('E', E),\n    f('F', F),\n    f('G', G),\n    f('H', H),\n    f('I', I),\n    f('J', J),\n    f('K', K),\n    f('L', L),\n    f('M', M),\n    f('N', N),\n    f('O', O),\n    f('P', P),\n    f('Q', Q),\n    f('R', R),\n    f('S', S),\n    f('T', T),\n    f('U', U),\n    f('V', V),\n    f('W', W),\n    f('X', X),\n    f('Y', Y),\n    f('Z', Z),\n    f('[', left_square_bracket),\n    f('\\\\', reverse_solidus),\n    f(']', right_square_bracket),\n    f('^', circumflex_accent),\n    f('_', low_line),\n    f('`', grave_accent),\n    f('a', a),\n    f('b', b),\n    f('c', c),\n    f('d', d),\n    f('e', e),\n    f('f', f),\n    f('g', g),\n    f('h', h),\n    f('i', i),\n    f('j', j),\n    f('k', k),\n    f('l', l),\n    f('m', m),\n    f('n', n),\n    f('o', o),\n    f('p', p),\n    f('q', q),\n    f('r', r),\n    f('s', s),\n    f('t', t),\n    f('u', u),\n    f('v', v),\n    f('w', w),\n    f('x', x),\n    f('y', y),\n    f('z', z),\n    f('{', left_curly_bracket),\n    f('|', vertical_line),\n    f('}', right_curly_bracket),\n    f('~', tilde),\n};\n\n#undef f\n\nvoid __ish_exception_exfiltrate_NAME__(void) {\n\t__asm__(\"nop\");\n}\n\nvoid __ish_exception_exfiltrate_REASON__(void) {\n\t__asm__(\"nop\");\n}\n\nvoid __ish_exception_exfiltrate_BACKTRACE__(void) {\n\t__asm__(\"nop\");\n}\n\nstatic void (*function_for_character(unichar character))(void) {\n\treturn character < sizeof(character2function) / sizeof(*character2function) ? (character2function[character] ? character2function[character] : ish_exception_exfiltrate_unprintable) : ish_exception_exfiltrate_unprintable;\n}\n\nstatic void *address_for_function(void (*function)(void)) {\n\treturn (void *)((uintptr_t)function + 1);\n}\n\nstruct frame {\n\tvoid *next;\n\tvoid *address;\n};\n\nstatic void *__ish_exception_exfiltrate_THREAD__(void *frames) {\n\t*(void **)__builtin_frame_address(0) = frames;\n\t__builtin_trap();\n}\n\nvoid iSHExceptionHandler(NSException *exception) {\n\tNSArray<NSNumber *> *backtrace = exception.callStackReturnAddresses;\n\tNSString *name = exception.name;\n\tNSString *reason = exception.reason;\n\tsize_t size =\n\t    backtrace.count + /* backtrace frames */\n\t    1 +               /* __ish_exception_exfiltrate_BACKTRACE__ */\n\t    name.length +     /* name */\n\t    1 +               /* __ish_exception_exfiltrate__NAME__ */\n\t    reason.length +   /* reason */\n\t    1;                /* __ish_exception_exfiltrate__REASON__ */\n\tstruct frame *frames = malloc(sizeof(struct frame) * size);\n\tframes[0].next = NULL;\n\n\tfor (size_t i = 1; i < size; ++i) {\n\t\tframes[i].next = frames + i - 1;\n\t}\n\n\tsize_t index = 0;\n\n\tfor (NSNumber *address in backtrace.reverseObjectEnumerator) {\n\t\tframes[index++].address = address.pointerValue;\n\t}\n\n\tframes[index++].address = address_for_function(__ish_exception_exfiltrate_BACKTRACE__);\n\n\tfor (NSUInteger i = 0; i < reason.length; ++i, ++index) {\n\t\tframes[index].address = address_for_function(function_for_character([reason characterAtIndex:reason.length - i - 1]));\n\t}\n\n\tframes[index++].address = address_for_function(__ish_exception_exfiltrate_REASON__);\n\n\tfor (NSUInteger i = 0; i < name.length; ++i, ++index) {\n\t\tframes[index].address = address_for_function(function_for_character([name characterAtIndex:name.length - i - 1]));\n\t}\n\n\tframes[index++].address = address_for_function(__ish_exception_exfiltrate_NAME__);\n\n\tpthread_t thread;\n\tpthread_create(&thread, NULL, __ish_exception_exfiltrate_THREAD__, frames + size - 1);\n\tpthread_join(thread, NULL);\n}\n"
  },
  {
    "path": "app/FileProvider/FileProviderEnumerator.h",
    "content": "//\n//  FileProviderEnumerator.h\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import <FileProvider/FileProvider.h>\n#import \"FileProviderItem.h\"\n\n@interface FileProviderEnumerator : NSObject <NSFileProviderEnumerator>\n\n- (instancetype)init NS_UNAVAILABLE;\n- (instancetype)initWithItem:(FileProviderItem *)item;\n\n@end\n"
  },
  {
    "path": "app/FileProvider/FileProviderEnumerator.m",
    "content": "//\n//  FileProviderEnumerator.m\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import <MobileCoreServices/MobileCoreServices.h>\n#include <dirent.h>\n#import \"FileProviderExtension.h\"\n#import \"FileProviderEnumerator.h\"\n#import \"FileProviderItem.h\"\n#import \"NSError+ISHErrno.h\"\n#include \"fs/fake-db.h\"\n\n@interface FileProviderEnumerator ()\n\n@property FileProviderItem *item;\n\n@end\n\n@implementation FileProviderEnumerator\n\n- (instancetype)initWithItem:(FileProviderItem *)item {\n    if (self = [super init]) {\n        self.item = item;\n    }\n    return self;\n}\n\n- (void)enumerateItemsForObserver:(id<NSFileProviderEnumerationObserver>)observer startingAtPage:(NSFileProviderPage)page {\n    NSLog(@\"enumeration start %@\", self.item.itemIdentifier);\n    // if we're asked to enumerate the working set\n    if (self.item == nil) {\n        [observer finishEnumeratingUpToPage:page];\n        return;\n    }\n    // if we're asked to enumerate a file\n    if (![self.item.typeIdentifier isEqualToString:(NSString *) kUTTypeFolder]) {\n        NSLog(@\"not enumerating a file (%@)\", self.item.typeIdentifier);\n        [observer finishEnumeratingUpToPage:page];\n        return;\n    }\n    \n    NSError *error;\n    int fd = [self.item openNewFDWithError:&error];\n    if (fd == -1) {\n        [observer finishEnumeratingWithError:error];\n        return;\n    }\n    DIR *dir = fdopendir(fd);\n    NSMutableArray<FileProviderItem *> *items = [NSMutableArray new];\n    struct dirent *dirent;\n    errno = 0;\n    while ((dirent = readdir(dir))) {\n        if (strcmp(dirent->d_name, \".\") == 0 || strcmp(dirent->d_name, \"..\") == 0)\n            continue;\n\n        // this is annoying\n        NSString *path = _item.path;\n        NSString *childIdent;\n        if (strcmp(dirent->d_name, \"..\") == 0) {\n            childIdent = _item.parentItemIdentifier;\n        } else if (strcmp(dirent->d_name, \".\") != 0) {\n            db_begin_read(&_item.mount->db);\n            inode_t inode = path_get_inode(&_item.mount->db, [path stringByAppendingFormat:@\"/%@\", [NSString stringWithUTF8String:dirent->d_name]].fileSystemRepresentation);\n            db_commit(&_item.mount->db);\n            if (inode == 0) {\n                NSLog(@\"could not find %s in database, assuming nonexistent\", dirent->d_name);\n                continue;\n            }\n            childIdent = [NSString stringWithFormat:@\"%lu\", (unsigned long) inode];\n        }\n\n        NSLog(@\"returning %s %@\", dirent->d_name, childIdent);\n        FileProviderItem *item = [[FileProviderItem alloc] initWithIdentifier:childIdent mount:_item.mount error:&error];\n        if (item == nil) {\n            [observer finishEnumeratingWithError:error];\n            closedir(dir);\n            return;\n        }\n        [items addObject:item];\n        errno = 0;\n    }\n    if (errno != 0) {\n        NSError *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];\n        NSLog(@\"readdir returned %@\", error);\n        [observer finishEnumeratingWithError:error];\n        closedir(dir);\n        return;\n    }\n\n    closedir(dir);\n    NSLog(@\"returning %@\", items);\n    [observer didEnumerateItems:items];\n    [observer finishEnumeratingUpToPage:nil];\n}\n\n- (void)enumerateChangesForObserver:(id<NSFileProviderChangeObserver>)observer fromSyncAnchor:(NSFileProviderSyncAnchor)anchor {\n    NSLog(@\"saying no file changes\");\n    // TODO implement by having the sync anchor be a serialized list of files\n    [observer finishEnumeratingChangesUpToSyncAnchor:anchor moreComing:NO];\n}\n\n- (void)invalidate {\n}\n\n@end\n"
  },
  {
    "path": "app/FileProvider/FileProviderExtension.h",
    "content": "//\n//  FileProviderExtension.h\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import <FileProvider/FileProvider.h>\n#include \"fs/fake-db.h\"\n\nstruct fakefs_mount {\n    struct fakefs_db db;\n    int root_fd;\n    const char *source;\n};\n\n@interface FileProviderExtension : NSFileProviderExtension\n\n- (struct fakefs_mount *)mount;\n\n@end\n"
  },
  {
    "path": "app/FileProvider/FileProviderExtension.m",
    "content": "//\n//  FileProviderExtension.m\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import \"FileProviderExtension.h\"\n#import \"FileProviderItem.h\"\n#import \"FileProviderEnumerator.h\"\n#import \"NSError+ISHErrno.h\"\n#import \"../AppGroup.h\"\n#import \"../ExceptionExfiltrator.h\"\n#include \"fs/fake-db.h\"\n\n@interface FileProviderExtension () {\n    BOOL _mounted;\n    struct fakefs_mount _mount;\n};\n@property NSURL *root;\n@end\n\n@implementation FileProviderExtension\n\n- (struct fakefs_mount *)mount {\n    NSAssert(_mounted, @\"\");\n    return &_mount;\n}\n\n- (BOOL)getMount:(struct fakefs_mount **)mount error:(NSError **)error {\n    @synchronized (self) {\n        if (!_mounted) {\n            if (self.domain == nil) {\n                *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNotAuthenticated userInfo:nil];\n                return NO;\n            }\n            NSURL *container = ContainerURL();\n            NSURL *fs_dir = [[container URLByAppendingPathComponent:@\"roots\"]\n                      URLByAppendingPathComponent:self.domain.identifier];\n            _root = [fs_dir URLByAppendingPathComponent:@\"data\"];\n            _mount.source = strdup(_root.fileSystemRepresentation);\n            _mount.root_fd = open(_mount.source, O_RDONLY | O_DIRECTORY);\n            int err = fake_db_init(&_mount.db, [fs_dir URLByAppendingPathComponent:@\"meta.db\"].fileSystemRepresentation, _mount.root_fd);\n            if (err < 0) {\n                NSLog(@\"error opening root: %d\", err);\n                close(_mount.root_fd);\n                *error = [NSError errorWithISHErrno:err itemIdentifier:NSFileProviderRootContainerItemIdentifier];\n                return NO;\n            }\n            *mount = &_mount;\n            _mounted = YES;\n        }\n\n        // Run a cleanup every once in a while. The idea here is that this\n        // function gets called while the file provider is being interacted\n        // with, so this should generally get time to run at that point, but we\n        // don't want to do this when the user is not interacting with the file\n        // provider.\n        NSDate *lastCleanup = [NSUserDefaults.standardUserDefaults objectForKey:@\"LastCleanup\"];\n        lastCleanup = lastCleanup ? lastCleanup : NSDate.distantPast;\n        if ([lastCleanup timeIntervalSinceDate:NSDate.date] > 60 * 60 /* 1 hour */) {\n            [self cleanupStorage];\n        }\n        [NSUserDefaults.standardUserDefaults setObject:NSDate.date forKey:@\"LastCleanup\"];\n        \n        return YES;\n    }\n}\n\n- (NSURL *)storageURL {\n    NSURL *storage = NSFileProviderManager.defaultManager.documentStorageURL;\n    if (self.domain != nil)\n        storage = [storage URLByAppendingPathComponent:self.domain.pathRelativeToDocumentStorage isDirectory:YES];\n    return storage;\n}\n\n- (nullable NSFileProviderItem)itemForIdentifier:(NSFileProviderItemIdentifier)identifier error:(NSError * _Nullable *)error {\n    struct fakefs_mount *mount;\n    if (![self getMount:&mount error:error]) return nil;\n    NSLog(@\"item for id %@\", identifier);\n    NSError *err;\n    FileProviderItem *item = [[FileProviderItem alloc] initWithIdentifier:identifier mount:&_mount error:&err];\n    if (item == nil) {\n        if (error != nil)\n            *error = err;\n        return nil;\n    }\n    return item;\n}\n\n- (nullable NSURL *)URLForItemWithPersistentIdentifier:(NSFileProviderItemIdentifier)identifier {\n    if ([identifier isEqualToString:NSFileProviderRootContainerItemIdentifier])\n        return self.storageURL;\n    FileProviderItem *item = [self itemForIdentifier:identifier error:nil];\n    if (item == nil)\n        return nil;\n    NSURL *url = [self.storageURL URLByAppendingPathComponent:identifier isDirectory:YES];\n    url = [url URLByAppendingPathComponent:item.path.lastPathComponent isDirectory:NO];\n    NSLog(@\"url for id %@ = %@\", identifier, url);\n    return url;\n}\n\n- (nullable NSFileProviderItemIdentifier)persistentIdentifierForItemAtURL:(NSURL *)url {\n    if ([url.URLByDeletingLastPathComponent isEqual:NSFileProviderManager.defaultManager.documentStorageURL]) {\n        NSAssert([self.domain.identifier isEqualToString:url.lastPathComponent], @\"url isn't the same as our domain\");\n        return NSFileProviderRootContainerItemIdentifier;\n    }\n    NSString *identifier = url.pathComponents[url.pathComponents.count - 2];\n    if (identifier.longLongValue == 0)\n        return nil; // something must be screwed I guess\n    NSLog(@\"id for url %@ = %@\", url, identifier);\n    return identifier;\n}\n\n- (BOOL)enhanceSanityOfURL:(NSURL *)url error:(NSError **)error {\n    NSURL *dir = url.URLByDeletingLastPathComponent;\n    NSFileManager *manager = NSFileManager.defaultManager;\n    BOOL isDir;\n    if ([manager fileExistsAtPath:dir.path isDirectory:&isDir] && !isDir)\n        [manager removeItemAtURL:dir error:nil];\n    return [manager createDirectoryAtURL:dir\n             withIntermediateDirectories:YES\n                              attributes:nil\n                                   error:error];\n}\n\n- (void)providePlaceholderAtURL:(NSURL *)url completionHandler:(void (^)(NSError * _Nullable error))completionHandler {\n    NSError *err;\n    FileProviderItem *item = [self itemForIdentifier:[self persistentIdentifierForItemAtURL:url] error:&err];\n    if (item == nil) {\n        completionHandler(err);\n        return;\n    }\n    if (![self enhanceSanityOfURL:url error:&err]) {\n        completionHandler(err);\n        return;\n    }\n    if (![NSFileProviderManager writePlaceholderAtURL:[NSFileProviderManager placeholderURLForURL:url]\n                                         withMetadata:item\n                                                error:&err]) {\n        completionHandler(err);\n        return;\n    }\n    completionHandler(nil);\n}\n\n- (void)startProvidingItemAtURL:(NSURL *)url completionHandler:(void (^)(NSError *))completionHandler {\n    // Should ensure that the actual file is in the position returned by URLForItemWithIdentifier:, then call the completion handler\n    NSError *err;\n    FileProviderItem *item = [self itemForIdentifier:[self persistentIdentifierForItemAtURL:url] error:&err];\n    if (item == nil) {\n        completionHandler(err);\n        return;\n    }\n    if (![self enhanceSanityOfURL:url error:&err]) {\n        completionHandler(err);\n        return;\n    }\n    [item loadToURL:url];\n    completionHandler(nil);\n}\n\n- (void)itemChangedAtURL:(NSURL *)url {\n    FileProviderItem *item = [self itemForIdentifier:[self persistentIdentifierForItemAtURL:url] error:nil];\n    if (item == nil)\n        return;\n    [item saveFromURL:url];\n}\n\n#pragma mark - Action helpers\n\n// FIXME: not dry enough\n// It's ok to use _mount in these because in each case the caller has already invoked itemForIdentifier:error: at least once\n- (BOOL)doCreateDirectoryAt:(NSString *)path inode:(ino_t *)inode error:(NSError **)error {\n    NSURL *url = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:_mount.source]] URLByAppendingPathComponent:path];\n    db_begin_write(&_mount.db);\n    if (![NSFileManager.defaultManager createDirectoryAtURL:url\n                                withIntermediateDirectories:NO\n                                                 attributes:@{NSFilePosixPermissions: @0777}\n                                                      error:error]) {\n        db_rollback(&_mount.db);\n        return nil;\n    }\n    struct ish_stat stat;\n    NSString *parentPath = [path substringToIndex:[path rangeOfString:@\"/\" options:NSBackwardsSearch].location];\n    if (!path_read_stat(&_mount.db, parentPath.fileSystemRepresentation, &stat, NULL)) {\n        db_rollback(&_mount.db);\n        *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNoSuchItem userInfo:nil];\n        return nil;\n    }\n    stat.mode = (stat.mode & ~S_IFMT) | S_IFDIR;\n    path_create(&_mount.db, path.fileSystemRepresentation, &stat);\n    if (inode != NULL)\n        *inode = path_get_inode(&_mount.db, path.fileSystemRepresentation);\n    db_commit(&_mount.db);\n    return YES;\n}\n\n- (BOOL)doCreateFileAt:(NSString *)path importFrom:(NSURL *)importURL inode:(ino_t *)inode error:(NSError **)error {\n    NSURL *url = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:_mount.source]] URLByAppendingPathComponent:path];\n    db_begin_write(&_mount.db);\n    if (![NSFileManager.defaultManager copyItemAtURL:importURL\n                                               toURL:url\n                                               error:error]) {\n        db_rollback(&_mount.db);\n        return nil;\n    }\n    struct ish_stat stat;\n    NSString *parentPath = [path substringToIndex:[path rangeOfString:@\"/\" options:NSBackwardsSearch].location];\n    if (!path_read_stat(&_mount.db, parentPath.fileSystemRepresentation, &stat, NULL)) {\n        db_rollback(&_mount.db);\n        *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNoSuchItem userInfo:nil];\n        return nil;\n    }\n    stat.mode = (stat.mode & ~S_IFMT & ~0111) | S_IFREG;\n    path_create(&_mount.db, path.fileSystemRepresentation, &stat);\n    if (inode != NULL)\n        *inode = path_get_inode(&_mount.db, path.fileSystemRepresentation);\n    db_commit(&_mount.db);\n    return YES;\n}\n\n- (NSString *)pathOfItemWithIdentifier:(NSFileProviderItemIdentifier)identifier error:(NSError **)error {\n    FileProviderItem *parent = [self itemForIdentifier:identifier error:error];\n    if (parent == nil)\n        return nil;\n    return parent.path;\n}\n\n#pragma mark - Actions\n\n/* TODO: implement the actions for items here\n each of the actions follows the same pattern:\n - make a note of the change in the local model\n - schedule a server request as a background task to inform the server of the change\n - call the completion block with the modified item in its post-modification state\n */\n\n- (void)createDirectoryWithName:(NSString *)directoryName inParentItemIdentifier:(NSFileProviderItemIdentifier)parentItemIdentifier completionHandler:(void (^)(NSFileProviderItem _Nullable, NSError * _Nullable))completionHandler {\n    NSError *error;\n    NSString *parentPath = [self pathOfItemWithIdentifier:parentItemIdentifier error:&error];\n    if (parentPath == nil) {\n        completionHandler(nil, error);\n        return;\n    }\n    ino_t inode;\n    if (![self doCreateDirectoryAt:[parentPath stringByAppendingFormat:@\"/%@\", directoryName] inode:&inode error:&error]) {\n        completionHandler(nil, error);\n        return;\n    }\n    FileProviderItem *item = [self itemForIdentifier:[NSString stringWithFormat:@\"%lu\", (unsigned long) inode] error:&error];\n    if (item == nil)\n        completionHandler(nil, error);\n    else\n        completionHandler(item, nil);\n}\n\n- (void)importDocumentAtURL:(NSURL *)fileURL toParentItemIdentifier:(NSFileProviderItemIdentifier)parentItemIdentifier completionHandler:(void (^)(NSFileProviderItem _Nullable, NSError * _Nullable))completionHandler {\n    NSError *error;\n    NSString *parentPath = [self pathOfItemWithIdentifier:parentItemIdentifier error:&error];\n    if (parentPath == nil) {\n        completionHandler(nil, error);\n        return;\n    }\n    \n    [fileURL startAccessingSecurityScopedResource];\n    BOOL isDir;\n    assert([NSFileManager.defaultManager fileExistsAtPath:fileURL.path isDirectory:&isDir] && !isDir);\n    ino_t inode;\n    BOOL worked = [self doCreateFileAt:[parentPath stringByAppendingFormat:@\"/%@\", fileURL.lastPathComponent]\n                            importFrom:fileURL\n                                 inode:&inode\n                                 error:&error];\n    [fileURL stopAccessingSecurityScopedResource];\n    if (!worked) {\n        completionHandler(nil, error);\n        return;\n    }\n    \n    FileProviderItem *item = [self itemForIdentifier:[NSString stringWithFormat:@\"%lu\", (unsigned long) inode] error:&error];\n    if (item == nil)\n        completionHandler(nil, error);\n    else\n        completionHandler(item, nil);\n}\n\n- (NSString *)pathFromURL:(NSURL *)url {\n    NSURL *root = [NSURL fileURLWithPath:[NSString stringWithUTF8String:_mount.source]];\n    assert([url.path hasPrefix:root.path]);\n    NSString *path = [url.path substringFromIndex:root.path.length];\n    assert([path hasPrefix:@\"/\"]);\n    if ([path hasSuffix:@\"/\"])\n        path = [path substringToIndex:path.length - 1];\n    return path;\n}\n\n- (BOOL)doDelete:(NSString *)path itemIdentifier:(NSFileProviderItemIdentifier)identifier error:(NSError **)error {\n    NSURL *url = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:_mount.source]] URLByAppendingPathComponent:path];\n    NSDirectoryEnumerator<NSURL *> *enumerator = [NSFileManager.defaultManager enumeratorAtURL:url\n                                                                    includingPropertiesForKeys:nil\n                                                                                       options:NSDirectoryEnumerationSkipsSubdirectoryDescendants\n                                                                                  errorHandler:nil];\n    for (NSURL *suburl in enumerator) {\n        if (![self doDelete:[self pathFromURL:suburl] itemIdentifier:identifier error:error])\n            return NO;\n    }\n    db_begin_write(&_mount.db);\n    path_unlink(&_mount.db, path.fileSystemRepresentation);\n    int err = unlinkat(_mount.root_fd, fix_path(path.fileSystemRepresentation), 0);\n    if (err < 0)\n        err = unlinkat(_mount.root_fd, fix_path(path.fileSystemRepresentation), AT_REMOVEDIR);\n    if (err < 0) {\n        db_rollback(&_mount.db);\n        *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];\n        return NO;\n    }\n    db_commit(&_mount.db);\n    return YES;\n}\n\n- (void)deleteItemWithIdentifier:(NSFileProviderItemIdentifier)itemIdentifier completionHandler:(void (^)(NSError * _Nullable))completionHandler {\n    NSError *error;\n    NSString *path = [self pathOfItemWithIdentifier:itemIdentifier error:&error];\n    if (path == nil) {\n        completionHandler(error);\n        return;\n    }\n    if (![self doDelete:path itemIdentifier:itemIdentifier error:&error])\n        completionHandler(error);\n    else\n        completionHandler(nil);\n}\n\n- (BOOL)doRename:(NSString *)src to:(NSString *)dst itemIdentifier:(NSFileProviderItemIdentifier)identifier error:(NSError **)error {\n    db_begin_write(&_mount.db);\n    path_rename(&_mount.db, src.fileSystemRepresentation, dst.fileSystemRepresentation);\n    int err = renameat(_mount.root_fd, fix_path(src.fileSystemRepresentation), _mount.root_fd, fix_path(dst.fileSystemRepresentation));\n    if (err < 0) {\n        db_rollback(&_mount.db);\n        *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];\n        return NO;\n    }\n    db_commit(&_mount.db);\n    return YES;\n}\n\n- (void)renameItemWithIdentifier:(NSFileProviderItemIdentifier)itemIdentifier toName:(NSString *)itemName completionHandler:(void (^)(NSFileProviderItem _Nullable, NSError * _Nullable))completionHandler {\n    NSError *error;\n    FileProviderItem *item = [self itemForIdentifier:itemIdentifier error:&error];\n    if (item == nil) {\n        completionHandler(nil, error);\n        return;\n    }\n    NSString *dstPath = [item.path.stringByDeletingLastPathComponent stringByAppendingPathComponent:itemName];\n    if (![self doRename:item.path to:dstPath itemIdentifier:itemIdentifier error:&error]) {\n        completionHandler(nil, error);\n        return;\n    }\n    completionHandler(item, nil);\n}\n\n- (void)reparentItemWithIdentifier:(NSFileProviderItemIdentifier)itemIdentifier toParentItemWithIdentifier:(NSFileProviderItemIdentifier)parentItemIdentifier newName:(NSString *)newName completionHandler:(void (^)(NSFileProviderItem _Nullable, NSError * _Nullable))completionHandler {\n    NSError *error;\n    FileProviderItem *item = [self itemForIdentifier:itemIdentifier error:&error];\n    if (item == nil) {\n        completionHandler(nil, error);\n        return;\n    }\n    FileProviderItem *parent = [self itemForIdentifier:parentItemIdentifier error:&error];\n    if (parent == nil) {\n        completionHandler(nil, error);\n        return;\n    }\n    if (newName == nil)\n        newName = item.path.lastPathComponent;\n    if (![self doRename:item.path to:[parent.path stringByAppendingPathComponent:newName] itemIdentifier:itemIdentifier error:&error]) {\n        completionHandler(nil, error);\n        return;\n    }\n    completionHandler(item, nil);\n}\n\n#pragma mark - Enumeration\n\n- (nullable id<NSFileProviderEnumerator>)enumeratorForContainerItemIdentifier:(NSFileProviderItemIdentifier)containerItemIdentifier error:(NSError **)error {\n    FileProviderItem *item = [self itemForIdentifier:containerItemIdentifier error:error];\n    if (item == nil)\n        return nil;\n    return [[FileProviderEnumerator alloc] initWithItem:item];\n}\n\n- (void)dealloc {\n    if (_mounted) {\n        free((void *) _mount.source);\n        close(_mount.root_fd);\n        fake_db_deinit(&_mount.db);\n    }\n}\n\n#pragma mark - Storage deletion\n\n// According to an engineer I talked to at WWDC, -stopProvidingItemAtURL: is never ever called, so that can't be used to free up disk space.\n// Solution for now is to periodically look for and delete files in file provider storage where the original is missing.\n// TODO: Delete files in file provider storage when the original file is deleted\n// TODO: Create hardlinks into file provider storage instead of copies\n//\n- (void)cleanupStorage {\n    NSAssert(_mounted, @\"Mount should exist by this point\");\n\n    NSFileManager *manager = NSFileManager.defaultManager;\n    NSArray<NSURL *> *storageDirs = [manager contentsOfDirectoryAtURL:self.storageURL includingPropertiesForKeys:nil options:0 error:nil];\n    for (NSURL *dir in storageDirs) {\n        inode_t inode = dir.lastPathComponent.longLongValue;\n        if (inode == 0)\n            continue;\n\n        // TODO: make this a function in fake-db.c\n        db_begin_read(&_mount.db);\n        sqlite3_bind_int64(_mount.db.stmt.inode_read_stat, 1, inode);\n        BOOL exists = db_exec(&_mount.db, _mount.db.stmt.inode_read_stat);\n        db_reset(&_mount.db, _mount.db.stmt.inode_read_stat);\n        db_rollback(&_mount.db);\n\n        if (!exists) {\n            NSLog(@\"removing dead inode %llu\", inode);\n            NSError *err;\n            if (![manager removeItemAtURL:dir error:&err])\n                NSLog(@\"failed to remove dead inode: %@\", err);\n        }\n    }\n}\n\n// Dead code, leaving it here just in case\n- (void)stopProvidingItemAtURL:(NSURL *)url {\n    FileProviderItem *item = [self itemForIdentifier:[self persistentIdentifierForItemAtURL:url] error:nil];\n    if (item == nil)\n        return;\n    [item saveFromURL:url];\n    [[NSFileManager defaultManager] removeItemAtURL:url error:nil];\n    [NSFileProviderManager writePlaceholderAtURL:[NSFileProviderManager placeholderURLForURL:url]\n                                    withMetadata:item\n                                           error:nil];\n}\n\n+ (void)load {\n    NSSetUncaughtExceptionHandler(iSHExceptionHandler);\n}\n\n@end\n\nvoid die(const char *msg, ...) {\n    va_list args;\n    va_start(args, msg);\n    [NSException raise:@\"ish die\" format:[NSString stringWithUTF8String:msg] arguments:args];\n    abort();\n    va_end(args);\n}\n\nvoid ish_printk(const char *msg, ...) {\n    va_list args;\n    va_start(args, msg);\n    NSLogv([NSString stringWithUTF8String:msg], args);\n    va_end(args);\n}\n"
  },
  {
    "path": "app/FileProvider/FileProviderItem.h",
    "content": "//\n//  FileProviderItem.h\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import <FileProvider/FileProvider.h>\n#include \"fs/fake-db.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface FileProviderItem : NSObject <NSFileProviderItem>\n\n- (instancetype)initWithIdentifier:(NSFileProviderItemIdentifier)identifier mount:(struct fakefs_mount *)mount error:(NSError *_Nullable *)err;\n- (void)loadToURL:(NSURL *)url;\n- (void)saveFromURL:(NSURL *)url;\n- (int)openNewFDWithError:(NSError *_Nullable *)err;\n\n@property (readonly) NSString *path;\n@property (readonly) struct fakefs_mount *mount;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/FileProvider/FileProviderItem.m",
    "content": "//\n//  FileProviderItem.m\n//  iSHFiles\n//\n//  Created by Theodore Dubois on 9/20/18.\n//\n\n#import <MobileCoreServices/MobileCoreServices.h>\n#include <sys/stat.h>\n#include <dirent.h>\n#import \"FileProviderExtension.h\"\n#import \"FileProviderItem.h\"\n#include \"fs/fake-db.h\"\n#include \"kernel/errno.h\"\n\n@interface FileProviderItem ()\n\n@property (readonly) NSFileProviderItemIdentifier identifier;\n@property (readonly) int fd;\n@property (readonly) BOOL isRoot;\n\n@end\n\n@implementation FileProviderItem\n\n- (instancetype)initWithIdentifier:(NSFileProviderItemIdentifier)identifier mount:(struct fakefs_mount *)mount error:(NSError *__autoreleasing  _Nullable *)error {\n    if (self = [super init]) {\n        _identifier = identifier;\n        _mount = mount;\n        _fd = [self openNewFDWithError:error];\n        if (_fd == -1)\n            return nil;\n    }\n    return self;\n}\n\n- (BOOL)isRoot {\n    return [self.identifier isEqualToString:NSFileProviderRootContainerItemIdentifier];\n}\n\n- (int)openNewFDWithError:(NSError *__autoreleasing  _Nullable *)error {\n    int fd = -1;\n    if (self.isRoot) {\n        fd = open(_mount->source, O_DIRECTORY | O_RDONLY);\n    } else {\n        db_begin_read(&_mount->db);\n        sqlite3_stmt *stmt = _mount->db.stmt.path_from_inode;\n        sqlite3_bind_int64(_mount->db.stmt.path_from_inode, 1, _identifier.longLongValue);\n        while (db_exec(&_mount->db, stmt)) {\n            const char *path = (const char *) sqlite3_column_text(stmt, 0);\n            fd = openat(_mount->root_fd, fix_path(path), O_RDWR);\n            if (fd == -1 && errno == EISDIR)\n                fd = openat(_mount->root_fd, fix_path(path), O_RDONLY);\n            if (fd == -1 && errno != ENOENT)\n                break;\n        }\n        db_reset(&_mount->db, stmt);\n        db_commit(&_mount->db);\n    }\n    if (fd == -1) {\n        if (error != nil) {\n            if (errno == ENOENT)\n                *error = [NSError fileProviderErrorForNonExistentItemWithIdentifier:_identifier];\n            else\n                *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];\n        }\n        NSLog(@\"opening %@ failed: %@\", self.identifier, *error);\n        return -1;\n    }\n    return fd;\n}\n\n- (NSString *)path {\n    char path[PATH_MAX] = \"\";\n    int err = fcntl(_fd, F_GETPATH, path);\n    [self handleError:err inFunction:@\"getpath\"];\n    const char *myPath = path + strlen(_mount->source);\n    return [NSFileManager.defaultManager stringWithFileSystemRepresentation:myPath length:strlen(myPath)];\n}\n\n- (NSURL *)URL {\n    NSURL *rootURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:_mount->source]];\n    if (self.isRoot)\n        return rootURL;\n    return [rootURL URLByAppendingPathComponent:self.path];\n}\n\n- (struct ish_stat)ishStat {\n    struct ish_stat stat = {};\n    db_begin_read(&_mount->db);\n    inode_t inode = _identifier.longLongValue;\n    if ([_identifier isEqualToString:NSFileProviderRootContainerItemIdentifier])\n        inode = path_get_inode(&_mount->db, \"\");\n    inode_read_stat_or_die(&_mount->db, inode, &stat);\n    db_commit(&_mount->db);\n    return stat;\n}\n- (struct stat)realStat {\n    struct stat statbuf;\n    int err = fstat(_fd, &statbuf);\n    [self handleError:err inFunction:@\"realStat\"];\n    return statbuf;\n}\n\n- (NSFileProviderItemIdentifier)itemIdentifier {\n    if (self.isRoot)\n        return NSFileProviderRootContainerItemIdentifier;\n    return _identifier;\n}\n- (NSFileProviderItemIdentifier)parentItemIdentifier {\n    if (self.isRoot) {\n        NSLog(@\"parent of root %@ is %@\", self.path, NSFileProviderRootContainerItemIdentifier);\n        return NSFileProviderRootContainerItemIdentifier;\n    }\n    NSString *parentPath = self.path.stringByDeletingLastPathComponent;\n    if ([parentPath isEqualToString:@\"/\"])\n        return NSFileProviderRootContainerItemIdentifier;\n    db_begin_read(&_mount->db);\n    inode_t parentInode = path_get_inode(&_mount->db, parentPath.UTF8String);\n    db_commit(&_mount->db);\n    assert(parentInode != 0);\n    NSString *parent = [NSString stringWithFormat:@\"%lu\", (unsigned long) parentInode];\n    NSLog(@\"parent of %@ is %@\", self.path, parent);\n    return parent;\n}\n\n- (NSFileProviderItemCapabilities)capabilities {\n    NSFileProviderItemCapabilities caps = NSFileProviderItemCapabilitiesAllowsDeleting | NSFileProviderItemCapabilitiesAllowsRenaming | NSFileProviderItemCapabilitiesAllowsReparenting;\n    if (S_ISREG(self.ishStat.mode))\n        caps |= NSFileProviderItemCapabilitiesAllowsReading | NSFileProviderItemCapabilitiesAllowsWriting;\n    else if (S_ISDIR(self.ishStat.mode))\n        caps |= NSFileProviderItemCapabilitiesAllowsAddingSubItems | NSFileProviderItemCapabilitiesAllowsContentEnumerating;\n    else\n        return 0;\n    return caps;\n}\n\n- (NSString *)filename {\n    NSString *filename = self.path.lastPathComponent;\n    if ([filename isEqualToString:@\"\"])\n        filename = @\"/\";\n    NSLog(@\"filename %@\", filename);\n    return filename;\n}\n\n- (NSNumber *)documentSize {\n    struct stat statbuf;\n    int err = fstat(_fd, &statbuf);\n    [self handleError:err inFunction:@\"documentSize\"];\n    return [NSNumber numberWithUnsignedLongLong:statbuf.st_size];\n}\n\n- (NSNumber *)childItemCount {\n    if (!S_ISDIR(self.ishStat.mode))\n        return @0;\n    int fd = [self openNewFDWithError:nil];\n    if (fd == -1)\n        return @0;\n    unsigned n = 0;\n    DIR *dir = fdopendir(fd);\n    struct dirent *dirent;\n    while ((dirent = readdir(dir))) {\n        if (strcmp(dirent->d_name, \".\") == 0 || strcmp(dirent->d_name, \"..\") == 0)\n            continue;\n        n++;\n    }\n    closedir(dir);\n    return @(n);\n}\n\n- (NSDate *)contentModificationDate {\n    struct stat statbuf = self.realStat;\n    return [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtimespec.tv_sec + (NSTimeInterval) statbuf.st_mtimespec.tv_nsec / 1000000000];\n}\n\n- (NSString *)typeIdentifier {\n    if (self.isRoot) {\n        NSLog(@\"uti of %@ is %@\", self.path, (NSString *) kUTTypeFolder);\n        return (NSString *) kUTTypeFolder;\n    }\n    mode_t_ mode = self.ishStat.mode;\n    if ((mode & S_IFMT) == S_IFDIR)\n        return (NSString *) kUTTypeFolder;\n    if ((mode & S_IFMT) == S_IFLNK)\n        return (NSString *) kUTTypeSymLink;\n    NSString *uti = CFBridgingRelease(UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,\n                                                                            (__bridge CFStringRef _Nonnull) self.path.pathExtension, nil));\n    if ([uti hasPrefix:@\"dyn.\"])\n        uti = (NSString *) kUTTypePlainText;\n    NSLog(@\"uti of %@ is %@\", self.path, uti);\n    return uti;\n}\n\n// locking on these keeps the remove/copy operation atomic\n// or at least tries to\n\n- (void)loadToURL:(NSURL *)url {\n    NSLog(@\"copying %@ to %@\", self.path, url);\n    NSURL *itemURL = self.URL;\n    NSError *err;\n    sqlite3_mutex_enter(_mount->db.lock);\n    [NSFileManager.defaultManager removeItemAtURL:url error:nil];\n    BOOL success = [NSFileManager.defaultManager copyItemAtURL:itemURL\n                                                         toURL:url\n                                                         error:&err];\n    sqlite3_mutex_leave(_mount->db.lock);\n    if (!success) {\n        NSLog(@\"error copying to %@: %@\", url, err);\n    }\n}\n\n- (void)saveFromURL:(NSURL *)url {\n    NSLog(@\"copying %@ from %@\", self.path, url);\n    NSURL *itemURL = self.URL;\n    NSError *err;\n    sqlite3_mutex_enter(_mount->db.lock);\n    [NSFileManager.defaultManager removeItemAtURL:itemURL error:nil];\n    BOOL success = [NSFileManager.defaultManager copyItemAtURL:url\n                                                         toURL:itemURL\n                                                         error:&err];\n    sqlite3_mutex_leave(_mount->db.lock);\n    if (!success) {\n        NSLog(@\"error copying to %@: %@\", url, err);\n    }\n}\n\n- (void)dealloc {\n    if (self.fd != -1)\n        close(self.fd);\n}\n\n- (void)handleError:(long)err inFunction:(NSString *)func {\n    if (err < 0) {\n        [NSException raise:NSGenericException format:@\"%@ returned %ld %d\", func, err, errno];\n    }\n}\n\n@end\n"
  },
  {
    "path": "app/FileProvider/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>iSH</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>XPC!</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSExtension</key>\n\t<dict>\n\t\t<key>NSExtensionFileProviderDocumentGroup</key>\n\t\t<string>$(PRODUCT_APP_GROUP_IDENTIFIER)</string>\n\t\t<key>NSExtensionFileProviderSupportsEnumeration</key>\n\t\t<true/>\n\t\t<key>NSExtensionPointIdentifier</key>\n\t\t<string>com.apple.fileprovider-nonui</string>\n\t\t<key>NSExtensionPrincipalClass</key>\n\t\t<string>FileProviderExtension</string>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/FileProvider/NSError+ISHErrno.h",
    "content": "//\n//  NSError+ISHErrno.h\n//  iSH\n//\n//  Created by Theodore Dubois on 12/15/18.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface NSError (ISHErrno)\n\n+ (NSError *)errorWithISHErrno:(long)err itemIdentifier:(NSFileProviderItemIdentifier)identifier;\n\n@end\n\nextern NSString *const ISHErrnoDomain;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/FileProvider/NSError+ISHErrno.m",
    "content": "//\n//  NSError+ISHErrno.m\n//  iSH\n//\n//  Created by Theodore Dubois on 12/15/18.\n//\n\n#import <FileProvider/FileProvider.h>\n#import \"NSError+ISHErrno.h\"\n#include \"kernel/errno.h\"\n\n@implementation NSError (ISHErrno)\n\n+ (NSError *)errorWithISHErrno:(long)err itemIdentifier:(nonnull NSFileProviderItemIdentifier)identifier {\n    switch (err) {\n        case _ENOENT:\n            return [NSError fileProviderErrorForNonExistentItemWithIdentifier:identifier];\n    }\n    return [NSError errorWithDomain:ISHErrnoDomain\n                               code:err\n                           userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@\"error code %ld\", err]}];\n}\n\n@end\n\nNSString *const ISHErrnoDomain = @\"ISHErrnoDomain\";\n"
  },
  {
    "path": "app/FileProvider/iSHFileProvider.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>$(PRODUCT_APP_GROUP_IDENTIFIER)</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/FontPickerViewController.h",
    "content": "//\n//  FontPickerViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/26/19.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\nNS_CLASS_DEPRECATED_IOS(10_0, 12_0, \"UIFontPickerViewController is better\")\n@interface FontPickerViewController : UITableViewController\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/FontPickerViewController.m",
    "content": "//\n//  FontPickerViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/26/19.\n//\n\n#import \"FontPickerViewController.h\"\n#import \"UserPreferences.h\"\n\n@interface FontPickerViewController ()\n\n@property NSArray<NSString *> *fontFamilies;\n\n@end\n\n@implementation FontPickerViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    NSMutableArray *families = [NSMutableArray new];\n    for (NSString *family in UIFont.familyNames) {\n        UIFont *font = [UIFont fontWithName:family size:1];\n        if (font.fontDescriptor.symbolicTraits & UIFontDescriptorTraitMonoSpace) {\n            [families addObject:family];\n        }\n    }\n    self.fontFamilies = families;\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    return self.fontFamilies.count;\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@\"Font\"];\n    NSString *family = self.fontFamilies[indexPath.row];\n    UIFont *font = [UIFont fontWithName:family size:18];\n    cell.textLabel.font = [[UIFontMetrics metricsForTextStyle:UIFontTextStyleBody] scaledFontForFont:font];\n    cell.textLabel.adjustsFontForContentSizeCategory = YES;\n    cell.textLabel.text = family;\n    if ([family isEqualToString:UserPreferences.shared.fontFamily])\n        cell.accessoryType = UITableViewCellAccessoryCheckmark;\n    return cell;\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\n    UserPreferences.shared.fontFamily = self.fontFamilies[indexPath.row];\n    [self.navigationController popViewControllerAnimated:YES];\n}\n\n@end\n"
  },
  {
    "path": "app/IOSCalls.m",
    "content": "//\n//  IOSCalls.m\n//  libiSHApp\n//\n//  Created by Theodore Dubois on 8/15/21.\n//\n\n#if ISH_LINUX\n\n#include <UIKit/UIKit.h>\n#include <pthread.h>\n#include \"Roots.h\"\n#include \"LinuxInterop.h\"\n\nvoid async_do_in_ios(void (^block)(void)) {\n    dispatch_async(dispatch_get_main_queue(), block);\n}\n\nvoid ConsoleLog(const char *data, unsigned len) {\n    NSLog(@\"%.*s\", len, data);\n}\n\nnsobj_t objc_get(nsobj_t object) {\n    CFBridgingRetain((__bridge id) object);\n    return object;\n}\n\nvoid objc_put(nsobj_t object) {\n    CFBridgingRelease(object);\n}\n\nvoid sync_do_in_workqueue(void (^block)(void (^done)(void))) {\n    __block pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n    __block pthread_cond_t cond = PTHREAD_COND_INITIALIZER;\n    __block bool flag = false;\n    async_do_in_workqueue(^{\n        block(^{\n            pthread_mutex_lock(&mutex);\n            flag = true;\n            pthread_mutex_unlock(&mutex);\n            pthread_cond_broadcast(&cond);\n        });\n    });\n    pthread_mutex_lock(&mutex);\n    while (!flag)\n        pthread_cond_wait(&cond, &mutex);\n    pthread_mutex_unlock(&mutex);\n}\n\nlong UIPasteboard_changeCount(void) {\n    return UIPasteboard.generalPasteboard.changeCount;\n}\nnsobj_t UIPasteboard_get(void) {\n    return (__bridge nsobj_t) [UIPasteboard.generalPasteboard.string dataUsingEncoding:NSUTF8StringEncoding];\n}\nvoid UIPasteboard_set(const char *data, size_t len) {\n    UIPasteboard.generalPasteboard.string = [[NSString alloc] initWithBytes:data length:len encoding:NSUTF8StringEncoding];\n}\nsize_t NSData_length(nsobj_t data) {\n    return [(__bridge NSData *) data length];\n}\nconst void *NSData_bytes(nsobj_t data) {\n    return [(__bridge NSData *) data bytes];\n}\n\n#endif\n"
  },
  {
    "path": "app/Icons/Icons.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key></key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://mastodon.social/@tbodt</string>\n\t\t<key>author</key>\n\t\t<string>@tbodt</string>\n\t\t<key>description</key>\n\t\t<string>Default</string>\n\t</dict>\n\t<key>ihash1</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-562960935</string>\n\t\t<key>author</key>\n\t\t<string>@01010101lzy</string>\n\t\t<key>description</key>\n\t\t<string>i#</string>\n\t</dict>\n\t<key>uninspired</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-562906800</string>\n\t\t<key>author</key>\n\t\t<string>@saagarjha</string>\n\t\t<key>description</key>\n\t\t<string>uninspired</string>\n\t</dict>\n\t<key>3d</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-567060974</string>\n\t\t<key>author</key>\n\t\t<string>@ricardohnn</string>\n\t\t<key>description</key>\n\t\t<string>3D</string>\n\t</dict>\n\t<key>icon1337</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-675671406</string>\n\t\t<key>author</key>\n\t\t<string>@AudioBra4n</string>\n\t\t<key>description</key>\n\t\t<string>1337</string>\n\t</dict>\n\t<key>pydann2</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-562897067</string>\n\t\t<key>author</key>\n\t\t<string>@PyDann</string>\n\t\t<key>description</key>\n\t\t<string>&gt;| Light</string>\n\t</dict>\n\t<key>pydann1</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-562897067</string>\n\t\t<key>author</key>\n\t\t<string>@PyDann</string>\n\t\t<key>description</key>\n\t\t<string>&gt;| Dark</string>\n\t</dict>\n\t<key>colontildehash</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-566759722</string>\n\t\t<key>author</key>\n\t\t<string>@TechUpdateGuy</string>\n\t\t<key>description</key>\n\t\t<string>:~#</string>\n\t</dict>\n\t<key>idollarhash</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-566855431</string>\n\t\t<key>author</key>\n\t\t<string>@TechUpdateGuy</string>\n\t\t<key>description</key>\n\t\t<string>i$#</string>\n\t</dict>\n\t<key>iinhash</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-576352583</string>\n\t\t<key>author</key>\n\t\t<string>@relikd</string>\n\t\t<key>description</key>\n\t\t<string>i in #</string>\n\t</dict>\n\t<key>metal</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-676845860</string>\n\t\t<key>author</key>\n\t\t<string>@heronwr</string>\n\t\t<key>description</key>\n\t\t<string>iS̈H</string>\n\t</dict>\n\t<key>notsurewhatthisis</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-725585045</string>\n\t\t<key>author</key>\n\t\t<string>@wack-inc</string>\n\t\t<key>description</key>\n\t\t<string>Not sure what this is</string>\n\t</dict>\n\t<key>reworked</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-717395400</string>\n\t\t<key>author</key>\n\t\t<string>@peterlewis</string>\n\t\t<key>description</key>\n\t\t<string>iSH: Reworked</string>\n\t</dict>\n\t<key>rgb</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-713075148</string>\n\t\t<key>author</key>\n\t\t<string>@FelixTornqvist</string>\n\t\t<key>description</key>\n\t\t<string>RGB</string>\n\t</dict>\n\t<key>sprite64</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-716465657</string>\n\t\t<key>author</key>\n\t\t<string>@peterlewis</string>\n\t\t<key>description</key>\n\t\t<string>Sprite 64</string>\n\t</dict>\n\t<key>is</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-828967195</string>\n\t\t<key>author</key>\n\t\t<string>@MelinaCodesInMinecraft</string>\n\t\t<key>description</key>\n\t\t<string>iS</string>\n\t</dict>\n\t<key>circular</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-716465657</string>\n\t\t<key>author</key>\n\t\t<string>@haohailong</string>\n\t\t<key>description</key>\n\t\t<string>Circular</string>\n\t</dict>\n\t<key>dollarblock1</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-968200645</string>\n\t\t<key>author</key>\n\t\t<string>@omduggineni</string>\n\t\t<key>description</key>\n\t\t<string>$+block light</string>\n\t</dict>\n\t<key>dollarblock2</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-968201479</string>\n\t\t<key>author</key>\n\t\t<string>@omduggineni</string>\n\t\t<key>description</key>\n\t\t<string>$+block dark</string>\n\t</dict>\n\t<key>freeiosterminal</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-971898997</string>\n\t\t<key>author</key>\n\t\t<string>@huxl3</string>\n\t\t<key>description</key>\n\t\t<string>iOS iSH Free Ios Terminal</string>\n\t</dict>\n\t<key>ishcolontildehash</key>\n\t<dict>\n\t\t<key>link</key>\n\t\t<string>https://github.com/ish-app/ish/issues/578#issuecomment-1038424724</string>\n\t\t<key>author</key>\n\t\t<string>@moontr3</string>\n\t\t<key>description</key>\n\t\t<string>iSH:~# dark</string>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSLocalNetworkUsageDescription</key>\n\t<string>This is required for connecting to localhost and using the ping command.</string>\n\t<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>\n\t<string>Programs running in iSH will be allowed to track your location in the background.</string>\n\t<key>NSLocationAlwaysUsageDescription</key>\n\t<string>Programs running in iSH will be allowed to track your location in the background.</string>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string>Programs running in iSH will be allowed to see your location.</string>\n\t<key>NSUserActivityTypes</key>\n\t<array>\n\t\t<string>app.ish.scene</string>\n\t</array>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<true/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneClassName</key>\n\t\t\t\t\t<string>UIWindowScene</string>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Main App</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>SceneDelegate</string>\n\t\t\t\t\t<key>UISceneStoryboardFile</key>\n\t\t\t\t\t<string>Terminal</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>location</string>\n\t</array>\n\t<key>UIFileSharingEnabled</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Terminal</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>fuc</key>\n\t<string>ICON_STUFF</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/Linux.xcconfig",
    "content": "ISH_KERNEL = linux\nNINJA_TARGETS = deps/liblinux.a libfakefs.a libish_emu.a\nLINUX_HOSTCC = env -u SDKROOT -u IPHONEOS_DEPLOYMENT_TARGET xcrun clang\nGCC_PREPROCESSOR_DEFINITIONS = ISH_LINUX=1\nLINUX_APP_LDFLAGS = -Wl,-ld_classic -sectalign __DATA __percpu_first 1000 -sectalign __DATA __tracepoints 20 -force_load $(BUILT_PRODUCTS_DIR)/liblinux.a -force_load $(BUILT_PRODUCTS_DIR)/libiSHLinux.a\n"
  },
  {
    "path": "app/LinuxInterop.c",
    "content": "//\n//  LinuxInterop.c\n//  iSH\n//\n//  Created by Theodore Dubois on 7/3/21.\n//\n\n#include \"LinuxInterop.h\"\n#include <Block.h>\n#include <linux/start_kernel.h>\n#include <linux/slab.h>\n#include <linux/kernel.h>\n#include <linux/notifier.h>\n#include <linux/termios.h>\n#include <linux/string.h>\n#include <linux/completion.h>\n#include <linux/interrupt.h>\n#include <linux/file.h>\n#include <linux/umh.h>\n#include <linux/syscalls.h>\n#include <linux/utsname.h>\n#include <linux/panic_notifier.h>\n#include <linux/init_syscalls.h>\n#include <asm/irq.h>\n#include <user/fs.h>\n#include <user/irq.h>\n\nextern void run_kernel(void);\n\nvoid actuate_kernel(const char *cmdline) {\n    strcpy(boot_command_line, cmdline);\n    run_kernel();\n}\n\nstatic int panic_report(struct notifier_block *nb, unsigned long action, void *data) {\n    const char *message = data;\n    async_do_in_ios(^{\n        ReportPanic(message);\n    });\n    return 0;\n}\n\nstatic struct notifier_block panic_report_block = {\n    .notifier_call = panic_report,\n    .priority = INT_MAX,\n};\nstatic int __init panic_report_init(void) {\n    atomic_notifier_chain_register(&panic_notifier_list, &panic_report_block);\n    return 0;\n}\ncore_initcall(panic_report_init);\n\nstatic int block_request_read;\nstatic int block_request_write;\nstatic irqreturn_t call_block_irq(int irq, void *dev) {\n    void (^block)(void);\n    for (;;) {\n        int err = host_read(block_request_read, &block, sizeof(block));\n        if (err <= 0)\n            break;\n        block();\n        Block_release(block);\n    }\n    return IRQ_HANDLED;\n}\n\nvoid async_do_in_irq(void (^block)(void)) {\n    block = Block_copy(block);\n    int err = host_write(block_request_write, &block, sizeof(block));\n    if (err < 0)\n        __builtin_trap();\n    trigger_irq(CALL_BLOCK_IRQ);\n}\n\nstruct ios_work {\n    void (^block)(void);\n    struct work_struct work;\n};\n\nstatic void do_ios_work(struct work_struct *work) {\n    struct ios_work *ios_work = container_of(work, struct ios_work, work);\n    ios_work->block();\n    Block_release(ios_work->block);\n    kfree(ios_work);\n}\n\nvoid async_do_in_workqueue(void (^block)(void)) {\n    async_do_in_irq(^{\n        struct ios_work *work = kzalloc(sizeof(*work), GFP_ATOMIC);\n        work->block = Block_copy(block);\n        INIT_WORK(&work->work, do_ios_work);\n        schedule_work(&work->work);\n    });\n}\n\nstatic int __init call_block_init(void) {\n    int err = host_pipe(&block_request_read, &block_request_write);\n    if (err < 0)\n        return err;\n    err = fd_set_nonblock(block_request_read);\n    if (err < 0)\n        return err;\n    err = request_irq(CALL_BLOCK_IRQ, call_block_irq, 0, \"block\", NULL);\n    if (err < 0)\n        return err;\n    return 0;\n}\nsubsys_initcall(call_block_init);\n\nstruct ish_session {\n    struct file *tty;\n    nsobj_t terminal;\n    int pid;\n    StartSessionDoneBlock callback;\n};\n\nstatic int session_init(struct subprocess_info *info, struct cred *cred) {\n    struct ish_session *session = info->data;\n    int err = ksys_setsid();\n    if (err < 0)\n        return err;\n    err = vfs_ioctl(session->tty, TIOCSCTTY, 0);\n    if (err < 0)\n        return err;\n    for (int fd = 0; fd <= 2; fd++) {\n        int err = replace_fd(fd, session->tty, 0);\n        if (err < 0)\n            return err;\n    }\n    session->pid = task_pid_nr(current);\n    return 0;\n}\n\nstatic void session_cleanup(struct subprocess_info *info) {\n    struct ish_session *session = info->data;\n    if (session->pid != 0 || info->retval != 0)\n        session->callback(info->retval, session->pid, objc_get(session->terminal));\n    else; // otherwise, there was a synchronous failure, returned directly from call_usermodehelper_exec\n    if (session->tty != NULL)\n        fput(session->tty);\n    objc_put(session->terminal);\n    kfree(session);\n}\n\nvoid linux_start_session(const char *exe, const char *const *argv, const char *const *envp, StartSessionDoneBlock done) {\n    struct ish_session *session = kzalloc(sizeof(*session), GFP_KERNEL);\n    session->tty = ios_pty_open(&session->terminal);\n    session->callback = done;\n    struct subprocess_info *proc = call_usermodehelper_setup(exe, (char **) argv, (char **) envp, GFP_KERNEL, session_init, session_cleanup, session);\n    int err = call_usermodehelper_exec(proc, UMH_WAIT_EXEC);\n    if (err < 0)\n        done(err, 0, NULL);\n}\n\nvoid linux_sethostname(const char *hostname) {\n    int len = strlen(hostname);\n    if (len > __NEW_UTS_LEN)\n        len = __NEW_UTS_LEN;\n    down_write(&uts_sem);\n    struct new_utsname *u = utsname();\n    if (strncmp(u->nodename, hostname, len) != 0) {\n        memcpy(u->nodename, hostname, len);\n        memset(u->nodename + len, 0, sizeof(u->nodename) - len);\n        uts_proc_notify(UTS_PROC_HOSTNAME);\n    }\n    up_write(&uts_sem);\n}\n\nssize_t linux_read_file(const char *path, char *buf, size_t size) {\n    struct file *filp = filp_open(path, O_RDONLY, 0);\n    if (IS_ERR(filp))\n        return PTR_ERR(filp);\n    ssize_t res = vfs_read(filp, buf, size, NULL);\n    filp_close(filp, NULL);\n    if (res >= size)\n        return -ENAMETOOLONG;\n    return res;\n}\nssize_t linux_write_file(const char *path, const char *buf, size_t size) {\n    struct file *filp = filp_open(path, O_WRONLY, 0);\n    ssize_t res = vfs_write(filp, buf, size, NULL);\n    filp_close(filp, NULL);\n    return res;\n}\nint linux_remove_directory(const char *path) {\n    return init_rmdir(path);\n}\n"
  },
  {
    "path": "app/LinuxInterop.h",
    "content": "//\n//  LinuxInterop.h\n//  iSH\n//\n//  Created by Theodore Dubois on 7/3/21.\n//\n\n#ifndef LinuxInterop_h\n#define LinuxInterop_h\n\n#ifndef __KERNEL__\n#include <sys/types.h>\n#else\n#include <linux/types.h>\n#include <linux/fs.h>\n#endif\n\nvoid async_do_in_irq(void (^block)(void));\nvoid async_do_in_workqueue(void (^block)(void));\nvoid async_do_in_ios(void (^block)(void));\nvoid sync_do_in_workqueue(void (^block)(void (^done)(void)));\n\n// call into ios from kernel:\n\nvoid actuate_kernel(const char *cmdline);\n\nvoid ReportPanic(const char *message);\nvoid ConsoleLog(const char *data, unsigned len);\nconst char *DefaultRootPath(void);\n\ntypedef const void *nsobj_t;\nnsobj_t objc_get(nsobj_t object);\nvoid objc_put(nsobj_t object);\n\nstruct linux_tty {\n    struct linux_tty_callbacks *ops;\n};\nstruct linux_tty_callbacks {\n    void (*can_output)(struct linux_tty *tty);\n    void (*send_input)(struct linux_tty *tty, const char *data, size_t length);\n    void (*resize)(struct linux_tty *tty, int cols, int rows);\n    void (*hangup)(struct linux_tty *tty);\n};\n\n#ifdef __KERNEL__\nstruct file *ios_pty_open(nsobj_t *terminal_out);\n#endif\n\nnsobj_t Terminal_terminalWithType_number(int type, int number);\nvoid Terminal_setLinuxTTY(nsobj_t _self, struct linux_tty *tty);\nint Terminal_sendOutput_length(nsobj_t _self, const char *data, int size);\nint Terminal_roomForOutput(nsobj_t _self);\n\nnsobj_t UIPasteboard_get(void);\nlong UIPasteboard_changeCount(void);\nvoid UIPasteboard_set(const char *data, size_t len);\nsize_t NSData_length(nsobj_t data);\nconst void *NSData_bytes(nsobj_t data);\n\n// call into kernel from ios:\n\ntypedef void (^StartSessionDoneBlock)(int retval, int pid, nsobj_t terminal);\nvoid linux_start_session(const char *exe, const char *const *argv, const char *const *envp, StartSessionDoneBlock done);\n\nvoid linux_sethostname(const char *hostname);\n\nssize_t linux_read_file(const char *path, char *buf, size_t size);\nssize_t linux_write_file(const char *path, const char *buf, size_t size);\nint linux_remove_directory(const char *path);\n\n#endif /* LinuxInterop_h */\n"
  },
  {
    "path": "app/LinuxPTY.c",
    "content": "//\n//  LinuxPTY.c\n//  libiSHLinux\n//\n//  Created by Theodore Dubois on 12/30/21.\n//\n\n#include <linux/init.h>\n#include <linux/namei.h>\n#include <linux/errname.h>\n#include <linux/kthread.h>\n#include <linux/fs.h>\n#include <linux/hashtable.h>\n#include <linux/syscalls.h>\n#include <linux/init_syscalls.h>\n#include <linux/init_task.h>\n#include <linux/termios.h>\n#include <linux/fcntl.h>\n#include <linux/vmalloc.h>\n#include <linux/fdtable.h>\n#include <uapi/linux/mount.h>\n#include \"LinuxInterop.h\"\n\nstatic struct path ptmx_path;\n\nstruct ios_pty_wq {\n    struct ios_pty *pty;\n    struct wait_queue_entry wq;\n    struct wait_queue_head *head;\n};\n\nstruct ios_pty {\n    dev_t pts_rdev;\n    struct file *ptm;\n    nsobj_t terminal;\n    struct linux_tty linux_tty;\n    // pseudoterminals have multiple wait queues and you need a different wait_queue_entry for each one. fun fact!\n    int n_wqs;\n    struct ios_pty_wq wqs[4];\n    poll_table pt;\n\n    struct work_struct poll_cb_work;\n    struct work_struct output_work;\n};\n\nstatic void ios_pty_output_work(struct work_struct *output_work) {\n    struct ios_pty *pty = container_of(output_work, struct ios_pty, output_work);\n    char *buf = kvmalloc(PAGE_SIZE, GFP_KERNEL);\n    ssize_t size;\n    for (;;) {\n        size_t room = Terminal_roomForOutput(pty->terminal);\n        if (room == 0) {\n            printk(KERN_WARNING \"ios: no room for pty output\\n\");\n            break;\n        }\n        size = kernel_read(pty->ptm, buf, room, NULL);\n        if (size < 0) {\n            if (size != -EAGAIN)\n                printk(KERN_WARNING \"ios: pty read failed: %s\\n\", errname(size));\n            break;\n        }\n        int sent = Terminal_sendOutput_length(pty->terminal, buf, size);\n        if (sent != size) {\n            printk(KERN_WARNING \"ios: dropped %ld bytes of pty output\\n\", size - sent);\n            break;\n        }\n    }\n    kvfree(buf);\n}\n\nstatic void ios_pty_cleanup(struct ios_pty *pty) {\n    for (int i = 0; i < pty->n_wqs; i++)\n        remove_wait_queue(pty->wqs[i].head, &pty->wqs[i].wq);\n    fput(pty->ptm);\n    nsobj_t terminal = pty->terminal;\n    Terminal_setLinuxTTY(terminal, NULL);\n    objc_put(terminal);\n    kfree(pty);\n}\n\nstatic void ios_pty_cb_can_output(struct linux_tty *linux_tty) {\n    struct ios_pty *pty = container_of(linux_tty, struct ios_pty, linux_tty);\n    schedule_work(&pty->output_work);\n}\n\nstatic void ios_pty_cb_send_input(struct linux_tty *linux_tty, const char *data, size_t length) {\n    struct ios_pty *pty = container_of(linux_tty, struct ios_pty, linux_tty);\n    ssize_t written = kernel_write(pty->ptm, data, length, NULL);\n    if (written < 0)\n        printk(KERN_WARNING \"ios: pty input failed: %s\\n\", errname(written));\n    else if (written != length)\n        printk(KERN_WARNING \"ios: dropped %ld bytes of pty input\\n\", length - written);\n}\n\nstatic void ios_pty_cb_resize(struct linux_tty *linux_tty, int cols, int rows) {\n    struct ios_pty *pty = container_of(linux_tty, struct ios_pty, linux_tty);\n    struct winsize ws = {\n        .ws_row = rows,\n        .ws_col = cols,\n    };\n    vfs_ioctl(pty->ptm, TIOCSWINSZ, (unsigned long) &ws);\n}\n\nstatic void ios_pty_cb_hangup(struct linux_tty *linux_tty) {\n    // TODO: figure out what this should be doing\n}\n\nstatic struct linux_tty_callbacks ios_pty_callbacks = {\n    .can_output = ios_pty_cb_can_output,\n    .send_input = ios_pty_cb_send_input,\n    .resize = ios_pty_cb_resize,\n    .hangup = ios_pty_cb_hangup,\n};\n\nstatic void ios_pty_poll_cb_work(struct work_struct *work) {\n    struct ios_pty *pty = container_of(work, struct ios_pty, poll_cb_work);\n    __poll_t events = vfs_poll(pty->ptm, NULL);\n    if (events & EPOLLIN)\n        ios_pty_output_work(&pty->output_work);\n    if (events & EPOLLHUP)\n        ios_pty_cleanup(pty);\n}\n\nstatic int ptm_callback(struct wait_queue_entry *wq_entry, unsigned mode, int flags, void *key) {\n    struct ios_pty *pty = container_of(wq_entry, struct ios_pty_wq, wq)->pty;\n    schedule_work(&pty->poll_cb_work);\n    return 0;\n}\n\nstatic void poll_callback(struct file *file, wait_queue_head_t *whead, poll_table *pt) {\n    struct ios_pty *pty = container_of(pt, struct ios_pty, pt);\n    if (pty->n_wqs >= ARRAY_SIZE(pty->wqs))\n        panic(\"ios pty: too many wait queues!\");\n    struct ios_pty_wq *pty_wq = &pty->wqs[pty->n_wqs++];\n    pty_wq->pty = pty;\n    pty_wq->head = whead;\n    init_waitqueue_func_entry(&pty_wq->wq, ptm_callback);\n    add_wait_queue(whead, &pty_wq->wq);\n}\n\nstruct file *ios_pty_open(nsobj_t *terminal_out) {\n    struct file *ptm_file = dentry_open(&ptmx_path, O_RDWR, current_cred());\n    if (IS_ERR(ptm_file))\n        return ptm_file;\n\n    int lock_pty = 0;\n    vfs_ioctl(ptm_file, TIOCSPTLCK, (unsigned long) &lock_pty);\n    spin_lock(&ptm_file->f_lock);\n    ptm_file->f_flags |= O_NONBLOCK;\n    spin_unlock(&ptm_file->f_lock);\n\n    // sadly this api can't just return a struct file *\n    int fd = vfs_ioctl(ptm_file, TIOCGPTPEER, O_RDWR);\n    if (fd < 0) {\n        fput(ptm_file);\n        return ERR_PTR(fd);\n    }\n    struct file *pts_file = fget(fd);\n    close_fd(fd);\n\n    struct ios_pty *pty = kzalloc(sizeof(*pty), GFP_KERNEL);\n    if (pty == NULL) {\n        fput(pts_file);\n        fput(ptm_file);\n        return ERR_PTR(-ENOMEM);\n    }\n    pty->ptm = ptm_file;\n\n    INIT_WORK(&pty->poll_cb_work, ios_pty_poll_cb_work);\n    INIT_WORK(&pty->output_work, ios_pty_output_work);\n\n    pty->pts_rdev = pts_file->f_inode->i_rdev;\n    pty->terminal = Terminal_terminalWithType_number(MAJOR(pty->pts_rdev), MINOR(pty->pts_rdev));\n    pty->linux_tty.ops = &ios_pty_callbacks;\n    Terminal_setLinuxTTY(pty->terminal, &pty->linux_tty);\n    *terminal_out = pty->terminal;\n\n    init_poll_funcptr(&pty->pt, poll_callback);\n    __poll_t revents = vfs_poll(pty->ptm, &pty->pt);\n    if (revents)\n        ptm_callback(&pty->wqs[pty->n_wqs-1].wq, 0, 0, NULL);\n    return pts_file;\n}\n\nstatic __init int ios_pty_init(void) {\n    init_mkdir(\"/dev/pts\", 0755);\n    int err = do_mount(\"devpts\", \"/dev/pts\", \"devpts\", MS_SILENT, NULL);\n    if (err < 0) {\n        panic(\"ish: failed to mount devpts: %s\", errname(err));\n    }\n    err = kern_path(\"/dev/pts/ptmx\", 0, &ptmx_path);\n    if (err < 0) {\n        panic(\"ish: failed to acquire ptmx: %s\", errname(err));\n    }\n    return 0;\n}\n\ndevice_initcall(ios_pty_init);\n"
  },
  {
    "path": "app/LinuxRoot.c",
    "content": "//\n//  LinuxRoot.c\n//  libiSHLinux\n//\n//  Created by Theodore Dubois on 12/29/21.\n//\n\n#include <linux/init.h>\n#include <linux/syscalls.h>\n#include <linux/init_syscalls.h>\n#include <linux/fs.h>\n#include <linux/errname.h>\n#include <linux/device.h>\n#include <uapi/linux/mount.h>\n#include \"LinuxInterop.h\"\n\nvoid FsInitialize(void);\n\nstatic __init int ish_rootfs(void) {\n    rootfs_mounted = true;\n\n    const char *fakefs_path = DefaultRootPath();\n    int err = do_mount(fakefs_path, \"/root\", \"fakefs\", MS_SILENT, NULL);\n    if (err < 0) {\n        pr_emerg(\"ish: failed to mount fakefs root from %s: %s\\n\", fakefs_path, errname(err));\n        return err;\n    }\n    init_chdir(\"/root\");\n\n    devtmpfs_mount();\n    err = do_mount(\"proc\", \"proc\", \"proc\", MS_SILENT, NULL);\n    if (err < 0) {\n        pr_warn(\"ish: failed to mount procfs: %s\", errname(err));\n    }\n\n    do_mount(\".\", \"/\", NULL, MS_MOVE, NULL);\n    init_chroot(\".\");\n\n    FsInitialize();\n    return 0;\n}\n\nrootfs_initcall(ish_rootfs);\n"
  },
  {
    "path": "app/LinuxTTY.c",
    "content": "//\n//  LinuxTTY.c\n//  libiSHLinux\n//\n//  Created by Theodore Dubois on 8/15/21.\n//\n\n#include \"LinuxInterop.h\"\n#include <linux/bug.h>\n#include <linux/console.h>\n#include <linux/errno.h>\n#include <linux/init.h>\n#include <linux/tty.h>\n#include <linux/tty_flip.h>\n#include <linux/slab.h>\n\nstatic void nslog_console_write(struct console *console, const char *data, unsigned len) {\n    ConsoleLog(data, len);\n}\n\nstatic struct console nslog_console = {\n    .name = \"nslog\",\n    .write = nslog_console_write,\n    .flags = CON_PRINTBUFFER|CON_ANYTIME,\n    .index = -1,\n};\n\nstatic __init int nslog_init(void) {\n    register_console(&nslog_console);\n    return 0;\n}\ndevice_initcall(nslog_init);\n\nstruct ios_tty {\n    struct linux_tty linux_tty;\n    struct tty_port port;\n};\n\n#define NUM_TTYS 6\nstatic struct tty_driver *ios_tty_driver;\nstatic struct ios_tty ios_ttys[NUM_TTYS];\n\nstatic int ios_tty_port_activate(struct tty_port *port, struct tty_struct *tty) {\n    BUG_ON(port != &ios_ttys[tty->index].port);\n    port->client_data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, tty->index);\n    Terminal_setLinuxTTY(port->client_data, &container_of(port, struct ios_tty, port)->linux_tty);\n    return 0;\n}\nstatic void ios_tty_port_destruct(struct tty_port *port) {\n    async_do_in_ios(^{\n        objc_put(port->client_data);\n        async_do_in_irq(^{\n            kfree(port);\n        });\n    });\n}\nstatic struct tty_port_operations ios_tty_port_ops = {\n    .activate = ios_tty_port_activate,\n    .destruct = ios_tty_port_destruct,\n};\n\nstatic void ios_tty_cb_can_output(struct linux_tty *linux_tty) {\n    struct ios_tty *tty = container_of(linux_tty, struct ios_tty, linux_tty);\n    tty_port_tty_wakeup(&tty->port);\n}\n\nstatic void ios_tty_cb_send_input(struct linux_tty *linux_tty, const char *data, size_t length) {\n    struct ios_tty *tty = container_of(linux_tty, struct ios_tty, linux_tty);\n    tty_insert_flip_string(&tty->port, data, length);\n    tty_flip_buffer_push(&tty->port);\n}\n\nstatic void ios_tty_cb_resize(struct linux_tty *linux_tty, int cols, int rows) {\n    struct ios_tty *tty = container_of(linux_tty, struct ios_tty, linux_tty);\n    struct winsize ws = {\n        .ws_row = rows,\n        .ws_col = cols,\n    };\n    tty_do_resize(tty->port.tty, &ws);\n}\n\nstatic void ios_tty_cb_hangup(struct linux_tty *linux_tty) {\n    // nah\n}\n\nstatic struct linux_tty_callbacks ios_tty_callbacks = {\n    .can_output = ios_tty_cb_can_output,\n    .send_input = ios_tty_cb_send_input,\n    .resize = ios_tty_cb_resize,\n    .hangup = ios_tty_cb_hangup,\n};\n\nstatic int ios_tty_open(struct tty_struct *tty, struct file *filp) {\n    return tty_port_open(tty->port, tty, filp);\n}\n\nstatic int ios_tty_write(struct tty_struct *tty, const unsigned char *data, int len) {\n    return Terminal_sendOutput_length(tty->port->client_data, data, len);\n}\n\nstatic unsigned int ios_tty_write_room(struct tty_struct *tty) {\n    return Terminal_roomForOutput(tty->port->client_data);\n}\n\nstatic struct tty_operations ios_tty_ops = {\n    .open = ios_tty_open,\n    .write = ios_tty_write,\n    .write_room = ios_tty_write_room,\n};\n\nstatic int ios_tty_console_setup(struct console *console, char *what) {\n    console->data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, 1);\n    return 0;\n}\n\nstatic void ios_tty_console_write(struct console *console, const char *data, unsigned len) {\n    nsobj_t tty = console->data;\n    while (len) {\n        const char *newline = memchr(data, '\\n', len);\n        if (newline != NULL) {\n            Terminal_sendOutput_length(tty, data, newline - data);\n            Terminal_sendOutput_length(tty, \"\\r\\n\", 2);\n            len -= newline - data + 1;\n            data = newline + 1;\n        } else {\n            Terminal_sendOutput_length(tty, data, len);\n            len = 0;\n        }\n    }\n}\n\nstatic struct tty_driver *ios_tty_console_device(struct console *console, int *index) {\n    *index = console->index;\n    return ios_tty_driver;\n}\n\nstatic struct console ios_tty_console = {\n    .name = \"tty\",\n    .setup = ios_tty_console_setup,\n    .write = ios_tty_console_write,\n    .device = ios_tty_console_device,\n    .flags = CON_PRINTBUFFER|CON_ANYTIME,\n    .index = -1,\n};\n\nstatic __init int ios_tty_init(void) {\n    for (int i = 0; i < NUM_TTYS; i++) {\n        ios_ttys[i].linux_tty.ops = &ios_tty_callbacks;\n        tty_port_init(&ios_ttys[i].port);\n        ios_ttys[i].port.ops = &ios_tty_port_ops;\n    }\n\n    ios_tty_driver = tty_alloc_driver(NUM_TTYS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS);\n    ios_tty_driver->driver_name = \"ios\";\n    ios_tty_driver->name = \"tty\";\n    ios_tty_driver->name_base = 1;\n    ios_tty_driver->major = TTY_MAJOR;\n    ios_tty_driver->minor_start = 1;\n    ios_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE;\n    ios_tty_driver->subtype = SYSTEM_TYPE_CONSOLE;\n    ios_tty_driver->init_termios = tty_std_termios;\n    tty_set_operations(ios_tty_driver, &ios_tty_ops);\n\n    for (int i = 0; i < NUM_TTYS; i++) {\n        tty_port_link_device(&ios_ttys[i].port, ios_tty_driver, i);\n    }\n\n    if (tty_register_driver(ios_tty_driver))\n        panic(\"ios tty: failed to tty_register_driver\");\n\n    register_console(&ios_tty_console);\n\n    return 0;\n}\ndevice_initcall(ios_tty_init);\n"
  },
  {
    "path": "app/LocationDevice.h",
    "content": "//\n//  LocationDevice.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/20/19.\n//\n\nextern struct dev_ops location_dev;\n"
  },
  {
    "path": "app/LocationDevice.m",
    "content": "//\n//  LocationDevice.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/20/19.\n//\n\n#import <Foundation/Foundation.h>\n#import <CoreLocation/CoreLocation.h>\n#include \"kernel/fs.h\"\n#include \"fs/dev.h\"\n#include \"util/sync.h\"\n\n@interface LocationTracker : NSObject <CLLocationManagerDelegate>\n\n+ (LocationTracker *)instance;\n\n@property CLLocationManager *locationManager;\n@property (nonatomic) CLLocation *latest;\n@property lock_t lock;\n@property cond_t updateCond;\n\n- (int)waitForUpdate;\n\n@end\n\nBOOL CLIsAuthorized(CLAuthorizationStatus status) {\n    return status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways;\n}\n\n@implementation LocationTracker\n\n+ (LocationTracker *)instance {\n    static __weak LocationTracker *tracker;\n    if (tracker == nil) {\n        __block LocationTracker *newTracker;\n        dispatch_sync(dispatch_get_main_queue(), ^{\n            if (tracker == nil) {\n                newTracker = [LocationTracker new];\n                tracker = newTracker;\n            }\n        });\n        return newTracker;\n    }\n    return tracker;\n}\n\n- (instancetype)init {\n    if (self = [super init]) {\n        self.locationManager = [CLLocationManager new];\n        self.locationManager.delegate = self;\n        self.locationManager.allowsBackgroundLocationUpdates = YES;\n        if (CLIsAuthorized([CLLocationManager authorizationStatus])) {\n            [self.locationManager startUpdatingLocation];\n            [self.locationManager requestLocation];\n        } else {\n            [self.locationManager requestAlwaysAuthorization];\n        }\n        \n        lock_init(&_lock);\n        cond_init(&_updateCond);\n    }\n    return self;\n}\n\n- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {\n    lock(&_lock);\n    self.latest = locations.lastObject;\n    notify(&_updateCond);\n    unlock(&_lock);\n}\n\n- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {\n    NSLog(@\"location failed %@\", error);\n}\n\n- (void)dealloc {\n    [self.locationManager stopUpdatingLocation];\n    cond_destroy(&_updateCond);\n}\n\n- (int)waitForUpdate {\n    lock(&_lock);\n    CLLocation *oldLatest = self.latest;\n    int err = 0;\n    while (self.latest == oldLatest) {\n        err = wait_for(&_updateCond, &_lock, NULL);\n        if (err < 0)\n            break;\n    }\n    unlock(&_lock);\n    return err;\n}\n\n- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {\n    if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {\n        NSLog(@\"got auth, starting updates\");\n        [manager startUpdatingLocation];\n    }\n}\n\n@end\n\n@interface LocationFile : NSObject {\n    NSData *buffer;\n    size_t bufferOffset;\n}\n\n@property LocationTracker *tracker;\n\n- (ssize_t)readIntoBuffer:(void *)buf size:(size_t)size;\n\n@end\n\n@implementation LocationFile\n\n- (instancetype)init {\n    if (self = [super init]) {\n        self.tracker = [LocationTracker instance];\n    }\n    return self;\n}\n\n- (int)waitForUpdate {\n    if (buffer != nil)\n        return 0;\n    int err = [self.tracker waitForUpdate];\n    if (err < 0)\n        return err;\n    CLLocation *location = self.tracker.latest;\n    NSString *output = [NSString stringWithFormat:@\"%+f,%+f\\n\", location.coordinate.latitude, location.coordinate.longitude];\n    buffer = [output dataUsingEncoding:NSUTF8StringEncoding];\n    bufferOffset = 0;\n    return 0;\n}\n\n- (ssize_t)readIntoBuffer:(void *)buf size:(size_t)size {\n    @synchronized (self) {\n        int err = [self waitForUpdate];\n        if (err < 0)\n            return err;\n        size_t remaining = buffer.length - bufferOffset;\n        if (size > remaining)\n            size = remaining;\n        [buffer getBytes:buf range:NSMakeRange(bufferOffset, size)];\n        bufferOffset += size;\n        if (bufferOffset == buffer.length)\n            buffer = nil;\n        return size;\n    }\n}\n\n@end\n\nstatic int location_open(int major, int minor, struct fd *fd) {\n    fd->data = (void *) CFBridgingRetain([LocationFile new]);\n    return 0;\n}\n\nstatic int location_close(struct fd *fd) {\n    CFBridgingRelease(fd->data);\n    return 0;\n}\n\nstatic ssize_t location_read(struct fd *fd, void *buf, size_t size) {\n    LocationFile *file = (__bridge LocationFile *) fd->data;\n    return [file readIntoBuffer:buf size:size];\n}\n\nconst struct dev_ops location_dev = {\n    .open = location_open,\n    .fd.close = location_close,\n    .fd.read = location_read,\n};\n"
  },
  {
    "path": "app/NSObject+SaneKVO.h",
    "content": "//\n//  NSObject+SaneKVO.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/10/20.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface KVOObservation : NSObject {\n    BOOL _enabled;\n    __weak id _object;\n    NSString *_keyPath;\n    void (^_block)(void);\n}\n- (void)disable;\n@end\n\n@interface NSObject (SaneKVO)\n\n- (KVOObservation *)observe:(NSString *)keyPath\n                    options:(NSKeyValueObservingOptions)options\n                 usingBlock:(void (^)(void))block;\n- (void)observe:(NSArray<NSString *> *)keyPaths\n        options:(NSKeyValueObservingOptions)options\n          owner:(id)owner\n     usingBlock:(void (^)(id))block;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/NSObject+SaneKVO.m",
    "content": "//\n//  NSObject+SaneKVO.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/10/20.\n//\n\n#import <objc/runtime.h>\n#import \"NSObject+SaneKVO.h\"\n\nstatic void *kKVOObservations = &kKVOObservations;\n\n@interface KVOObservation ()\n- (instancetype)initWithKeyPath:(NSString *)keyPath object:(id)object block:(void (^)(void))block;\n@end\n\n@implementation NSObject (SaneKVO)\n\n- (KVOObservation *)observe:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(void (^)(void))block {\n    KVOObservation *observation = [[KVOObservation alloc] initWithKeyPath:keyPath object:self block:block];\n    [self addObserver:observation forKeyPath:keyPath options:options context:NULL];\n    return observation;\n}\n\n- (void)observe:(NSArray<NSString *> *)keyPaths options:(NSKeyValueObservingOptions)options owner:(id)owner usingBlock:(void (^)(id self))block {\n    __weak id weakOwner = owner;\n    void (^newBlock)(void) = ^{\n        id owner = weakOwner;\n        NSAssert(owner, @\"kvo notification shouldn't come to dead object\");\n        block(owner);\n    };\n    @synchronized (owner) {\n        for (NSString *keyPath in keyPaths) {\n            NSMutableSet *observations = objc_getAssociatedObject(owner, kKVOObservations);\n            if (observations == nil) {\n                observations = [NSMutableSet new];\n                objc_setAssociatedObject(owner, kKVOObservations, observations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);\n            }\n            [observations addObject:[self observe:keyPath options:options usingBlock:newBlock]];\n        }\n    }\n}\n\n@end\n\n@implementation KVOObservation\n\n- (instancetype)initWithKeyPath:(NSString *)keyPath object:(id)object block:(void (^)(void))block {\n    if (self = [super init]) {\n        _keyPath = keyPath;\n        _object = object;\n        _block = block;\n        _enabled = YES;\n    }\n    return self;\n}\n\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {\n    _block();\n}\n\n- (void)disable {\n    if (_enabled) {\n        [_object removeObserver:self forKeyPath:_keyPath context:NULL];\n        _enabled = NO;\n    }\n}\n- (void)dealloc {\n    [self disable];\n}\n\n@end\n"
  },
  {
    "path": "app/NotLinux.xcconfig",
    "content": "NINJA_TARGETS = libish.a libish_emu.a libfakefs.a\n"
  },
  {
    "path": "app/PassthroughView.h",
    "content": "//\n//  PassthroughView.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/24/20.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface PassthroughView : UIView\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/PassthroughView.m",
    "content": "//\n//  PassthroughView.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/24/20.\n//\n\n#import \"PassthroughView.h\"\n\n@implementation PassthroughView\n\n- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {\n    for (UIView *subview in self.subviews) {\n        if (subview.userInteractionEnabled && [subview pointInside:[self convertPoint:point toView:subview] withEvent:event])\n            return YES;\n    }\n    return NO;\n}\n\n@end\n"
  },
  {
    "path": "app/PasteboardDevice.h",
    "content": "// Pasteboard is implementation of /dev/clipboard device\nextern struct dev_ops clipboard_dev;\n"
  },
  {
    "path": "app/PasteboardDevice.m",
    "content": "#include <string.h>\n#import <UIKit/UIKit.h>\n#include \"fs/poll.h\"\n#include \"fs/dyndev.h\"\n#include \"kernel/errno.h\"\n#include \"debug.h\"\n\n/**\n * buffer is dynamically sized buffer of size buffer_cap\n * All writes go to it, and buffer_len is length of data held in buffer\n */\n\n// Prepare for fd separation\n#define fd_priv(fd) fd->clipboard\ntypedef struct fd clip_fd;\n\n#define INITIAL_BUFFER_CAP 1024\n// 8MB: https://stackoverflow.com/a/3523175\n#define MAXIMAL_BUFFER_CAP 8*1024*1024\n\n// If pasteboard contents were changed since file was opened,\n// all read operations on in return error\nstatic int check_read_generation(clip_fd *fd) {\n    UIPasteboard *pb = UIPasteboard.generalPasteboard;\n\n    uint64_t pb_gen = (uint64_t) pb.changeCount;\n    uint64_t fd_gen = fd_priv(fd).generation;\n\n    if (fd_gen == 0 || fd->offset == 0) {\n        fd_priv(fd).generation = pb_gen;\n    } else if (fd_gen != pb_gen) {\n        return -1;\n    }\n\n    return 0;\n}\n\nstatic const char *get_data(clip_fd *fd, size_t *len) {\n    if (fd_priv(fd).buffer != NULL) {\n        *len = fd_priv(fd).buffer_len;\n        return fd_priv(fd).buffer;\n    }\n\n    if (check_read_generation(fd) != 0) {\n        return NULL;\n    }\n\n    NSString __autoreleasing *contents = UIPasteboard.generalPasteboard.string;\n    *len = contents.length;\n    return contents.UTF8String;\n}\n\nstatic int realloc_to_fit(clip_fd* fd, size_t fit_len) {\n    // (Re)allocate buffer if there's not enough space to fit fit_len\n    if (fit_len <= fd_priv(fd).buffer_cap) {\n        return 0;\n    }\n    if (fit_len > MAXIMAL_BUFFER_CAP) {\n        return 1;\n    }\n\n    size_t size = fd_priv(fd).buffer_cap * 2;\n    if (size == 0) {\n        size = INITIAL_BUFFER_CAP;\n    }\n    while (size < fit_len) size *= 2;\n\n    void *new_buf = realloc(fd_priv(fd).buffer, size);\n    if (new_buf == NULL) {\n        return 1;\n    }\n\n    fd_priv(fd).buffer = new_buf;\n    fd_priv(fd).buffer_cap = size;\n\n    return 0;\n}\n\n// buffer => UIPasteboard\nstatic int clipboard_write_sync(clip_fd *fd) {\n    if (fd_priv(fd).buffer == NULL) {\n        return 0;\n    }\n\n    void *data = fd_priv(fd).buffer;\n    size_t len = fd_priv(fd).buffer_len;\n\n    // FIXME(stek29): This logs \"Returning local object of class NSString\"\n    // and I have no idea why (or how to fix it)\n    UIPasteboard.generalPasteboard.string = [[NSString alloc]\n                                             initWithBytes:data\n                                             length:len\n                                             encoding:NSUTF8StringEncoding];\n\n    // Reset generation since we've just updated UIPasteboard\n    // note: offset doesn't change\n    fd_priv(fd).generation = 0;\n\n    return 0;\n}\n\n// UIPasteboard => buffer, return len\nstatic ssize_t clipboard_read_sync(clip_fd *fd) {\n    if (fd_priv(fd).buffer != NULL) {\n        free(fd_priv(fd).buffer);\n        fd_priv(fd).buffer = NULL;\n        fd_priv(fd).buffer_cap = 0;\n        fd_priv(fd).buffer_len = 0;\n    }\n\n    @autoreleasepool {\n        size_t len;\n        const void *data = get_data(fd, &len);\n\n        // Make sure size is still INITIAL_BUFFER_CAP based\n        if (realloc_to_fit(fd, len)) {\n            return _ENOMEM;\n        }\n\n        memcpy(fd_priv(fd).buffer, data, len);\n        fd_priv(fd).buffer_len = len;\n\n        return len;\n    }\n}\n\nstatic int clipboard_poll(clip_fd *fd) {\n    return POLL_READ | POLL_WRITE;\n}\n\nstatic ssize_t clipboard_read(clip_fd *fd, void *buf, size_t bufsize) {\n    @autoreleasepool {\n        size_t length = 0;\n        const char *data = get_data(fd, &length);\n\n        if (data == NULL) {\n            return _EIO;\n        }\n\n        size_t remaining = length - fd->offset;\n        if ((size_t) fd->offset > length)\n            remaining = 0;\n\n        size_t n = bufsize;\n        if (n > remaining)\n            n = remaining;\n\n        if (n != 0) {\n            memcpy(buf, data + fd->offset, n);\n            fd->offset += n;\n        }\n\n        return n;\n    }\n}\n\nstatic ssize_t clipboard_write(clip_fd *fd, const void *buf, size_t bufsize) {\n    size_t new_len = fd->offset + bufsize;\n    size_t old_len = fd_priv(fd).buffer_len;\n\n    if (old_len > new_len) {\n        new_len = old_len;\n    }\n\n    if (realloc_to_fit(fd, new_len)) {\n        return _ENOMEM;\n    }\n\n    // fill the hole between new offset and old len\n    if (old_len < fd->offset) {\n        memset(fd_priv(fd).buffer + old_len, '\\0', fd->offset - old_len);\n    }\n\n    memcpy(fd_priv(fd).buffer + fd->offset, buf, bufsize);\n    fd->offset += bufsize;\n    fd_priv(fd).buffer_len = new_len;\n\n    return bufsize;\n}\n\nstatic off_t_ clipboard_lseek(clip_fd *fd, off_t_ off, int whence) {\n    off_t_ old_off = fd->offset;\n    size_t length = 0;\n\n    if (whence != LSEEK_SET || off != 0) {\n        @autoreleasepool {\n            if (get_data(fd, &length) == NULL) {\n                return _EIO;\n            }\n        }\n    }\n\n    switch (whence) {\n    case LSEEK_SET:\n        fd->offset = off;\n        break;\n\n    case LSEEK_CUR:\n        fd->offset += off;\n        break;\n\n    case LSEEK_END:\n        fd->offset = length + off;\n        break;\n\n    default:\n        return _EINVAL;\n    }\n\n    if (fd->offset < 0) {\n        fd->offset = old_off;\n        return _EINVAL;\n    }\n\n    return fd->offset;\n}\n\nstatic int clipboard_close(clip_fd *fd) {\n    clipboard_write_sync(fd);\n    if (fd_priv(fd).buffer != NULL) {\n        free(fd_priv(fd).buffer);\n    }\n    return 0;\n}\n\nstatic int clipboard_open(int major, int minor, clip_fd *fd) {\n    // Zero fd_priv data\n    memset(&fd_priv(fd), 0, sizeof(fd_priv(fd)));\n\n    // If O_TRUNC is not set, initialize buffer with current pasteboard contents\n    if (!(fd->flags & O_TRUNC_)) {\n        ssize_t len = clipboard_read_sync(fd);\n        if (len < 0) {\n            return (int) len;\n        }\n        if (fd->flags & O_APPEND_) {\n            fd->offset = (size_t) len;\n        }\n    }\n\n    return 0;\n}\n\nstruct dev_ops clipboard_dev = {\n    .open = clipboard_open,\n    .fd.read = clipboard_read,\n    .fd.write = clipboard_write,\n    .fd.lseek = clipboard_lseek,\n    .fd.poll = clipboard_poll,\n    .fd.close = clipboard_close,\n    .fd.fsync = clipboard_write_sync,\n};\n"
  },
  {
    "path": "app/PasteboardDeviceLinux.c",
    "content": "//\n//  PasteboardDeviceLinux.c\n//  iSH+Linux\n//\n//  Created by Theodore Dubois on 2/19/22.\n//\n\n#include <linux/slab.h>\n#include <linux/errno.h>\n#include <linux/fs.h>\n#include <linux/miscdevice.h>\n#include <linux/init.h>\n#include <linux/fcntl.h>\n#include \"LinuxInterop.h\"\n\n#define INITIAL_BUFFER_CAP 1024\n// 8MB: https://stackoverflow.com/a/3523175\n#define MAXIMAL_BUFFER_CAP 8*1024*1024\n\nstruct pasteboard_file {\n    char *buffer;\n    size_t cap;\n    size_t len;\n    long generation;\n};\n\nstatic int realloc_buffer_to_fit(struct file *file, size_t fit_len) {\n    struct pasteboard_file *pb = file->private_data;\n    // (Re)allocate buffer if there's not enough space to fit fit_len\n    if (fit_len <= pb->cap)\n        return 0;\n    if (fit_len > MAXIMAL_BUFFER_CAP)\n        return -ENOSPC;\n\n    size_t size = pb->cap * 2;\n    if (size == 0)\n        size = INITIAL_BUFFER_CAP;\n    while (size < fit_len) size *= 2;\n\n    void *new_buf = krealloc(pb->buffer, size, GFP_KERNEL);\n    if (new_buf == NULL)\n        return -ENOMEM;\n\n    pb->buffer = new_buf;\n    pb->cap = size;\n    return 0;\n}\n\nstatic int read_pasteboard_to_buffer(struct file *file) {\n    struct pasteboard_file *pb = file->private_data;\n    nsobj_t data = UIPasteboard_get();\n    int err = realloc_buffer_to_fit(file, NSData_length(data));\n    if (err < 0) {\n        objc_put(data);\n        return err;\n    }\n    pb->len = NSData_length(data);\n    memcpy(pb->buffer, NSData_bytes(data), pb->len);\n    objc_put(data);\n    return 0;\n}\n\nstatic int pasteboard_open(struct inode *ino, struct file *file) {\n    struct pasteboard_file *pb = kzalloc(sizeof(struct pasteboard_file), GFP_KERNEL);\n    int err = -ENOMEM;\n    if (pb == NULL)\n        goto fail;\n    file->private_data = pb;\n\n    if (!(file->f_flags & O_TRUNC)) {\n        err = read_pasteboard_to_buffer(file);\n        if (err < 0)\n            goto fail_free_pb;\n    }\n\n    return 0;\n\nfail_free_pb:\n    if (pb->buffer != NULL)\n        kfree(pb->buffer);\n    kfree(pb);\nfail:\n    file->private_data = NULL;\n    return err;\n}\n\nstatic loff_t pasteboard_llseek(struct file *file, loff_t off, int whence) {\n    struct pasteboard_file *pb = file->private_data;\n    return generic_file_llseek_size(file, off, whence, MAXIMAL_BUFFER_CAP, pb->len);\n}\n\nstatic ssize_t pasteboard_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {\n    struct pasteboard_file *pb = file->private_data;\n    return simple_read_from_buffer(buf, count, ppos, pb->buffer, pb->len);\n}\n\nstatic ssize_t pasteboard_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {\n    struct pasteboard_file *pb = file->private_data;\n    if (file->f_flags & O_APPEND)\n        *ppos = pb->len;\n    loff_t new_len = *ppos + count;\n    int err = realloc_buffer_to_fit(file, new_len);\n    if (err < 0)\n        return err;\n    ssize_t result = simple_write_to_buffer(pb->buffer, pb->cap, ppos, buf, count);\n    if (result < 0)\n        return result;\n    pb->len = new_len;\n    return result;\n}\n\nstatic int pasteboard_fsync(struct file *file, loff_t start, loff_t end, int datasync) {\n    struct pasteboard_file *pb = file->private_data;\n    UIPasteboard_set(pb->buffer, pb->len);\n    return 0;\n}\n\nstatic int pasteboard_release(struct inode *inode, struct file *file) {\n    struct pasteboard_file *pb = file->private_data;\n    pasteboard_fsync(file, 0, 0, 0);\n    if (pb->buffer != NULL)\n        kfree(pb->buffer);\n    kfree(pb);\n    return 0;\n}\n\nstatic struct file_operations pasteboard_fops = {\n    .owner = THIS_MODULE,\n    .open = pasteboard_open,\n    .read = pasteboard_read,\n    .write = pasteboard_write,\n    .llseek = pasteboard_llseek,\n    .fsync = pasteboard_fsync,\n    .release = pasteboard_release,\n};\n\nstatic struct miscdevice pasteboard_dev = {\n    .name = \"clipboard\",\n    .minor = MISC_DYNAMIC_MINOR,\n    .fops = &pasteboard_fops,\n};\n\nint __init pasteboard_init(void) {\n    return misc_register(&pasteboard_dev);\n}\n\ndevice_initcall(pasteboard_init);\n"
  },
  {
    "path": "app/ProgressReportViewController.h",
    "content": "//\n//  ProgressReportViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 6/18/20.\n//\n\n#import <UIKit/UIKit.h>\n#import \"Roots.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface ProgressReportViewController : UIViewController <ProgressReporter>\n\n- (void)updateProgress:(double)progressFraction message:(NSString *)progressMessage;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/ProgressReportViewController.m",
    "content": "//\n//  ProgressReportViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 6/18/20.\n//\n\n#import \"ProgressReportViewController.h\"\n\n@interface ProgressReportViewController ()\n\n@property (weak, nonatomic) IBOutlet UIView *popupView;\n@property (weak, nonatomic) IBOutlet UIVisualEffectView *backdrop;\n@property (weak, nonatomic) IBOutlet UILabel *titleLabel;\n@property (weak, nonatomic) IBOutlet UILabel *statusLabel;\n@property (weak, nonatomic) IBOutlet UIProgressView *bar;\n@property (weak, nonatomic) IBOutlet UIButton *cancelButton;\n\n@property (nonatomic) double progress;\n@property (nonatomic) NSString *message;\n@property (nonatomic) BOOL cancelled;\n\n@property CADisplayLink *timer;\n\n@end\n\n@implementation ProgressReportViewController\n\n- (void)viewDidLoad {\n    self.titleLabel.text = self.title;\n    if (@available(iOS 13, *)) {\n        self.backdrop.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterial];\n    }\n}\n\n- (void)viewDidLayoutSubviews {\n    CAShapeLayer *mask = [CAShapeLayer new];\n    mask.path = [UIBezierPath bezierPathWithRoundedRect:self.popupView.bounds cornerRadius:13].CGPath;\n    self.popupView.layer.mask = mask;\n    self.popupView.layer.masksToBounds = YES;\n}\n\n- (void)viewWillAppear:(BOOL)animated {\n    [super viewWillAppear:animated];\n    self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];\n    [self.timer addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];\n}\n\n- (void)viewDidDisappear:(BOOL)animated {\n    [super viewDidDisappear:animated];\n    [self.timer invalidate];\n}\n\n- (void)updateProgress:(double)progressFraction message:(NSString *)progressMessage {\n    @synchronized (self) {\n        _progress = progressFraction;\n        _message = progressMessage;\n    }\n}\n\n- (BOOL)shouldCancel {\n    @synchronized (self) {\n        return _cancelled;\n    }\n}\n\n- (void)update {\n    @synchronized (self) {\n        self.bar.progress = _progress;\n        self.statusLabel.text = _message;\n    }\n}\n\n- (IBAction)cancel:(id)sender {\n    @synchronized (self) {\n        self.cancelled = YES;\n        self.cancelButton.enabled = NO;\n    }\n}\n\n@end\n"
  },
  {
    "path": "app/Project.xcconfig",
    "content": "#include \"iSH.xcconfig\"\n\nMARKETING_VERSION = 1.3.3\n\nENABLE_BITCODE = NO // no idea why\nPRODUCT_BUNDLE_IDENTIFIER = $(ROOT_BUNDLE_IDENTIFIER)\nPRODUCT_APP_GROUP_IDENTIFIER = group.$(ROOT_BUNDLE_IDENTIFIER)\nIPHONEOS_DEPLOYMENT_TARGET = 11.0\nVERSIONING_SYSTEM = apple-generic\n\nMESON_BUILD_DIR = $(CONFIGURATION_BUILD_DIR)/meson\nNINJA_TARGETS = libish.a libish_emu.a libfakefs.a\n\nSUPPORTED_PLATFORMS = iphonesimulator iphoneos macosx\n"
  },
  {
    "path": "app/ProjectDebug.xcconfig",
    "content": "#include \"XcodeDebug.xcconfig\"\n#include \"Project.xcconfig\"\n#include \"NotLinux.xcconfig\"\n"
  },
  {
    "path": "app/ProjectDebugLinux.xcconfig",
    "content": "#include \"XcodeDebug.xcconfig\"\n#include \"Project.xcconfig\"\n#include \"Linux.xcconfig\"\n"
  },
  {
    "path": "app/ProjectRelease.xcconfig",
    "content": "#include \"XcodeRelease.xcconfig\"\n#include \"Project.xcconfig\"\n#include \"NotLinux.xcconfig\"\n"
  },
  {
    "path": "app/ProjectReleaseLinux.xcconfig",
    "content": "#include \"XcodeRelease.xcconfig\"\n#include \"Project.xcconfig\"\n#include \"Linux.xcconfig\"\n"
  },
  {
    "path": "app/Roots.h",
    "content": "//\n//  Roots.h\n//  iSH\n//\n//  Created by Theodore Dubois on 6/7/20.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@protocol ProgressReporter\n\n- (void)updateProgress:(double)progressFraction message:(NSString *)progressMessage;\n- (BOOL)shouldCancel;\n\n@end\n\n@interface Roots : NSObject\n\n+ (instancetype)instance;\n\n@property (readonly) NSOrderedSet<NSString *> *roots;\n@property NSString *defaultRoot;\n@property (readonly) BOOL wantsVersionFile;\n- (NSURL *)rootUrl:(NSString *)name;\n- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress;\n- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress;\n- (BOOL)destroyRootNamed:(NSString *)name error:(NSError **)error;\n- (BOOL)renameRoot:(NSString *)name toName:(NSString *)newName error:(NSError **)error;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/Roots.m",
    "content": "//\n//  Roots.m\n//  iSH\n//\n//  Created by Theodore Dubois on 6/7/20.\n//\n\n#import <FileProvider/FileProvider.h>\n#import \"Roots.h\"\n#import \"AppGroup.h\"\n#import \"NSObject+SaneKVO.h\"\n#include \"tools/fakefs.h\"\n\nstatic NSURL *RootsDir(void) {\n    static NSURL *rootsDir;\n    static dispatch_once_t token;\n    dispatch_once(&token, ^{\n        rootsDir = [ContainerURL() URLByAppendingPathComponent:@\"roots\"];\n        NSFileManager *manager = [NSFileManager defaultManager];\n        [manager createDirectoryAtURL:rootsDir\n          withIntermediateDirectories:YES\n                           attributes:@{}\n                                error:nil];\n    });\n    return rootsDir;\n}\n\nstatic NSString *kDefaultRoot = @\"Default Root\";\n\n@interface Roots ()\n@property NSMutableOrderedSet<NSString *> *roots;\n@property BOOL updatingDomains;\n@property BOOL domainsNeedUpdate;\n@property BOOL wantsVersionFile;\n@end\n\n@implementation Roots\n\n- (instancetype)init {\n    if (self = [super init]) {\n        NSError *error = nil;\n        NSArray<NSString *> *rootNames = [NSFileManager.defaultManager contentsOfDirectoryAtPath:RootsDir().path error:&error];\n        NSAssert(error == nil, @\"couldn't list roots: %@\", error);\n        self.roots = [rootNames mutableCopy];\n        if (!self.roots.count) {\n            // import default root\n            NSError *error;\n            if (![self importRootFromArchive:[NSBundle.mainBundle URLForResource:@\"root\" withExtension:@\"tar.gz\"]\n                                        name:@\"default\"\n                                       error:&error\n                            progressReporter:nil]) {\n                NSAssert(NO, @\"failed to import default root, error %@\", error);\n            }\n            _wantsVersionFile = YES;\n        }\n        [self observe:@[@\"roots\"] options:0 owner:self usingBlock:^(typeof(self) self) {\n            if (self.defaultRoot == nil && self.roots.count)\n                self.defaultRoot = self.roots[0];\n            [self syncFileProviderDomains];\n        }];\n        [self syncFileProviderDomains];\n\n        if ((!self.defaultRoot || ![self.roots containsObject:self.defaultRoot]) && self.roots.count)\n            self.defaultRoot = self.roots.firstObject;\n    }\n    return self;\n}\n\n- (NSString *)defaultRoot {\n    return [NSUserDefaults.standardUserDefaults stringForKey:kDefaultRoot];\n}\n- (void)setDefaultRoot:(NSString *)defaultRoot {\n    [NSUserDefaults.standardUserDefaults setObject:defaultRoot forKey:kDefaultRoot];\n}\n\n- (NSURL *)rootUrl:(NSString *)name {\n    return [RootsDir() URLByAppendingPathComponent:name];\n}\n\n- (void)syncFileProviderDomains {\n    if (self.updatingDomains) {\n        self.domainsNeedUpdate = YES;\n        return;\n    }\n    self.updatingDomains = YES;\n    self.domainsNeedUpdate = NO;\n\n    [NSFileProviderManager getDomainsWithCompletionHandler:^(NSArray<NSFileProviderDomain *> *domains, NSError *error) {\n        void (^onError)(NSError *error) = ^(NSError *error) {\n            if (error != nil)\n                NSLog(@\"error adjusting domains: %@\", error);\n        };\n        onError(error);\n        NSMutableOrderedSet<NSString *> *missingRoots = [self.roots mutableCopy];\n        for (NSFileProviderDomain *domain in domains) {\n            if ([missingRoots containsObject:domain.identifier]) {\n                [missingRoots removeObject:domain.identifier];\n            } else {\n                [NSFileManager.defaultManager removeItemAtURL:\n                 [NSFileProviderManager.defaultManager.documentStorageURL\n                  URLByAppendingPathComponent:domain.pathRelativeToDocumentStorage]\n                                                        error:nil];\n                [NSFileProviderManager removeDomain:domain completionHandler:onError];\n            }\n        }\n        for (NSString *rootId in missingRoots) {\n            [NSFileProviderManager addDomain:[[NSFileProviderDomain alloc] initWithIdentifier:rootId\n                                                                                  displayName:rootId\n                                                                pathRelativeToDocumentStorage:rootId]\n                           completionHandler:onError];\n        }\n        if (self.domainsNeedUpdate)\n            [self syncFileProviderDomains];\n        self.updatingDomains = NO;\n    }];\n}\n\n- (BOOL)accessInstanceVariablesDirectly {\n    return YES;\n}\n\nvoid root_progress_callback(void *cookie, double progress, const char *message, bool *should_cancel) {\n    id <ProgressReporter> reporter = (__bridge id<ProgressReporter>) cookie;\n    [reporter updateProgress:progress message:[NSString stringWithUTF8String:message]];\n    if ([reporter shouldCancel])\n        *should_cancel = true;\n}\n\n- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress {\n    NSAssert(![self.roots containsObject:name], @\"root already exists: %@\", name);\n    struct fakefsify_error fs_err;\n    NSURL *destination = [self rootUrl:name];\n    NSURL *tempDestination = [NSFileManager.defaultManager.temporaryDirectory\n                              URLByAppendingPathComponent:[NSProcessInfo.processInfo globallyUniqueString]];\n    if (tempDestination == nil)\n        return NO;\n    if (!fakefs_import(archive.fileSystemRepresentation,\n                       tempDestination.fileSystemRepresentation,\n                       &fs_err, (struct progress) {(__bridge void *) progress, root_progress_callback})) {\n        NSString *domain = NSPOSIXErrorDomain;\n        if (fs_err.type == ERR_SQLITE)\n            domain = @\"SQLite\";\n        *error = [NSError errorWithDomain:domain\n                                     code:fs_err.code\n                                 userInfo:@{NSLocalizedDescriptionKey:\n                                                [NSString stringWithFormat:@\"%s, line %d\", fs_err.message, fs_err.line]}];\n        if (fs_err.type == ERR_CANCELLED)\n            *error = nil;\n        free(fs_err.message);\n        [NSFileManager.defaultManager removeItemAtURL:tempDestination error:nil];\n        return NO;\n    }\n    if (![NSFileManager.defaultManager moveItemAtURL:tempDestination toURL:destination error:error])\n        return NO;\n\n    void (^addRoot)(void) = ^{\n        [[self mutableOrderedSetValueForKey:@\"roots\"] addObject:name];\n    };\n    if (!NSThread.isMainThread)\n        dispatch_sync(dispatch_get_main_queue(), addRoot);\n    else\n        addRoot();\n    return YES;\n}\n\n- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress {\n    NSAssert([self.roots containsObject:name], @\"trying to export a root that doesn't exist: %@\", name);\n    struct fakefsify_error fs_err;\n    if (!fakefs_export([self rootUrl:name].fileSystemRepresentation,\n                       archive.fileSystemRepresentation,\n                       &fs_err, (struct progress) {(__bridge void *) progress, root_progress_callback})) {\n        // TODO: dedup with above method\n        NSString *domain = NSPOSIXErrorDomain;\n        if (fs_err.type == ERR_SQLITE)\n            domain = @\"SQLite\";\n        *error = [NSError errorWithDomain:domain\n                                     code:fs_err.code\n                                 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:fs_err.message]}];\n        if (fs_err.type == ERR_CANCELLED)\n            *error = nil;\n        free(fs_err.message);\n        return NO;\n    }\n    return YES;\n}\n\n- (BOOL)destroyRootNamed:(NSString *)name error:(NSError **)error {\n    if ([name isEqualToString:self.defaultRoot]) {\n        *error = [NSError errorWithDomain:@\"iSH\" code:0 userInfo:@{NSLocalizedDescriptionKey: @\"Cannot delete the default filesystem\"}];\n        return NO;\n    }\n    NSAssert([self.roots containsObject:name], @\"root does not exist: %@\", name);\n    if (![NSFileManager.defaultManager removeItemAtURL:[self rootUrl:name] error:error])\n        return NO;\n    [[self mutableOrderedSetValueForKey:@\"roots\"] removeObject:name];\n    return YES;\n}\n\n- (BOOL)renameRoot:(NSString *)name toName:(NSString *)newName error:(NSError **)error {\n    if (name.length == 0) {\n        *error = [NSError errorWithDomain:@\"iSH\" code:0 userInfo:@{NSLocalizedDescriptionKey: @\"Filesystem name can't be empty\"}];\n        return NO;\n    }\n    if ([name containsString:@\"/\"]) {\n        *error = [NSError errorWithDomain:@\"iSH\" code:0 userInfo:@{NSLocalizedDescriptionKey: @\"Filesystem name can't contain /\"}];\n        return NO;\n    }\n    if ([name isEqualToString:@\".\"] || [name isEqualToString:@\"..\"]) {\n        *error = [NSError errorWithDomain:@\"iSH\" code:0 userInfo:@{NSLocalizedDescriptionKey: @\"Filesystem name can't be . or ..\"}];\n        return NO;\n    }\n    if ([name isEqualToString:self.defaultRoot]) {\n        *error = [NSError errorWithDomain:@\"iSH\" code:0 userInfo:@{NSLocalizedDescriptionKey: @\"Cannot rename the default filesystem\"}];\n        return NO;\n    }\n    NSAssert([self.roots containsObject:name], @\"root does not exist: %@\", name);\n    \n    if (![NSFileManager.defaultManager moveItemAtURL:[self rootUrl:name] toURL:[self rootUrl:newName] error:error])\n        return NO;\n    NSUInteger index = [self.roots indexOfObject:name];\n    [[self mutableOrderedSetValueForKey:@\"roots\"] replaceObjectAtIndex:index withObject:newName];\n    return YES;\n}\n\n+ (instancetype)instance {\n    static Roots *instance;\n    static dispatch_once_t token;\n    dispatch_once(&token, ^{\n        instance = [Roots new];\n    });\n    return instance;\n}\n\n@end\n"
  },
  {
    "path": "app/Roots.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"19455\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"6Vk-68-e3U\">\n    <device id=\"retina5_5\" orientation=\"landscape\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"19454\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Filesystems-->\n        <scene sceneID=\"h0d-Ft-vFa\">\n            <objects>\n                <tableViewController id=\"6Vk-68-e3U\" customClass=\"RootsTableViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"dYB-X7-Une\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"414\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" reuseIdentifier=\"Root\" textLabel=\"Mwz-4l-jfY\" style=\"IBUITableViewCellStyleDefault\" id=\"qSD-L1-cEX\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"49\" width=\"736\" height=\"43.666667938232422\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"qSD-L1-cEX\" id=\"7y5-yu-imS\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"706.33333333333337\" height=\"43.666667938232422\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Title\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"Mwz-4l-jfY\">\n                                            <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"678.33333333333337\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <segue destination=\"2wD-dq-vPG\" kind=\"show\" id=\"qmJ-r1-LRg\"/>\n                                </connections>\n                            </tableViewCell>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"disclosureIndicator\" indentationWidth=\"10\" reuseIdentifier=\"Default Root\" textLabel=\"c8w-0c-rgD\" detailTextLabel=\"s1e-Wg-AtO\" style=\"IBUITableViewCellStyleSubtitle\" id=\"o8d-j7-u1g\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"92.666667938232422\" width=\"736\" height=\"55.666667938232422\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"o8d-j7-u1g\" id=\"lIx-1n-IVb\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"706.33333333333337\" height=\"55.666667938232422\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Title\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"c8w-0c-rgD\">\n                                            <rect key=\"frame\" x=\"20\" y=\"8.9999999999999982\" width=\"33\" height=\"20.333333333333332\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Mounted at /\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"s1e-Wg-AtO\">\n                                            <rect key=\"frame\" x=\"20\" y=\"31.333333333333332\" width=\"71.666666666666671\" height=\"14.333333333333334\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"12\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <segue destination=\"2wD-dq-vPG\" kind=\"show\" id=\"21q-u2-n5O\"/>\n                                </connections>\n                            </tableViewCell>\n                        </prototypes>\n                        <sections/>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"6Vk-68-e3U\" id=\"Wwh-GM-zyQ\"/>\n                            <outlet property=\"delegate\" destination=\"6Vk-68-e3U\" id=\"E9t-5E-aSw\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Filesystems\" leftItemsSupplementBackButton=\"YES\" id=\"10D-7Y-shL\">\n                        <barButtonItem key=\"rightBarButtonItem\" title=\"Import\" id=\"8Bo-9f-BwE\">\n                            <connections>\n                                <action selector=\"importFilesystem:\" destination=\"6Vk-68-e3U\" id=\"wwh-UQ-gZ2\"/>\n                            </connections>\n                        </barButtonItem>\n                    </navigationItem>\n                    <simulatedNavigationBarMetrics key=\"simulatedTopBarMetrics\" prompted=\"NO\"/>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"5cl-ob-BIN\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-909\" y=\"145\"/>\n        </scene>\n        <!--Root Detail View Controller-->\n        <scene sceneID=\"9ux-Kg-zZq\">\n            <objects>\n                <tableViewController id=\"2wD-dq-vPG\" customClass=\"RootDetailViewController\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"static\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"bpq-zM-xfc\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"414\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"groupTableViewBackgroundColor\"/>\n                        <sections>\n                            <tableViewSection id=\"npR-Ok-6YD\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" textLabel=\"get-up-pHQ\" style=\"IBUITableViewCellStyleDefault\" id=\"hgx-e8-jKW\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"18\" width=\"736\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"hgx-e8-jKW\" id=\"2I1-Zq-g1z\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Name\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"get-up-pHQ\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"696\" height=\"43.666667938232422\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" textAlignment=\"right\" minimumFontSize=\"17\" clearButtonMode=\"always\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"W3r-9O-obS\">\n                                                    <rect key=\"frame\" x=\"95\" y=\"4\" width=\"621\" height=\"35.666666666666664\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                    <textInputTraits key=\"textInputTraits\" returnKeyType=\"done\"/>\n                                                    <connections>\n                                                        <action selector=\"nameChanged:\" destination=\"2wD-dq-vPG\" eventType=\"editingDidEnd\" id=\"UiC-KI-0BP\"/>\n                                                        <outlet property=\"delegate\" destination=\"2wD-dq-vPG\" id=\"LPx-zt-MaK\"/>\n                                                    </connections>\n                                                </textField>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"W3r-9O-obS\" firstAttribute=\"trailing\" secondItem=\"get-up-pHQ\" secondAttribute=\"trailing\" id=\"LlT-y2-ARA\"/>\n                                                <constraint firstItem=\"W3r-9O-obS\" firstAttribute=\"leading\" secondItem=\"2I1-Zq-g1z\" secondAttribute=\"leadingMargin\" constant=\"75\" id=\"WhI-jd-7Mo\"/>\n                                                <constraint firstItem=\"W3r-9O-obS\" firstAttribute=\"top\" secondItem=\"2I1-Zq-g1z\" secondAttribute=\"topMargin\" constant=\"-7\" id=\"dbG-gf-NBS\"/>\n                                                <constraint firstItem=\"W3r-9O-obS\" firstAttribute=\"centerY\" secondItem=\"get-up-pHQ\" secondAttribute=\"centerY\" id=\"il9-ms-FUZ\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"wKw-Vo-Xko\" style=\"IBUITableViewCellStyleDefault\" id=\"ukW-Tg-Uaz\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"61.666667938232422\" width=\"736\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"ukW-Tg-Uaz\" id=\"Inz-2O-tLV\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Browse Files\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"wKw-Vo-Xko\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"696\" height=\"43.666667938232422\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <color key=\"textColor\" systemColor=\"systemBlueColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"k5h-m7-MM6\" style=\"IBUITableViewCellStyleDefault\" id=\"pGO-Lo-Iqh\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"105.33333587646484\" width=\"736\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"pGO-Lo-Iqh\" id=\"UzY-uU-GCw\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Export\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"k5h-m7-MM6\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"696\" height=\"43.666667938232422\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <color key=\"textColor\" systemColor=\"systemBlueColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection footerTitle=\"This will restart the app.\" id=\"uz7-5p-jKp\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"jVu-zc-iaY\" style=\"IBUITableViewCellStyleDefault\" id=\"rs6-db-M98\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"185.00000381469729\" width=\"736\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"rs6-db-M98\" id=\"gOc-8S-WPH\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Boot From This Filesystem\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"jVu-zc-iaY\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"696\" height=\"43.666667938232422\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <color key=\"textColor\" systemColor=\"systemBlueColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection id=\"Xdw-Qu-Lzh\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"Afx-pj-PW3\" style=\"IBUITableViewCellStyleDefault\" id=\"8i7-Yk-j16\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"273.0000057220459\" width=\"736\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"8i7-Yk-j16\" id=\"xR2-MO-1Ck\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"Delete Filesystem\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"Afx-pj-PW3\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"696\" height=\"43.666667938232422\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <color key=\"textColor\" systemColor=\"systemRedColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                        </sections>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"2wD-dq-vPG\" id=\"6cF-cW-Bqa\"/>\n                            <outlet property=\"delegate\" destination=\"2wD-dq-vPG\" id=\"e0l-RA-io3\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" id=\"w52-SB-GoM\"/>\n                    <connections>\n                        <outlet property=\"deleteCell\" destination=\"8i7-Yk-j16\" id=\"eng-0G-hBA\"/>\n                        <outlet property=\"deleteLabel\" destination=\"Afx-pj-PW3\" id=\"CEx-GB-nBg\"/>\n                        <outlet property=\"nameField\" destination=\"W3r-9O-obS\" id=\"dPM-5Y-kJR\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"zBR-e1-fEw\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-131.8840579710145\" y=\"143.47826086956522\"/>\n        </scene>\n        <!--Progress Report View Controller-->\n        <scene sceneID=\"2Tm-WP-Box\">\n            <objects>\n                <viewController storyboardIdentifier=\"progress\" modalTransitionStyle=\"crossDissolve\" modalPresentationStyle=\"overFullScreen\" id=\"KZI-b6-Ikz\" customClass=\"ProgressReportViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Se6-aN-9KE\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"736\" height=\"414\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"2X7-vG-Bav\">\n                                <rect key=\"frame\" x=\"118\" y=\"142\" width=\"500\" height=\"130\"/>\n                                <subviews>\n                                    <visualEffectView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"fRw-kV-GMw\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"500\" height=\"130\"/>\n                                        <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"X5o-tI-i04\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"500\" height=\"130\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                        </view>\n                                        <blurEffect style=\"extraLight\"/>\n                                    </visualEffectView>\n                                    <stackView opaque=\"NO\" contentMode=\"scaleToFill\" axis=\"vertical\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kaW-sc-dJA\">\n                                        <rect key=\"frame\" x=\"20\" y=\"20\" width=\"460\" height=\"60\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"middleTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"uls-2R-fu1\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"460\" height=\"20.333333333333332\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"middleTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QK2-Aw-TuX\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"30.333333333333346\" width=\"460\" height=\"15.666666666666668\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"13\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <progressView opaque=\"NO\" contentMode=\"scaleToFill\" verticalHuggingPriority=\"750\" progress=\"0.5\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"G6p-Tb-Jdw\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"56\" width=\"460\" height=\"4\"/>\n                                            </progressView>\n                                        </subviews>\n                                    </stackView>\n                                    <stackView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"dPf-45-cvF\" userLabel=\"Buttons Stack View\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"90\" width=\"500\" height=\"30\"/>\n                                        <subviews>\n                                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Uvf-lb-Web\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"500\" height=\"30\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"15\"/>\n                                                <state key=\"normal\" title=\"Cancel\"/>\n                                                <connections>\n                                                    <action selector=\"cancel:\" destination=\"KZI-b6-Ikz\" eventType=\"touchUpInside\" id=\"qYx-d0-Bt9\"/>\n                                                </connections>\n                                            </button>\n                                        </subviews>\n                                    </stackView>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"fRw-kV-GMw\" firstAttribute=\"leading\" secondItem=\"2X7-vG-Bav\" secondAttribute=\"leading\" id=\"0ku-0m-ybR\"/>\n                                    <constraint firstAttribute=\"width\" constant=\"270\" id=\"1Wv-C6-zec\">\n                                        <variation key=\"widthClass=regular\" constant=\"500\"/>\n                                    </constraint>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"fRw-kV-GMw\" secondAttribute=\"trailing\" id=\"Q3p-TZ-7yO\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"dPf-45-cvF\" secondAttribute=\"trailing\" id=\"TAx-BY-2Ot\"/>\n                                    <constraint firstItem=\"dPf-45-cvF\" firstAttribute=\"leading\" secondItem=\"2X7-vG-Bav\" secondAttribute=\"leading\" id=\"TXU-sz-q8H\"/>\n                                    <constraint firstItem=\"kaW-sc-dJA\" firstAttribute=\"top\" secondItem=\"2X7-vG-Bav\" secondAttribute=\"top\" constant=\"20\" id=\"V7X-IN-6rE\"/>\n                                    <constraint firstItem=\"fRw-kV-GMw\" firstAttribute=\"top\" secondItem=\"2X7-vG-Bav\" secondAttribute=\"top\" id=\"inx-IG-lJ3\"/>\n                                    <constraint firstItem=\"kaW-sc-dJA\" firstAttribute=\"leading\" secondItem=\"2X7-vG-Bav\" secondAttribute=\"leading\" constant=\"20\" id=\"l5t-QP-ZpO\"/>\n                                    <constraint firstItem=\"dPf-45-cvF\" firstAttribute=\"top\" secondItem=\"kaW-sc-dJA\" secondAttribute=\"bottom\" constant=\"10\" id=\"mVP-ot-7GR\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"dPf-45-cvF\" secondAttribute=\"bottom\" constant=\"10\" id=\"nzx-Po-AXk\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"fRw-kV-GMw\" secondAttribute=\"bottom\" id=\"vIb-Jh-Roo\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"kaW-sc-dJA\" secondAttribute=\"trailing\" constant=\"20\" id=\"zVd-X8-Plf\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"b5D-lf-9zV\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.20283069349315069\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"2X7-vG-Bav\" firstAttribute=\"centerX\" secondItem=\"Se6-aN-9KE\" secondAttribute=\"centerX\" id=\"LZj-qB-ZRG\"/>\n                            <constraint firstItem=\"2X7-vG-Bav\" firstAttribute=\"centerY\" secondItem=\"Se6-aN-9KE\" secondAttribute=\"centerY\" id=\"mQi-SO-SQX\"/>\n                        </constraints>\n                    </view>\n                    <value key=\"contentSizeForViewInPopover\" type=\"size\" width=\"100\" height=\"100\"/>\n                    <connections>\n                        <outlet property=\"backdrop\" destination=\"fRw-kV-GMw\" id=\"dZp-lQ-c12\"/>\n                        <outlet property=\"bar\" destination=\"G6p-Tb-Jdw\" id=\"8cY-YK-FAJ\"/>\n                        <outlet property=\"cancelButton\" destination=\"Uvf-lb-Web\" id=\"fJC-F3-qiT\"/>\n                        <outlet property=\"popupView\" destination=\"2X7-vG-Bav\" id=\"1DC-WM-4JH\"/>\n                        <outlet property=\"statusLabel\" destination=\"QK2-Aw-TuX\" id=\"1LM-hx-ZjX\"/>\n                        <outlet property=\"titleLabel\" destination=\"uls-2R-fu1\" id=\"m0x-lG-u1k\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"Sy2-bR-wDy\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"541.875\" y=\"144.71830985915494\"/>\n        </scene>\n    </scenes>\n    <inferredMetricsTieBreakers>\n        <segue reference=\"21q-u2-n5O\"/>\n    </inferredMetricsTieBreakers>\n    <resources>\n        <systemColor name=\"groupTableViewBackgroundColor\">\n            <color red=\"0.94901960784313721\" green=\"0.94901960784313721\" blue=\"0.96862745098039216\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n        <systemColor name=\"systemBlueColor\">\n            <color red=\"0.0\" green=\"0.47843137254901963\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n        <systemColor name=\"systemRedColor\">\n            <color red=\"1\" green=\"0.23137254901960785\" blue=\"0.18823529411764706\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "app/RootsTableViewController.h",
    "content": "//\n//  RootsTableViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 6/7/20.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface RootsTableViewController : UITableViewController <UIDocumentPickerDelegate>\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/RootsTableViewController.m",
    "content": "//\n//  RootsTableViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 6/7/20.\n//\n\n#import \"Roots.h\"\n#import \"RootsTableViewController.h\"\n#import \"ProgressReportViewController.h\"\n#import \"UIApplication+OpenURL.h\"\n#import \"UIViewController+Extras.h\"\n#import \"NSObject+SaneKVO.h\"\n\n@interface RootsTableViewController ()\n@end\n\n@interface RootDetailViewController : UITableViewController <UIDocumentPickerDelegate, UITextFieldDelegate>\n\n@property (nonatomic) NSString *rootName;\n@property (nonatomic) NSURL *exportURL;\n\n@property (weak, nonatomic) IBOutlet UITextField *nameField;\n@property (weak, nonatomic) IBOutlet UILabel *deleteLabel;\n@property (weak, nonatomic) IBOutlet UITableViewCell *deleteCell;\n\n@end\n\n@implementation RootsTableViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    [Roots.instance observe:@[@\"roots\", @\"defaultRoot\"]\n                    options:0 owner:self usingBlock:^(typeof(self) self) {\n        [self.tableView reloadData];\n    }];\n}\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {\n    return 1;\n}\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    return Roots.instance.roots.count;\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    NSString *ident = @\"Root\";\n    if ([Roots.instance.roots[indexPath.row] isEqual:Roots.instance.defaultRoot])\n        ident = @\"Default Root\";\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ident forIndexPath:indexPath];\n    cell.textLabel.text = Roots.instance.roots[indexPath.row];\n    return cell;\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\n}\n\n- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {\n    RootDetailViewController *vc = segue.destinationViewController;\n    vc.rootName = Roots.instance.roots[self.tableView.indexPathForSelectedRow.row];\n}\n\n- (IBAction)importFilesystem:(id)sender {\n    UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc]\n                                              initWithDocumentTypes:@[@\"public.tar-archive\", @\"org.gnu.gnu-zip-archive\"]\n                                              inMode:UIDocumentPickerModeImport];\n    [self presentViewController:picker animated:YES completion:nil];\n    if (@available(iOS 13, *)) {\n        picker.shouldShowFileExtensions = YES;\n    }\n    picker.delegate = self;\n}\n\n- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {\n    NSAssert(urls.count == 1, @\"somehow picked multiple documents\");\n    NSURL *url = urls.firstObject;\n    NSString *fileName = url.lastPathComponent.stringByDeletingPathExtension;\n    if ([fileName hasSuffix:@\".tar\"])\n        fileName = fileName.stringByDeletingPathExtension;\n    unsigned i = 2;\n    NSString *name = fileName;\n    while ([Roots.instance.roots containsObject:name]) {\n        name = [NSString stringWithFormat:@\"%@ %u\", fileName, i++];\n    }\n\n    ProgressReportViewController *progressVC = [self.storyboard instantiateViewControllerWithIdentifier:@\"progress\"];\n    progressVC.title = [NSString stringWithFormat:@\"Importing %@\", name];\n    [self presentViewController:progressVC animated:YES completion:nil];\n\n    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{\n        NSError *error;\n        [url startAccessingSecurityScopedResource];\n        BOOL success = [Roots.instance importRootFromArchive:url name:name error:&error progressReporter:progressVC];\n        [url stopAccessingSecurityScopedResource];\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [progressVC dismissViewControllerAnimated:YES completion:^{\n                if (!success && error != nil)\n                    [self presentError:error title:@\"Import failed\"];\n            }];\n        });\n    });\n}\n\n@end\n\n@implementation RootDetailViewController\n\n- (void)viewWillAppear:(BOOL)animated {\n    self.nameField.text = self.rootName;\n    [self update];\n}\n\n- (void)update {\n    self.navigationItem.title = self.rootName;\n    self.nameField.enabled = !self.isDefaultRoot;\n    self.nameField.clearButtonMode = self.isDefaultRoot ? UITextFieldViewModeNever : UITextFieldViewModeAlways;\n    self.deleteLabel.enabled = !self.isDefaultRoot;\n    self.deleteCell.selectionStyle = !self.isDefaultRoot ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone;\n    [self.tableView reloadData];\n}\n\n- (IBAction)nameChanged:(id)sender {\n    NSString *newName = self.nameField.text;\n    NSError *err;\n    if (![Roots.instance renameRoot:self.rootName toName:newName error:&err]) {\n        self.nameField.text = self.rootName;\n        [self presentError:err title:@\"Rename failed\"];\n        return;\n    }\n    self.rootName = newName;\n    [self update];\n}\n\n- (BOOL)textFieldShouldReturn:(UITextField *)textField {\n    [textField resignFirstResponder];\n    return NO;\n}\n\n- (BOOL)isDefaultRoot {\n    return [self.rootName isEqualToString:Roots.instance.defaultRoot];\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    if (section == 2) { // delete\n        if (self.isDefaultRoot)\n            return @\"This filesystem can't be deleted because it's currently mounted as the root.\";\n    }\n    return [super tableView:tableView titleForFooterInSection:section];\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (indexPath.section == 0 && indexPath.row == 1)\n        [self browseFiles];\n    if (indexPath.section == 0 && indexPath.row == 2)\n        [self exportFilesystem];\n    if (indexPath.section == 1 && indexPath.row == 0)\n        [self bootThis];\n    if (indexPath.section == 2 && indexPath.row == 0)\n        [self deleteFilesystem];\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\n}\n\n- (void)browseFiles {\n    NSURL *url = [NSFileProviderManager.defaultManager.documentStorageURL URLByAppendingPathComponent:self.rootName];\n    NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];\n    components.scheme = @\"shareddocuments\";\n    [UIApplication openURL:components.string];\n}\n\n- (void)exportFilesystem {\n    self.exportURL = [[NSFileManager.defaultManager.temporaryDirectory\n                       URLByAppendingPathComponent:[NSProcessInfo.processInfo globallyUniqueString]]\n                      URLByAppendingPathComponent:[NSString stringWithFormat:@\"%@.tar.gz\", self.rootName]];\n    [NSFileManager.defaultManager createDirectoryAtURL:self.exportURL.URLByDeletingLastPathComponent\n                           withIntermediateDirectories:YES\n                                            attributes:nil\n                                                 error:nil];\n    ProgressReportViewController *progressVC = [self.storyboard instantiateViewControllerWithIdentifier:@\"progress\"];\n    progressVC.title = [NSString stringWithFormat:@\"Exporting %@\", self.rootName];\n    [self presentViewController:progressVC animated:YES completion:nil];\n\n    // witness the callback hell\n    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{\n        NSError *err;\n        BOOL success = [Roots.instance exportRootNamed:self.rootName toArchive:self.exportURL error:&err progressReporter:progressVC];\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [progressVC dismissViewControllerAnimated:YES completion:^{\n                if (!success) {\n                    if (err != nil)\n                        [self presentError:err title:@\"Export failed\"];\n                    return;\n                }\n\n                UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc]\n                                                          initWithURL:self.exportURL\n                                                          inMode:UIDocumentPickerModeExportToService];\n                picker.delegate = self;\n                if (@available(iOS 13, *)) {\n                    picker.shouldShowFileExtensions = YES;\n                }\n                [self presentViewController:picker animated:YES completion:nil];\n            }];\n        });\n    });\n}\n\n- (void)setExportURL:(NSURL *)exportURL {\n    [NSFileManager.defaultManager removeItemAtURL:_exportURL.URLByDeletingLastPathComponent error:nil];\n    _exportURL = exportURL;\n}\n\n- (void)bootThis {\n    Roots.instance.defaultRoot = self.rootName;\n    exit(0);\n}\n\n- (void)deleteFilesystem {\n    if (self.isDefaultRoot)\n        return;\n    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@\"Really delete?\"\n                                                                   message:@\"I can't be bothered to implement any undo or regret UI so this is irreversable.\"\n                                                            preferredStyle:UIAlertControllerStyleAlert];\n    [alert addAction:[UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil]];\n    [alert addAction:[UIAlertAction actionWithTitle:@\"Delete\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {\n        NSError *error;\n        if (![Roots.instance destroyRootNamed:self.rootName error:&error]) {\n            [self presentError:error title:@\"Delete failed\"];\n        } else {\n            [self.navigationController popViewControllerAnimated:YES];\n        }\n    }]];\n    [self presentViewController:alert animated:YES completion:nil];\n}\n\n- (void)dealloc {\n    self.exportURL = nil; // get it deleted\n}\n\n@end\n"
  },
  {
    "path": "app/SceneDelegate.h",
    "content": "//\n//  SceneDelegate.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/26/19.\n//\n\n#import <UIKit/UIKit.h>\n#import \"TerminalViewController.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\nextern TerminalViewController *currentTerminalViewController;\n\nAPI_AVAILABLE(ios(13))\n@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>\n\n@property (nonatomic) UIWindow *window;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/SceneDelegate.m",
    "content": "//\n//  SceneDelegate.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/26/19.\n//\n\n#import \"SceneDelegate.h\"\n#import \"AboutViewController.h\"\n\nTerminalViewController *currentTerminalViewController = NULL;\n\n@interface SceneDelegate ()\n\n@property NSString *terminalUUID;\n\n@end\n\nstatic NSString *const TerminalUUID = @\"TerminalUUID\";\n\n@implementation SceneDelegate\n\n- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {\n    if ([NSUserDefaults.standardUserDefaults boolForKey:@\"recovery\"]) {\n        UINavigationController *vc = [[UIStoryboard storyboardWithName:@\"About\" bundle:nil] instantiateInitialViewController];\n        AboutViewController *avc = (AboutViewController *) vc.topViewController;\n        avc.recoveryMode = YES;\n        self.window.rootViewController = vc;\n        return;\n    }\n\n    TerminalViewController *vc = (TerminalViewController *) self.window.rootViewController;\n    vc.sceneSession = session;\n    if (session.stateRestorationActivity == nil) {\n        [vc startNewSession];\n    } else {\n        self.terminalUUID = session.stateRestorationActivity.userInfo[TerminalUUID];\n        [vc reconnectSessionFromTerminalUUID:\n         [[NSUUID alloc] initWithUUIDString:self.terminalUUID]];\n    }\n}\n\n- (NSUserActivity *)stateRestorationActivityForScene:(UIScene *)scene {\n    NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:@\"app.ish.scene\"];\n    TerminalViewController *vc = (TerminalViewController *) self.window.rootViewController;\n    if ([vc isKindOfClass:TerminalViewController.class]) {\n        self.terminalUUID = vc.sessionTerminalUUID.UUIDString;\n        if (self.terminalUUID != nil) {\n            [activity addUserInfoEntriesFromDictionary:@{TerminalUUID: self.terminalUUID}];\n        }\n    }\n    return activity;\n}\n\n- (void)sceneDidBecomeActive:(UIScene *)scene {\n    TerminalViewController *terminalViewController = (TerminalViewController *) self.window.rootViewController;;\n    currentTerminalViewController = terminalViewController;\n}\n\n- (void)sceneWillResignActive:(UIScene *)scene {\n    TerminalViewController *terminalViewController = (TerminalViewController *) self.window.rootViewController;\n\n    if (currentTerminalViewController == terminalViewController) {\n        currentTerminalViewController = NULL;\n    }\n}\n\n@end\n"
  },
  {
    "path": "app/ScrollbarView.h",
    "content": "//\n//  ScrollbarView.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/2/19.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface ScrollbarView : UIScrollView\n\n@property (nonatomic, nullable) UIView *contentView;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/ScrollbarView.m",
    "content": "//\n//  ScrollbarView.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/2/19.\n//\n\n#import \"ScrollbarView.h\"\n\n@class ScrollbarViewDelegate;\n\n@interface ScrollbarView ()\n\n@property CGPoint contentViewOrigin;\n@property ScrollbarViewDelegate *outerDelegate;\n\n@end\n\n@interface ScrollbarViewDelegate : NSObject <UIScrollViewDelegate>\n@property (weak) id<UIScrollViewDelegate> innerDelegate;\n@end\n@implementation ScrollbarViewDelegate\n\n- (void)scrollViewDidScroll:(ScrollbarView *)scrollView {\n    CGRect frame = scrollView.contentView.frame;\n    frame.origin.x = scrollView.contentOffset.x + scrollView.contentViewOrigin.x;\n    frame.origin.y = scrollView.contentOffset.y + scrollView.contentViewOrigin.y;\n    scrollView.contentView.frame = frame;\n    [self.innerDelegate scrollViewDidScroll:scrollView];\n}\n\n- (id)forwardingTargetForSelector:(SEL)aSelector {\n    if ([self.innerDelegate respondsToSelector:aSelector])\n        return self.innerDelegate;\n    return [super forwardingTargetForSelector:aSelector];\n}\n\n@end\n\n@implementation ScrollbarView\n\n- (instancetype)initWithFrame:(CGRect)frame {\n    if (self = [super initWithFrame:frame]) {\n        self.outerDelegate = [ScrollbarViewDelegate new];\n        super.delegate = self.outerDelegate;\n    }\n    return self;\n}\n\n- (void)setContentView:(UIView *)contentView {\n    _contentView = contentView;\n    self.contentViewOrigin = contentView.frame.origin;\n}\n\n- (id<UIScrollViewDelegate>)delegate {\n    return self.outerDelegate.innerDelegate;\n}\n\n- (void)setDelegate:(id<UIScrollViewDelegate>)delegate {\n    self.outerDelegate.innerDelegate = delegate;\n}\n\n@end\n"
  },
  {
    "path": "app/Settings.bundle/Root.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreferenceSpecifiers</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>Type</key>\n\t\t\t<string>PSGroupSpecifier</string>\n\t\t\t<key>FooterText</key>\n\t\t\t<string>Opens the app straight to the settings menu. Useful if you changed anything there and need to change it back but the app won&apos;t start.</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>Type</key>\n\t\t\t<string>PSToggleSwitchSpecifier</string>\n\t\t\t<key>Title</key>\n\t\t\t<string>Recovery Mode</string>\n\t\t\t<key>Key</key>\n\t\t\t<string>recovery</string>\n\t\t\t<key>DefaultValue</key>\n\t\t\t<false/>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/StaticLib.xcconfig",
    "content": "SKIP_INSTALL = YES\nEXECUTABLE_PREFIX =\nPRODUCT_NAME = $(TARGET_NAME)\nVERSIONING_SYSTEM =\n"
  },
  {
    "path": "app/StaticLibLinux.xcconfig",
    "content": "#include \"StaticLib.xcconfig\"\n\nHEADER_SEARCH_PATHS = $(SRCROOT)/deps/linux/arch/ish/include $(MESON_BUILD_DIR)/deps/linux/arch/ish/include/generated $(SRCROOT)/deps/linux/include $(MESON_BUILD_DIR)/deps/linux/include $(SRCROOT)/deps/linux/arch/ish/include/uapi $(MESON_BUILD_DIR)/deps/linux/arch/ish/include/generated/uapi $(SRCROOT)/deps/linux/include/uapi $(MESON_BUILD_DIR)/deps/linux/arch/ish/include/generated/uapi $(SRCROOT)\nGCC_PREPROCESSOR_DEFINITIONS = __KERNEL__\nOTHER_CFLAGS = -U__weak -include linux/kconfig.h -include linux/compiler_types.h -Wno-gnu-variable-sized-type-not-at-end -Wno-conditional-uninitialized\n// disable modules because it results in clashes between linux and clang headers\nCLANG_ENABLE_MODULES = NO\n// these warnings are tripped by linux\nCLANG_WARN_DOCUMENTATION_COMMENTS = NO\nGCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO\nGCC_WARN_64_TO_32_BIT_CONVERSION = NO\n"
  },
  {
    "path": "app/StaticLibLinuxUser.xcconfig",
    "content": "#include \"StaticLib.xcconfig\"\n\nHEADER_SEARCH_PATHS = $(SRCROOT)/deps/linux/arch/ish/include $(SRCROOT)/deps/linux/include $(MESON_BUILD_DIR)/deps/linux/include $(SRCROOT)\nOTHER_CFLAGS = -include user.h -include linux/kconfig.h\n"
  },
  {
    "path": "app/Terminal.h",
    "content": "//\n//  Terminal.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/18/17.\n//\n\n#import <UIKit/UIKit.h>\n#import <WebKit/WebKit.h>\n\nstruct tty;\n\n@interface Terminal : NSObject\n\n+ (Terminal *)terminalWithType:(int)type number:(int)number;\n#if !ISH_LINUX\n// Returns a strong struct tty and a Terminal that has a weak reference to the same tty\n+ (Terminal *)createPseudoTerminal:(struct tty **)tty;\n#endif\n\n+ (Terminal *)terminalWithUUID:(NSUUID *)uuid;\n@property (readonly) NSUUID *uuid;\n\n+ (void)convertCommand:(NSArray<NSString *> *)command toArgs:(char *)argv limitSize:(size_t)maxSize;\n\n- (int)sendOutput:(const void *)buf length:(int)len;\n- (void)sendInput:(NSData *)input;\n\n- (NSString *)arrow:(char)direction;\n\n// Make this terminal no longer be the singleton terminal with its type and number. Will happen eventually if all references go away, but sometimes you want it to happen now.\n- (void)destroy;\n\n@property (readonly) WKWebView *webView;\n@property (nonatomic) BOOL enableVoiceOverAnnounce;\n// Use KVO on this\n@property (readonly) BOOL loaded;\n\n@end\n\nextern struct tty_driver ios_console_driver;\n"
  },
  {
    "path": "app/Terminal.m",
    "content": "//\n//  Terminal.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/18/17.\n//\n\n#import \"Terminal.h\"\n#import \"DelayedUITask.h\"\n#import \"UserPreferences.h\"\n#include \"LinuxInterop.h\"\n#include \"fs/devices.h\"\n#include \"fs/tty.h\"\n#include \"fs/devices.h\"\n\nextern struct tty_driver ios_pty_driver;\n\n#if !ISH_LINUX\ntypedef struct tty *tty_t;\n#else\ntypedef struct linux_tty *tty_t;\n#endif\n\n@interface Terminal () <WKScriptMessageHandler> {\n#if !ISH_LINUX\n    lock_t _dataLock;\n    cond_t _dataConsumed;\n#endif\n}\n\n@property BOOL loaded;\n@property (nonatomic) tty_t tty;\n// lock with dataLock for !linux and @synchronized(self) for linux\n@property (nonatomic) NSMutableData *pendingData;\n// sending output is an asynchronous thing due to javascript, this is used to ensure it doesn't happen twice at once\n@property (nonatomic) BOOL outputInProgress;\n\n@property DelayedUITask *refreshTask;\n@property DelayedUITask *scrollToBottomTask;\n\n@property BOOL applicationCursor;\n\n@property NSNumber *terminalsKey;\n@property NSUUID *uuid;\n\n@end\n\n@interface CustomWebView : WKWebView\n@end\n@implementation CustomWebView\n- (BOOL)becomeFirstResponder {\n    if (@available(iOS 13.4, *)) {\n        return [super becomeFirstResponder];\n    }\n    return NO;\n}\n\n- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {\n    if (action == @selector(copy:) || action == @selector(paste:)) {\n        return NO;\n    }\n    return [super canPerformAction:action withSender:sender];\n}\n@end\n\n@implementation Terminal\n@synthesize webView = _webView;\n\nstatic const int BUF_SIZE = 1<<14;\n\nstatic NSMapTable<NSNumber *, Terminal *> *terminals;\nstatic NSMapTable<NSUUID *, Terminal *> *terminalsByUUID;\n\n- (instancetype)initWithType:(int)type number:(int)num {\n    @synchronized (Terminal.class) {\n        self.terminalsKey = @(dev_make(type, num));\n        Terminal *terminal = [terminals objectForKey:self.terminalsKey];\n        if (terminal)\n            return terminal;\n\n        if (self = [super init]) {\n            self.pendingData = [[NSMutableData alloc] initWithCapacity:BUF_SIZE];\n            self.refreshTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(refresh)];\n            self.scrollToBottomTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(scrollToBottom)];\n#if !ISH_LINUX\n            lock_init(&_dataLock);\n            cond_init(&_dataConsumed);\n#endif\n\n            [terminals setObject:self forKey:self.terminalsKey];\n            self.uuid = [NSUUID UUID];\n            [terminalsByUUID setObject:self forKey:self.uuid];\n        }\n        return self;\n    }\n}\n\n- (WKWebView *)webView {\n    if (_webView == nil) {\n        WKWebViewConfiguration *config = [WKWebViewConfiguration new];\n        [config.userContentController addScriptMessageHandler:self name:@\"load\"];\n        [config.userContentController addScriptMessageHandler:self name:@\"log\"];\n        [config.userContentController addScriptMessageHandler:self name:@\"sendInput\"];\n        [config.userContentController addScriptMessageHandler:self name:@\"resize\"];\n        [config.userContentController addScriptMessageHandler:self name:@\"propUpdate\"];\n        // Make the web view really big so that if a program tries to write to the terminal before it's displayed, the text probably won't wrap too badly.\n        CGRect webviewSize = CGRectMake(0, 0, 10000, 10000);\n        _webView = [[CustomWebView alloc] initWithFrame:webviewSize configuration:config];\n        if (@available(macOS 13.3, iOS 16.4, tvOS 16.4, *))\n            _webView.inspectable = YES;\n        _webView.scrollView.scrollEnabled = NO;\n        NSURL *xtermHtmlFile = [NSBundle.mainBundle URLForResource:@\"term\" withExtension:@\"html\"];\n        [_webView loadFileURL:xtermHtmlFile allowingReadAccessToURL:xtermHtmlFile];\n    }\n    return _webView;\n}\n\n#if !ISH_LINUX\n+ (Terminal *)createPseudoTerminal:(struct tty **)tty {\n    *tty = pty_open_fake(&ios_pty_driver);\n    if (IS_ERR(*tty))\n        return nil;\n    return (__bridge Terminal *) (*tty)->data;\n}\n#endif\n\n- (void)setTty:(tty_t)tty {\n    @synchronized (self) {\n        _tty = tty;\n    }\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [self syncWindowSize];\n    });\n}\n\n- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {\n    if ([message.name isEqualToString:@\"load\"]) {\n        self.loaded = YES;\n        [self.refreshTask schedule];\n        // make sure this setting works if it's set before loading\n        self.enableVoiceOverAnnounce = self.enableVoiceOverAnnounce;\n    } else if ([message.name isEqualToString:@\"log\"]) {\n        NSLog(@\"%@\", message.body);\n    } else if ([message.name isEqualToString:@\"sendInput\"]) {\n        NSData *data = [message.body dataUsingEncoding:NSUTF8StringEncoding];\n        [self sendInput:data];\n    } else if ([message.name isEqualToString:@\"resize\"]) {\n        [self syncWindowSize];\n    } else if ([message.name isEqualToString:@\"propUpdate\"]) {\n        [self setValue:message.body[1] forKey:message.body[0]];\n    }\n}\n\n- (void)syncWindowSize {\n    [self.webView evaluateJavaScript:@\"exports.getSize()\" completionHandler:^(NSArray<NSNumber *> *dimensions, NSError *error) {\n        int cols = dimensions[0].intValue;\n        int rows = dimensions[1].intValue;\n        if (self.tty == NULL)\n            return;\n#if !ISH_LINUX\n        lock(&self.tty->lock);\n        tty_set_winsize(self.tty, (struct winsize_) {.col = cols, .row = rows});\n        unlock(&self.tty->lock);\n#else\n        async_do_in_workqueue(^{\n            self->_tty->ops->resize(self->_tty, cols, rows);\n        });\n#endif\n    }];\n}\n\n- (void)setEnableVoiceOverAnnounce:(BOOL)enableVoiceOverAnnounce {\n    _enableVoiceOverAnnounce = enableVoiceOverAnnounce;\n    [self.webView evaluateJavaScript:[NSString stringWithFormat:@\"term.setAccessibilityEnabled(%@)\",\n                                      enableVoiceOverAnnounce ? @\"true\" : @\"false\"]\n                   completionHandler:nil];\n}\n\n- (int)sendOutput:(const void *)buf length:(int)len {\n#if !ISH_LINUX\n    lock(&_dataLock);\n    if (!NSThread.isMainThread) {\n        // The main thread is the only one that can unblock this, so sleeping here would be a deadlock.\n        // The only reason for this to be called on the main thread is if input is echoed.\n        while (_pendingData.length > BUF_SIZE)\n            wait_for_ignore_signals(&_dataConsumed, &_dataLock, NULL);\n    }\n    [_pendingData appendData:[NSData dataWithBytes:buf length:len]];\n    [self.refreshTask schedule];\n    unlock(&_dataLock);\n#else\n    @synchronized (self) {\n        int room = [self roomForOutput];\n        if (len > room)\n            len = room;\n        if (len > 0) {\n            [_pendingData appendData:[NSData dataWithBytes:buf length:len]];\n            [_refreshTask schedule];\n        }\n    }\n#endif\n    return len;\n}\n\n#if ISH_LINUX\n- (int)roomForOutput {\n    @synchronized (self) {\n        if (_pendingData.length > BUF_SIZE)\n            return 0;\n        return BUF_SIZE - (int) _pendingData.length;\n    }\n}\n#endif\n\n- (void)sendInput:(NSData *)input {\n    if (self.tty == NULL)\n        return;\n#if !ISH_LINUX\n    tty_input(self.tty, input.bytes, input.length, 0);\n#else\n    async_do_in_workqueue(^{\n        NSData *inputRef = input;\n        self.tty->ops->send_input(self.tty, inputRef.bytes, inputRef.length);\n    });\n#endif\n    [self.webView evaluateJavaScript:@\"exports.setUserGesture()\" completionHandler:nil];\n    [self.scrollToBottomTask schedule];\n}\n\n- (void)scrollToBottom {\n    [self.webView evaluateJavaScript:@\"exports.scrollToBottom()\" completionHandler:nil];\n}\n\n- (NSString *)arrow:(char)direction {\n    return [NSString stringWithFormat:@\"\\x1b%c%c\", self.applicationCursor ? 'O' : '[', direction];\n}\n\n- (void)refresh {\n    if (!self.loaded)\n        return;\n\n#if !ISH_LINUX\n    lock(&_dataLock);\n    if (_outputInProgress) {\n        [self.refreshTask schedule];\n        unlock(&_dataLock);\n        return;\n    }\n    NSData *data = _pendingData;\n    _pendingData = [[NSMutableData alloc] initWithCapacity:BUF_SIZE];\n    _outputInProgress = YES;\n    notify(&self->_dataConsumed);\n    unlock(&_dataLock);\n#else\n    NSData *data;\n    @synchronized (self) {\n        if (_outputInProgress) {\n            [self.refreshTask schedule];\n            return;\n        }\n        data = _pendingData;\n        _pendingData = [[NSMutableData alloc] initWithCapacity:BUF_SIZE];\n        _outputInProgress = YES;\n        if (self->_tty)\n            async_do_in_irq(^{\n                self->_tty->ops->can_output(self->_tty);\n            });\n    }\n#endif\n\n    NSString *dataString = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSISOLatin1StringEncoding];\n    // escape for javascript. only have to worry about the first 256 codepoints, because of the latin-1 encoding.\n    dataString = [dataString stringByReplacingOccurrencesOfString:@\"\\\\\" withString:@\"\\\\\\\\\"];\n    dataString = [dataString stringByReplacingOccurrencesOfString:@\"\\r\" withString:@\"\\\\r\"];\n    dataString = [dataString stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\"\\\\n\"];\n    dataString = [dataString stringByReplacingOccurrencesOfString:@\"\\\"\" withString:@\"\\\\\\\"\"];\n    NSString *jsToEvaluate = [NSString stringWithFormat:@\"exports.write(\\\"%@\\\")\", dataString];\n    [self.webView evaluateJavaScript:jsToEvaluate completionHandler:^(id result, NSError *error) {\n#if !ISH_LINUX\n        lock(&self->_dataLock);\n        self->_outputInProgress = NO;\n        unlock(&self->_dataLock);\n#else\n        @synchronized (self) {\n            self->_outputInProgress = NO;\n        }\n#endif\n        if (error != nil) {\n            NSLog(@\"error sending bytes to the terminal: %@\", error);\n            return;\n        }\n    }];\n}\n\n+ (void)convertCommand:(NSArray<NSString *> *)command toArgs:(char *)argv limitSize:(size_t)maxSize {\n    char *p = argv;\n    for (NSString *cmd in command) {\n        const char *c = cmd.UTF8String;\n        // Save space for the final NUL byte in argv\n        while (p < argv + maxSize - 1 && (*p++ = *c++));\n        // If we reach the end of the buffer, the last string still needs to be\n        // NUL terminated\n        *p = '\\0';\n    }\n    // Add the final NUL byte to argv\n    *++p = '\\0';\n}\n\n+ (Terminal *)terminalWithType:(int)type number:(int)number {\n    return [[Terminal alloc] initWithType:type number:number];\n}\n\n+ (Terminal *)terminalWithUUID:(NSUUID *)uuid {\n    @synchronized (Terminal.class) {\n        return [terminalsByUUID objectForKey:uuid];\n    }\n}\n\n- (void)destroy {\n    tty_t tty = self.tty;\n    if (tty != NULL) {\n#if !ISH_LINUX\n        if (tty != NULL) {\n            lock(&tty->lock);\n            tty_hangup(tty);\n            unlock(&tty->lock);\n        }\n#else\n        tty->ops->hangup(tty);\n#endif\n    }\n    @synchronized (Terminal.class) {\n        [terminals removeObjectForKey:self.terminalsKey];\n    }\n}\n\n+ (void)initialize {\n    if (self == Terminal.class) {\n        terminals = [NSMapTable strongToWeakObjectsMapTable];\n        terminalsByUUID = [NSMapTable strongToWeakObjectsMapTable];\n    }\n}\n\n@end\n\n#if ISH_LINUX\nnsobj_t Terminal_terminalWithType_number(int type, int number) {\n    return CFBridgingRetain([Terminal terminalWithType:type number:number]);\n}\nint Terminal_sendOutput_length(nsobj_t _self, const char *data, int size) {\n    return [(__bridge Terminal *) _self sendOutput:data length:size];\n}\nint Terminal_roomForOutput(nsobj_t _self) {\n    return [(__bridge Terminal *) _self roomForOutput];\n}\nvoid Terminal_setLinuxTTY(nsobj_t _self, struct linux_tty *tty) {\n    return [(__bridge Terminal *) _self setTty:tty];\n}\n#endif\n\n#if !ISH_LINUX\nstatic int ios_tty_init(struct tty *tty) {\n    // This is called with ttys_lock but that results in deadlock since the main thread can also acquire ttys_lock. So release it.\n    unlock(&ttys_lock);\n    void (^init_block)(void) = ^{\n        Terminal *terminal = [Terminal terminalWithType:tty->type number:tty->num];\n        tty->data = (void *) CFBridgingRetain(terminal);\n        terminal.tty = tty;\n    };\n    if ([NSThread isMainThread])\n        init_block();\n    else\n        dispatch_sync(dispatch_get_main_queue(), init_block);\n\n    lock(&ttys_lock);\n    return 0;\n}\n\nstatic int ios_tty_write(struct tty *tty, const void *buf, size_t len, bool blocking) {\n    Terminal *terminal = (__bridge Terminal *) tty->data;\n    return [terminal sendOutput:buf length:(int) len];\n}\n\nstatic void ios_tty_cleanup(struct tty *tty) {\n    Terminal *terminal = CFBridgingRelease(tty->data);\n    tty->data = NULL;\n    terminal.tty = NULL;\n}\n\nstruct tty_driver_ops ios_tty_ops = {\n    .init = ios_tty_init,\n    .write = ios_tty_write,\n    .cleanup = ios_tty_cleanup,\n};\nDEFINE_TTY_DRIVER(ios_console_driver, &ios_tty_ops, TTY_CONSOLE_MAJOR, 64);\nstruct tty_driver ios_pty_driver = {.ops = &ios_tty_ops};\n#endif\n"
  },
  {
    "path": "app/TerminalView.h",
    "content": "//\n//  TerminalView.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/3/17.\n//\n\n#import <UIKit/UIKit.h>\n#import \"Terminal.h\"\n\nenum OverrideAppearance {\n    OverrideAppearanceNone,\n    OverrideAppearanceLight,\n    OverrideAppearanceDark,\n};\n\n@interface TerminalView : UIView <UITextInput, WKScriptMessageHandler, UIScrollViewDelegate>\n\n@property IBInspectable (nonatomic) BOOL canBecomeFirstResponder;\n\n@property (nonatomic) CGFloat overrideFontSize;\n@property (readonly) CGFloat effectiveFontSize;\n@property (nonatomic) enum OverrideAppearance overrideAppearance;\n\n@property (nonatomic) UIKeyboardAppearance keyboardAppearance;\n\n@property (weak) IBOutlet UIInputView *inputAccessoryView;\n@property (weak) IBOutlet UIButton *controlKey;\n\n@property (nonatomic) Terminal *terminal;\n\n@end\n"
  },
  {
    "path": "app/TerminalView.m",
    "content": "//\n//  TerminalView.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/3/17.\n//\n\n#import \"ScrollbarView.h\"\n#import \"TerminalView.h\"\n#import \"UserPreferences.h\"\n#import \"UIApplication+OpenURL.h\"\n#import \"NSObject+SaneKVO.h\"\n\nstruct rowcol {\n    int row;\n    int col;\n};\n\n@interface WeakScriptMessageHandler : NSObject <WKScriptMessageHandler>\n@property (weak) id <WKScriptMessageHandler> handler;\n@end\n@implementation WeakScriptMessageHandler\n- (instancetype)initWithHandler:(id <WKScriptMessageHandler>)handler {\n    if (self = [super init]) {\n        self.handler = handler;\n    }\n    return self;\n}\n- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {\n    [self.handler userContentController:userContentController didReceiveScriptMessage:message];\n}\n@end\n\n@interface TerminalView ()\n\n@property (nonatomic) NSMutableArray<UIKeyCommand *> *keyCommands;\n@property ScrollbarView *scrollbarView;\n@property (nonatomic) BOOL terminalFocused;\n\n@property (nullable) NSString *markedText;\n@property (nullable) NSString *selectedText;\n@property UITextRange *markedRange;\n@property UITextRange *selectedRange;\n\n@property struct rowcol floatingCursor;\n@property CGSize floatingCursorSensitivity;\n@property CGSize actualFloatingCursorSensitivity;\n\n@end\n\n@implementation TerminalView\n@synthesize inputDelegate;\n@synthesize tokenizer;\n@synthesize canBecomeFirstResponder;\n\n- (void)awakeFromNib {\n    [super awakeFromNib];\n    self.inputAssistantItem.leadingBarButtonGroups = @[];\n    self.inputAssistantItem.trailingBarButtonGroups = @[];\n\n    ScrollbarView *scrollbarView = self.scrollbarView = [[ScrollbarView alloc] initWithFrame:self.bounds];\n    scrollbarView.delegate = self;\n    scrollbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;\n    scrollbarView.bounces = NO;\n    [self addSubview:scrollbarView];\n\n    UserPreferences *prefs = UserPreferences.shared;\n    [prefs observe:@[@\"capsLockMapping\", @\"optionMapping\", @\"backtickMapEscape\", @\"overrideControlSpace\"]\n           options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            self->_keyCommands = nil;\n        });\n    }];\n    [prefs observe:@[@\"colorScheme\", @\"fontFamily\", @\"fontSize\", @\"theme\", @\"cursorStyle\", @\"blinkCursor\"]\n           options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self _updateStyle];\n        });\n    }];\n\n    self.markedRange = [UITextRange new];\n    self.selectedRange = [UITextRange new];\n}\n\n- (void)dealloc {\n    self.terminal = nil;\n}\n\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {\n    if (object == _terminal) {\n        if (_terminal.loaded) {\n            [self installTerminalView];\n            [self _updateStyle];\n        }\n    }\n}\n\nstatic NSString *const HANDLERS[] = {@\"syncFocus\", @\"focus\", @\"newScrollHeight\", @\"newScrollTop\", @\"openLink\"};\n\n- (void)setTerminal:(Terminal *)terminal {\n    if (_terminal) {\n        [_terminal removeObserver:self forKeyPath:@\"loaded\"];\n        [self uninstallTerminalView];\n    }\n\n    _terminal = terminal;\n    [_terminal addObserver:self forKeyPath:@\"loaded\" options:NSKeyValueObservingOptionInitial context:nil];\n    if (_terminal.loaded)\n        [self installTerminalView];\n}\n\n- (void)installTerminalView {\n    NSAssert(_terminal.loaded, @\"should probably not be installing a non-loaded terminal\");\n    UIView *superview = self.terminal.webView.superview;\n    if (superview != nil) {\n        NSAssert(superview == self.scrollbarView, @\"installing terminal that is already installed elsewhere\");\n        return;\n    }\n\n    WKWebView *webView = _terminal.webView;\n    _terminal.enableVoiceOverAnnounce = YES;\n    webView.scrollView.scrollEnabled = NO;\n    webView.scrollView.delaysContentTouches = NO;\n    webView.scrollView.canCancelContentTouches = NO;\n    webView.scrollView.panGestureRecognizer.enabled = NO;\n    id <WKScriptMessageHandler> handler = [[WeakScriptMessageHandler alloc] initWithHandler:self];\n    for (int i = 0; i < sizeof(HANDLERS)/sizeof(HANDLERS[0]); i++) {\n        [webView.configuration.userContentController addScriptMessageHandler:handler name:HANDLERS[i]];\n    }\n    webView.frame = self.bounds;\n    self.opaque = webView.opaque = NO;\n    webView.backgroundColor = UIColor.clearColor;\n    webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;\n\n    self.scrollbarView.contentView = webView;\n    [self.scrollbarView addSubview:webView];\n}\n\n- (void)uninstallTerminalView {\n    // remove old terminal\n    UIView *superview = _terminal.webView.superview;\n    if (superview != self.scrollbarView) {\n        NSAssert(superview == nil, @\"uninstalling terminal that is installed elsewhere\");\n        return;\n    }\n\n    [_terminal.webView removeFromSuperview];\n    self.scrollbarView.contentView = nil;\n    for (int i = 0; i < sizeof(HANDLERS)/sizeof(HANDLERS[0]); i++) {\n        [_terminal.webView.configuration.userContentController removeScriptMessageHandlerForName:HANDLERS[i]];\n    }\n    _terminal.enableVoiceOverAnnounce = NO;\n}\n\n#pragma mark Styling\n\n- (void)_updateStyle {\n    NSAssert(NSThread.isMainThread, @\"This method needs to be called on the main thread\");\n    if (!self.terminal.loaded)\n        return;\n    UserPreferences *prefs = [UserPreferences shared];\n    if (_overrideFontSize == prefs.fontSize.doubleValue)\n        _overrideFontSize = 0;\n    Palette *palette = prefs.palette;\n    if (self.overrideAppearance != OverrideAppearanceNone) {\n        palette = self.overrideAppearance == OverrideAppearanceLight ? prefs.theme.lightPalette : prefs.theme.darkPalette;\n    }\n    NSMutableDictionary<NSString *, id> *themeInfo = [@{\n        @\"fontFamily\": prefs.fontFamily,\n        @\"fontSize\": @(self.effectiveFontSize),\n        @\"foregroundColor\": palette.foregroundColor,\n        @\"backgroundColor\": palette.backgroundColor,\n        @\"blinkCursor\": @(prefs.blinkCursor),\n        @\"cursorShape\": prefs.htermCursorShape,\n    } mutableCopy];\n    if (prefs.palette.colorPaletteOverrides) {\n        themeInfo[@\"colorPaletteOverrides\"] = palette.colorPaletteOverrides;\n    }\n    NSString *json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:themeInfo options:0 error:nil] encoding:NSUTF8StringEncoding];\n    [self.terminal.webView evaluateJavaScript:[NSString stringWithFormat:@\"exports.updateStyle(%@)\", json] completionHandler:^(id result, NSError *error){\n        [self updateFloatingCursorSensitivity];\n    }];\n}\n\n- (void)setOverrideFontSize:(CGFloat)overrideFontSize {\n    _overrideFontSize = overrideFontSize;\n    [self _updateStyle];\n}\n\n- (void)setOverrideAppearance:(enum OverrideAppearance)overrideAppearance {\n    _overrideAppearance = overrideAppearance;\n    [self _updateStyle];\n}\n\n- (CGFloat)effectiveFontSize {\n    if (self.overrideFontSize != 0)\n        return self.overrideFontSize;\n    return UserPreferences.shared.fontSize.doubleValue;\n}\n\n#pragma mark Focus and scrolling\n\n- (void)setTerminalFocused:(BOOL)terminalFocused {\n    _terminalFocused = terminalFocused;\n    NSString *script = terminalFocused ? @\"exports.setFocused(true)\" : @\"exports.setFocused(false)\";\n    [self.terminal.webView evaluateJavaScript:script completionHandler:nil];\n}\n\n- (BOOL)becomeFirstResponder {\n    self.terminalFocused = YES;\n    [self reloadInputViews];\n    return [super becomeFirstResponder];\n}\n- (BOOL)resignFirstResponder {\n    self.terminalFocused = NO;\n    return [super resignFirstResponder];\n}\n- (void)windowDidBecomeKey:(NSNotification *)notif {\n    self.terminalFocused = YES;\n}\n- (void)windowDidResignKey:(NSNotification *)notif {\n    self.terminalFocused = NO;\n}\n\n- (IBAction)loseFocus:(id)sender {\n    [self resignFirstResponder];\n}\n\n- (void)willMoveToWindow:(UIWindow *)newWindow {\n    NSNotificationCenter *center = NSNotificationCenter.defaultCenter;\n    if (self.window != nil) {\n        [center removeObserver:self\n                          name:UIWindowDidBecomeKeyNotification\n                        object:self.window];\n        [center removeObserver:self\n                          name:UIWindowDidResignKeyNotification\n                        object:self.window];\n    }\n    if (newWindow != nil) {\n        [center addObserver:self\n                   selector:@selector(windowDidBecomeKey:)\n                       name:UIWindowDidBecomeKeyNotification\n                     object:newWindow];\n        [center addObserver:self\n                   selector:@selector(windowDidResignKey:)\n                       name:UIWindowDidResignKeyNotification\n                     object:newWindow];\n    }\n}\n\n- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {\n    if ([message.name isEqualToString:@\"syncFocus\"]) {\n        self.terminalFocused = self.terminalFocused;\n    } else if ([message.name isEqualToString:@\"focus\"]) {\n        if (!self.isFirstResponder) {\n            [self becomeFirstResponder];\n        }\n    } else if ([message.name isEqualToString:@\"newScrollHeight\"]) {\n        self.scrollbarView.contentSize = CGSizeMake(0, [message.body doubleValue]);\n    } else if ([message.name isEqualToString:@\"newScrollTop\"]) {\n        CGFloat newOffset = [message.body doubleValue];\n        if (self.scrollbarView.contentOffset.y == newOffset)\n            return;\n        [self.scrollbarView setContentOffset:CGPointMake(0, newOffset) animated:NO];\n    } else if ([message.name isEqualToString:@\"openLink\"]) {\n        [UIApplication openURL:message.body];\n    }\n}\n\n- (void)scrollViewDidScroll:(UIScrollView *)scrollView {\n    [self.terminal.webView evaluateJavaScript:[NSString stringWithFormat:@\"exports.newScrollTop(%f)\", scrollView.contentOffset.y] completionHandler:nil];\n}\n\n- (void)setKeyboardAppearance:(UIKeyboardAppearance)keyboardAppearance {\n    BOOL needsFirstResponderDance = self.isFirstResponder && _keyboardAppearance != keyboardAppearance;\n    if (needsFirstResponderDance) {\n        [self resignFirstResponder];\n    }\n    _keyboardAppearance = keyboardAppearance;\n    if (needsFirstResponderDance) {\n        [self becomeFirstResponder];\n    }\n    if (keyboardAppearance == UIKeyboardAppearanceLight) {\n        self.scrollbarView.indicatorStyle = UIScrollViewIndicatorStyleBlack;\n    } else {\n        self.scrollbarView.indicatorStyle = UIScrollViewIndicatorStyleWhite;\n    }\n}\n\n#pragma mark Keyboard Input\n\n// implementing these makes a keyboard pop up when this view is first responder\n\n- (void)insertText:(NSString *)text {\n    self.markedText = nil;\n\n    if (self.controlKey.highlighted)\n        self.controlKey.selected = YES;\n    if (self.controlKey.selected) {\n        if (!self.controlKey.highlighted)\n            self.controlKey.selected = NO;\n        if (text.length == 1)\n            return [self insertControlChar:[text characterAtIndex:0]];\n    }\n\n    text = [text stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\"\\r\"];\n    NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];\n    [self.terminal sendInput:data];\n}\n\n- (void)insertControlChar:(char)ch {\n    if (strchr(controlKeys, ch) != NULL) {\n        if (ch == ' ') ch = '\\0';\n        if (ch == '2') ch = '@';\n        if (ch == '6') ch = '^';\n        if (ch != '\\0')\n            ch = toupper(ch) ^ 0x40;\n        [self.terminal sendInput:[NSData dataWithBytes:&ch length:1]];\n    }\n}\n\n- (void)deleteBackward {\n    [self insertText:@\"\\x7f\"];\n}\n\n- (BOOL)hasText {\n    return YES; // it's always ok to send a \"delete\"\n}\n\n#pragma mark IME Input and Selection\n\n- (void)setMarkedText:(nullable NSString *)markedText selectedRange:(NSRange)selectedRange {\n    self.markedText = markedText;\n}\n\n- (void)unmarkText {\n    [self insertText:self.markedText];\n}\n\n- (UITextRange *)markedTextRange {\n    if (self.markedText != nil)\n        return self.markedRange;\n    return nil;\n}\n\n// The only reason to have this selected range is to prevent the \"speak selection\" context action from failing to get the current selection and falling back on calling copy:. It doesn't even have to work, it seems...\n\n- (UITextRange *)selectedTextRange {\n    return self.selectedRange;\n}\n\n- (NSString *)textInRange:(UITextRange *)range {\n    if (range == self.markedRange)\n        return self.markedText;\n    if (range == self.selectedRange)\n        return @\"\";\n    return nil;\n}\n\n- (id)insertDictationResultPlaceholder {\n    return @\"\";\n}\n- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult {\n}\n\n#pragma mark Keyboard Actions\n\n- (void)paste:(id)sender {\n    NSString *string = UIPasteboard.generalPasteboard.string;\n    if (string) {\n        [self insertText:string];\n    }\n}\n\n- (void)copy:(id)sender {\n    [self.terminal.webView evaluateJavaScript:@\"exports.copy()\" completionHandler:nil];\n}\n\n- (void)clearScrollback:(UIKeyCommand *)command {\n    [self.terminal.webView evaluateJavaScript:@\"exports.clearScrollback()\" completionHandler:nil];\n}\n\n#pragma mark Floating cursor\n\n- (void)updateFloatingCursorSensitivity {\n    [self.terminal.webView evaluateJavaScript:@\"exports.getCharacterSize()\" completionHandler:^(NSArray *charSizeRaw, NSError *error) {\n        if (error != nil) {\n            NSLog(@\"error getting character size: %@\", error);\n            return;\n        }\n        CGSize charSize = CGSizeMake([charSizeRaw[0] doubleValue], [charSizeRaw[1] doubleValue]);\n        double sensitivity = 0.5;\n        self.floatingCursorSensitivity = CGSizeMake(charSize.width / sensitivity, charSize.height / sensitivity);\n    }];\n}\n\n- (struct rowcol)rowcolFromPoint:(CGPoint)point {\n    CGSize sensitivity = self.actualFloatingCursorSensitivity;\n    return (struct rowcol) {\n        .row = (int) (-point.y / sensitivity.height),\n        .col = (int) (point.x / sensitivity.width),\n    };\n}\n\n- (void)beginFloatingCursorAtPoint:(CGPoint)point {\n    self.actualFloatingCursorSensitivity = self.floatingCursorSensitivity;\n    self.floatingCursor = [self rowcolFromPoint:point];\n}\n\n- (void)updateFloatingCursorAtPoint:(CGPoint)point {\n    struct rowcol newPos = [self rowcolFromPoint:point];\n    int rowDiff = newPos.row - self.floatingCursor.row;\n    int colDiff = newPos.col - self.floatingCursor.col;\n    NSMutableString *arrows = [NSMutableString string];\n    for (int i = 0; i < abs(rowDiff); i++) {\n        [arrows appendString:[self.terminal arrow:rowDiff > 0 ? 'A': 'B']];\n    }\n    for (int i = 0; i < abs(colDiff); i++) {\n        [arrows appendString:[self.terminal arrow:colDiff > 0 ? 'C': 'D']];\n    }\n    [self insertText:arrows];\n    self.floatingCursor = newPos;\n}\n\n- (void)endFloatingCursor {\n    self.floatingCursor = (struct rowcol) {};\n}\n\n#pragma mark Keyboard Traits\n\n- (UITextSmartDashesType)smartDashesType API_AVAILABLE(ios(11)) {\n    return UITextSmartDashesTypeNo;\n}\n- (UITextSmartQuotesType)smartQuotesType API_AVAILABLE(ios(11)) {\n    return UITextSmartQuotesTypeNo;\n}\n- (UITextSmartInsertDeleteType)smartInsertDeleteType API_AVAILABLE(ios(11)) {\n    return UITextSmartInsertDeleteTypeNo;\n}\n- (UITextAutocapitalizationType)autocapitalizationType {\n    return UITextAutocapitalizationTypeNone;\n}\n- (UITextAutocorrectionType)autocorrectionType {\n    return UITextAutocorrectionTypeNo;\n}\n// Apparently required on iOS 15+: https://stackoverflow.com/a/72359764\n- (UITextSpellCheckingType)spellCheckingType {\n    return UITextSpellCheckingTypeNo;\n}\n\n#pragma mark Hardware Keyboard\n\n- (void)handleKeyCommand:(UIKeyCommand *)command {\n    NSString *key = command.input;\n    if (command.modifierFlags == 0) {\n        if ([key isEqualToString:@\"`\"] && UserPreferences.shared.backtickMapEscape)\n            key = UIKeyInputEscape;\n        if ([key isEqualToString:UIKeyInputEscape])\n            key = @\"\\x1b\";\n        else if ([key isEqualToString:UIKeyInputUpArrow])\n            key = [self.terminal arrow:'A'];\n        else if ([key isEqualToString:UIKeyInputDownArrow])\n            key = [self.terminal arrow:'B'];\n        else if ([key isEqualToString:UIKeyInputLeftArrow])\n            key = [self.terminal arrow:'D'];\n        else if ([key isEqualToString:UIKeyInputRightArrow])\n            key = [self.terminal arrow:'C'];\n        [self insertText:key];\n    } else if (command.modifierFlags & UIKeyModifierShift) {\n        [self insertText:[key uppercaseString]];\n    } else if (command.modifierFlags & UIKeyModifierAlternate) {\n        [self insertText:[@\"\\x1b\" stringByAppendingString:key]];\n    } else if (command.modifierFlags & UIKeyModifierAlphaShift) {\n        [self handleCapsLockWithCommand:command];\n    } else if (command.modifierFlags & UIKeyModifierControl || command.modifierFlags & UIKeyModifierAlphaShift) {\n        if (key.length == 0)\n            return;\n        if ([key isEqualToString:@\"2\"])\n            key = @\"@\";\n        else if ([key isEqualToString:@\"6\"])\n            key = @\"^\";\n        else if ([key isEqualToString:@\"-\"])\n            key = @\"_\";\n        [self insertControlChar:[key characterAtIndex:0]];\n    }\n}\n\nstatic const char *alphabet = \"abcdefghijklmnopqrstuvwxyz\";\nstatic const char *controlKeys = \"abcdefghijklmnopqrstuvwxyz@^26-=[]\\\\ \";\nstatic const char *metaKeys = \"abcdefghijklmnopqrstuvwxyz0123456789-=[]\\\\;',./\";\n\n- (NSArray<UIKeyCommand *> *)keyCommands {\n    if (_keyCommands != nil)\n        return _keyCommands;\n    _keyCommands = [NSMutableArray new];\n    [self addKeys:controlKeys withModifiers:UIKeyModifierControl];\n    for (NSString *specialKey in @[UIKeyInputEscape, UIKeyInputUpArrow, UIKeyInputDownArrow,\n                                   UIKeyInputLeftArrow, UIKeyInputRightArrow, @\"\\t\"]) {\n        [self addKey:specialKey withModifiers:0];\n    }\n    if (UserPreferences.shared.capsLockMapping != CapsLockMapNone) {\n        if (@available(iOS 13, *)); else {\n            [self addKeys:controlKeys withModifiers:UIKeyModifierAlphaShift];\n            [self addKeys:alphabet withModifiers:0];\n            [self addKeys:alphabet withModifiers:UIKeyModifierShift];\n            [self addKey:@\"\" withModifiers:UIKeyModifierAlphaShift]; // otherwise tap of caps lock can switch layouts\n        }\n    }\n    if (UserPreferences.shared.optionMapping == OptionMapEsc) {\n        [self addKeys:metaKeys withModifiers:UIKeyModifierAlternate];\n    }\n    if (UserPreferences.shared.backtickMapEscape) {\n        [self addKey:@\"`\" withModifiers:0];\n    }\n    [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:@\"k\"\n                                                modifierFlags:UIKeyModifierCommand|UIKeyModifierShift\n                                                       action:@selector(clearScrollback:)\n                                         discoverabilityTitle:@\"Clear Scrollback\"]];\n    return _keyCommands;\n}\n\n- (void)addKeys:(const char *)keys withModifiers:(UIKeyModifierFlags)modifiers {\n    for (size_t i = 0; keys[i] != '\\0'; i++) {\n        [self addKey:[NSString stringWithFormat:@\"%c\", keys[i]] withModifiers:modifiers];\n    }\n}\n\n- (void)addKey:(NSString *)key withModifiers:(UIKeyModifierFlags)modifiers {\n    UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:key\n                                                modifierFlags:modifiers\n                                                       action:@selector(handleKeyCommand:)];\n    if (@available(iOS 15, *)) {\n        command.wantsPriorityOverSystemBehavior = YES;\n    }\n    [_keyCommands addObject:command];\n}\n\n- (void)keyCommandTriggered:(UIKeyCommand *)sender {\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [self handleKeyCommand:sender];\n    });\n}\n\n- (void)handleCapsLockWithCommand:(UIKeyCommand *)command {\n    CapsLockMapping target = UserPreferences.shared.capsLockMapping;\n    NSString *newInput = command.input ? command.input : @\"\";\n    UIKeyModifierFlags flags = command.modifierFlags;\n    flags ^= UIKeyModifierAlphaShift;\n    if(target == CapsLockMapEscape) {\n        newInput = UIKeyInputEscape;\n    } else if(target == CapsLockMapControl) {\n        if([newInput length] == 0) {\n            return;\n        }\n        flags |= UIKeyModifierControl;\n    } else {\n        return;\n    }\n\n    UIKeyCommand *newCommand = [UIKeyCommand keyCommandWithInput:newInput\n                                                   modifierFlags:flags\n                                                          action:@selector(keyCommandTriggered:)];\n    [self handleKeyCommand:newCommand];\n}\n\n- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {\n    if (@available(iOS 13.4, *)) {\n        UIKey *key = presses.anyObject.key;\n        if (UserPreferences.shared.overrideControlSpace &&\n            key.keyCode == UIKeyboardHIDUsageKeyboardSpacebar &&\n            key.modifierFlags & UIKeyModifierControl) {\n            return [self insertControlChar:' '];\n        }\n    }\n    return [super pressesBegan:presses withEvent:event];\n}\n\n#pragma mark UITextInput stubs\n\n#if 0\n#define LogStub() NSLog(@\"%s\", __func__)\n#else\n#define LogStub()\n#endif\n\n- (NSWritingDirection)baseWritingDirectionForPosition:(nonnull UITextPosition *)position inDirection:(UITextStorageDirection)direction { LogStub(); return NSWritingDirectionLeftToRight; }\n- (void)setBaseWritingDirection:(NSWritingDirection)writingDirection forRange:(nonnull UITextRange *)range { LogStub(); }\n- (UITextPosition *)beginningOfDocument { LogStub(); return nil; }\n- (CGRect)caretRectForPosition:(nonnull UITextPosition *)position { LogStub(); return CGRectZero; }\n- (nullable UITextRange *)characterRangeAtPoint:(CGPoint)point { LogStub(); return nil; }\n- (nullable UITextRange *)characterRangeByExtendingPosition:(nonnull UITextPosition *)position inDirection:(UITextLayoutDirection)direction { LogStub(); return nil; }\n- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point { LogStub(); return nil; }\n- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(nonnull UITextRange *)range { LogStub(); return nil; }\n- (NSComparisonResult)comparePosition:(nonnull UITextPosition *)position toPosition:(nonnull UITextPosition *)other { LogStub(); return NSOrderedSame; }\n- (UITextPosition *)endOfDocument { LogStub(); return nil; }\n- (CGRect)firstRectForRange:(nonnull UITextRange *)range { LogStub(); return CGRectZero; }\n- (NSDictionary<NSAttributedStringKey,id> *)markedTextStyle { LogStub(); return nil; }\n- (void)setMarkedTextStyle:(NSDictionary<NSAttributedStringKey,id> *)markedTextStyle { LogStub(); }\n- (NSInteger)offsetFromPosition:(nonnull UITextPosition *)from toPosition:(nonnull UITextPosition *)toPosition { LogStub(); return 0; }\n- (nullable UITextPosition *)positionFromPosition:(nonnull UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset { LogStub(); return nil; }\n- (nullable UITextPosition *)positionFromPosition:(nonnull UITextPosition *)position offset:(NSInteger)offset { LogStub(); return nil; }\n- (nullable UITextPosition *)positionWithinRange:(nonnull UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction { LogStub(); return nil; }\n- (void)replaceRange:(nonnull UITextRange *)range withText:(nonnull NSString *)text { LogStub(); }\n- (void)setSelectedTextRange:(UITextRange *)selectedTextRange { LogStub(); }\n- (nonnull NSArray<UITextSelectionRect *> *)selectionRectsForRange:(nonnull UITextRange *)range { LogStub(); return @[]; }\n- (nullable UITextRange *)textRangeFromPosition:(nonnull UITextPosition *)fromPosition toPosition:(nonnull UITextPosition *)toPosition { LogStub(); return nil; }\n\n// conforming to UITextInput makes this view default to being an accessibility element, which blocks selecting anything in it\n- (BOOL)isAccessibilityElement { return NO; }\n\n@end\n"
  },
  {
    "path": "app/TerminalViewController.h",
    "content": "//\n//  ViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#import <UIKit/UIKit.h>\n#import \"Terminal.h\"\n\n@interface TerminalViewController : UIViewController\n\n@property (nonatomic) Terminal *terminal;\n\n- (void)startNewSession;\n- (void)reconnectSessionFromTerminalUUID:(NSUUID *)uuid;\n@property (readonly) NSUUID *sessionTerminalUUID; // 0 means invalid\n@property UISceneSession *sceneSession API_AVAILABLE(ios(13.0));\n\n@end\n\nextern struct tty_driver ios_tty_driver;\n"
  },
  {
    "path": "app/TerminalViewController.m",
    "content": "//\n//  ViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#import \"TerminalViewController.h\"\n#import \"AppDelegate.h\"\n#import \"TerminalView.h\"\n#import \"BarButton.h\"\n#import \"ArrowBarButton.h\"\n#import \"UserPreferences.h\"\n#import \"AboutViewController.h\"\n#import \"CurrentRoot.h\"\n#import \"NSObject+SaneKVO.h\"\n#import \"LinuxInterop.h\"\n#include \"kernel/init.h\"\n#include \"kernel/task.h\"\n#include \"kernel/calls.h\"\n#include \"fs/devices.h\"\n\n@interface TerminalViewController () <UIGestureRecognizerDelegate>\n\n@property UITapGestureRecognizer *tapRecognizer;\n@property (weak, nonatomic) IBOutlet TerminalView *termView;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;\n\n@property (weak, nonatomic) IBOutlet UIButton *tabKey;\n@property (weak, nonatomic) IBOutlet UIButton *controlKey;\n@property (weak, nonatomic) IBOutlet UIButton *escapeKey;\n@property (strong, nonatomic) IBOutletCollection(id) NSArray *barButtons;\n@property (strong, nonatomic) IBOutletCollection(id) NSArray *barControls;\n\n@property (weak, nonatomic) IBOutlet UIInputView *barView;\n@property (weak, nonatomic) IBOutlet UIStackView *bar;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barTop;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barBottom;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barLeading;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barTrailing;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barButtonWidth;\n@property (weak, nonatomic) IBOutlet NSLayoutConstraint *barHeight;\n@property (weak, nonatomic) IBOutlet UIView *settingsBadge;\n\n@property (weak, nonatomic) IBOutlet UIButton *infoButton;\n@property (weak, nonatomic) IBOutlet UIButton *pasteButton;\n@property (weak, nonatomic) IBOutlet UIButton *hideKeyboardButton;\n\n@property int sessionPid;\n@property (nonatomic) Terminal *sessionTerminal;\n\n@property BOOL ignoreKeyboardMotion;\n@property (nonatomic) BOOL hasExternalKeyboard;\n\n@end\n\n@implementation TerminalViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n\n#if !ISH_LINUX\n    int bootError = [AppDelegate bootError];\n    if (bootError < 0) {\n        NSString *message = [NSString stringWithFormat:@\"could not boot\"];\n        NSString *subtitle = [NSString stringWithFormat:@\"error code %d\", bootError];\n        if (bootError == _EINVAL)\n            subtitle = [subtitle stringByAppendingString:@\"\\n(try reinstalling the app, see release notes for details)\"];\n        [self showMessage:message subtitle:subtitle];\n        NSLog(@\"boot failed with code %d\", bootError);\n    }\n#endif\n\n    self.terminal = self.terminal;\n    [self.termView becomeFirstResponder];\n\n    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];\n    [center addObserver:self\n               selector:@selector(keyboardDidSomething:)\n                   name:UIKeyboardWillChangeFrameNotification\n                 object:nil];\n    [center addObserver:self\n               selector:@selector(keyboardDidSomething:)\n                   name:UIKeyboardDidChangeFrameNotification\n                 object:nil];\n    [center addObserver:self\n               selector:@selector(_updateBadge)\n                   name:FsUpdatedNotification\n                 object:nil];\n\n\n    [self _updateStyleFromPreferences:NO];\n    \n    if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {\n        [self.bar removeArrangedSubview:self.hideKeyboardButton];\n        [self.hideKeyboardButton removeFromSuperview];\n    }\n    if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone) {\n        self.barHeight.constant = 36;\n    } else {\n        self.barHeight.constant = 43;\n    }\n    \n    // SF Symbols is cool\n    if (@available(iOS 13, *)) {\n        [self.infoButton setImage:[UIImage systemImageNamed:@\"gear\"] forState:UIControlStateNormal];\n        [self.pasteButton setImage:[UIImage systemImageNamed:@\"doc.on.clipboard\"] forState:UIControlStateNormal];\n        [self.hideKeyboardButton setImage:[UIImage systemImageNamed:@\"keyboard.chevron.compact.down\"] forState:UIControlStateNormal];\n        \n        [self.tabKey setTitle:nil forState:UIControlStateNormal];\n        [self.tabKey setImage:[UIImage systemImageNamed:@\"arrow.right.to.line.alt\"] forState:UIControlStateNormal];\n        [self.controlKey setTitle:nil forState:UIControlStateNormal];\n        [self.controlKey setImage:[UIImage systemImageNamed:@\"control\"] forState:UIControlStateNormal];\n        [self.escapeKey setTitle:nil forState:UIControlStateNormal];\n        [self.escapeKey setImage:[UIImage systemImageNamed:@\"escape\"] forState:UIControlStateNormal];\n    }\n    \n    [UserPreferences.shared observe:@[@\"hideStatusBar\"] options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self setNeedsStatusBarAppearanceUpdate];\n        });\n    }];\n    [UserPreferences.shared observe:@[@\"colorScheme\", @\"theme\", @\"hideExtraKeysWithExternalKeyboard\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self _updateStyleFromPreferences:YES];\n        });\n    }];\n    [self _updateBadge];\n}\n\n- (void)awakeFromNib {\n    [super awakeFromNib];\n#if !ISH_LINUX\n    [NSNotificationCenter.defaultCenter addObserver:self\n                                           selector:@selector(processExited:)\n                                               name:ProcessExitedNotification\n                                             object:nil];\n#else\n    [NSNotificationCenter.defaultCenter addObserver:self\n                                           selector:@selector(kernelPanicked:)\n                                               name:KernelPanicNotification\n                                             object:nil];\n#endif\n}\n\n- (void)viewDidAppear:(BOOL)animated {\n    [AppDelegate maybePresentStartupMessageOnViewController:self];\n    [super viewDidAppear:animated];\n}\n\n- (void)startNewSession {\n    int err = [self startSession];\n    if (err < 0) {\n        [self showMessage:@\"could not start session\"\n                 subtitle:[NSString stringWithFormat:@\"error code %d\", err]];\n    }\n}\n\n- (void)reconnectSessionFromTerminalUUID:(NSUUID *)uuid {\n    self.sessionTerminal = [Terminal terminalWithUUID:uuid];\n    if (self.sessionTerminal == nil)\n        [self startNewSession];\n}\n\n- (NSUUID *)sessionTerminalUUID {\n    return self.terminal.uuid;\n}\n\n- (int)startSession {\n    NSArray<NSString *> *command = UserPreferences.shared.launchCommand;\n\n#if !ISH_LINUX\n    int err = become_new_init_child();\n    if (err < 0)\n        return err;\n    struct tty *tty;\n    self.sessionTerminal = nil;\n    Terminal *terminal = [Terminal createPseudoTerminal:&tty];\n    if (terminal == nil) {\n        NSAssert(IS_ERR(tty), @\"tty should be error\");\n        return (int) PTR_ERR(tty);\n    }\n    self.sessionTerminal = terminal;\n    NSString *stdioFile = [NSString stringWithFormat:@\"/dev/pts/%d\", tty->num];\n    err = create_stdio(stdioFile.fileSystemRepresentation, TTY_PSEUDO_SLAVE_MAJOR, tty->num);\n    if (err < 0)\n        return err;\n    tty_release(tty);\n\n    char argv[4096];\n    [Terminal convertCommand:command toArgs:argv limitSize:sizeof(argv)];\n    const char *envp = \"TERM=xterm-256color\\0\";\n    err = do_execve(command[0].UTF8String, command.count, argv, envp);\n    if (err < 0)\n        return err;\n    self.sessionPid = current->pid;\n    task_start(current);\n#else\n    const char *argv_arr[command.count + 1];\n    for (NSUInteger i = 0; i < command.count; i++)\n        argv_arr[i] = command[i].UTF8String;\n    argv_arr[command.count] = NULL;\n    const char *envp_arr[] = {\n        \"TERM=xterm-256color\",\n        NULL,\n    };\n    const char *const *argv = argv_arr;\n    const char *const *envp = envp_arr;\n    __block Terminal *terminal = nil;\n    __block int sessionPid = 0;\n    __block int err = 1;\n    sync_do_in_workqueue(^(void (^done)(void)) {\n        linux_start_session(argv[0], argv, envp, ^(int retval, int pid, nsobj_t term) {\n            err = retval;\n            if (term)\n                terminal = CFBridgingRelease(term);\n            sessionPid = pid;\n            done();\n        });\n    });\n    NSAssert(err <= 0, @\"session start did not finish??\");\n    if (err < 0)\n        return err;\n    self.sessionTerminal = terminal;\n    self.sessionPid = sessionPid;\n#endif\n    return 0;\n}\n\n#if !ISH_LINUX\n- (void)processExited:(NSNotification *)notif {\n    int pid = [notif.userInfo[@\"pid\"] intValue];\n    if (pid != self.sessionPid)\n        return;\n\n    [self.sessionTerminal destroy];\n    // On iOS 13, there are multiple windows, so just close this one.\n    if (@available(iOS 13, *)) {\n        // On iPhone, destroying scenes will fail, but the error doesn't actually go to the error handler, which is really stupid. Apple doesn't fix bugs, so I'm forced to just add a check here.\n        if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad && self.sceneSession != nil) {\n            [UIApplication.sharedApplication requestSceneSessionDestruction:self.sceneSession options:nil errorHandler:^(NSError *error) {\n                NSLog(@\"scene destruction error %@\", error);\n                self.sceneSession = nil;\n                [self processExited:notif];\n            }];\n            return;\n        }\n    }\n    current = NULL; // it's been freed\n    [self startNewSession];\n}\n#endif\n\n#if ISH_LINUX\n- (void)kernelPanicked:(NSNotification *)notif {\n    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@\"panik\" message:notif.userInfo[@\"message\"] preferredStyle:UIAlertControllerStyleAlert];\n    [alert addAction:[UIAlertAction actionWithTitle:@\"k\" style:UIAlertActionStyleDefault handler:nil]];\n    [self presentViewController:alert animated:YES completion:nil];\n}\n#endif\n\n- (void)showMessage:(NSString *)message subtitle:(NSString *)subtitle {\n    dispatch_async(dispatch_get_main_queue(), ^{\n        UIAlertController *alert = [UIAlertController alertControllerWithTitle:message message:subtitle preferredStyle:UIAlertControllerStyleAlert];\n        [alert addAction:[UIAlertAction actionWithTitle:@\"k\"\n                                                  style:UIAlertActionStyleDefault\n                                                handler:nil]];\n        [self presentViewController:alert animated:YES completion:nil];\n    });\n}\n\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {\n    if (object == [UserPreferences shared]) {\n        [self _updateStyleFromPreferences:YES];\n    } else {\n        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];\n    }\n}\n\n- (void)_updateStyleFromPreferences:(BOOL)animated {\n    NSAssert(NSThread.isMainThread, @\"This method needs to be called on the main thread\");\n    NSTimeInterval duration = animated ? 0.1 : 0;\n    [UIView animateWithDuration:duration animations:^{\n        self.view.backgroundColor = [[UIColor alloc] ish_initWithHexString:UserPreferences.shared.palette.backgroundColor];\n        UIKeyboardAppearance keyAppearance = UserPreferences.shared.keyboardAppearance;\n        self.termView.keyboardAppearance = keyAppearance;\n        for (BarButton *button in self.barButtons) {\n            button.keyAppearance = keyAppearance;\n        }\n        UIColor *tintColor = keyAppearance == UIKeyboardAppearanceLight ? UIColor.blackColor : UIColor.whiteColor;\n        for (UIControl *control in self.barControls) {\n            control.tintColor = tintColor;\n        }\n    }];\n    UIView *oldBarView = self.termView.inputAccessoryView;\n    if (UserPreferences.shared.hideExtraKeysWithExternalKeyboard && self.hasExternalKeyboard) {\n        self.termView.inputAccessoryView = nil;\n    } else {\n        self.termView.inputAccessoryView = self.barView;\n    }\n    if (self.termView.inputAccessoryView != oldBarView && self.termView.isFirstResponder) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            self.ignoreKeyboardMotion = YES; // avoid infinite recursion\n            [self.termView reloadInputViews];\n            self.ignoreKeyboardMotion = NO;\n        });\n    }\n}\n- (void)_updateStyleAnimated {\n    [self _updateStyleFromPreferences:YES];\n}\n\n- (void)_updateBadge {\n    self.settingsBadge.hidden = !FsNeedsRepositoryUpdate();\n}\n\n- (UIStatusBarStyle)preferredStatusBarStyle {\n    return UserPreferences.shared.statusBarStyle;\n}\n\n- (BOOL)prefersStatusBarHidden {\n    return UserPreferences.shared.hideStatusBar;\n}\n\n- (void)keyboardDidSomething:(NSNotification *)notification {\n    if (self.ignoreKeyboardMotion)\n        return;\n\n    CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];\n    UIScreen *screen = UIScreen.mainScreen;\n    // notification.object is nil before iOS 16.1 and the correct UIScreen after iOS 16.1\n    if (notification.object != nil)\n        screen = notification.object;\n    CGRect keyboardFrame = [self.view convertRect:screenKeyboardFrame fromCoordinateSpace:screen.coordinateSpace];\n    if (CGRectEqualToRect(keyboardFrame, CGRectZero))\n        return;\n    CGRect intersection = CGRectIntersection(keyboardFrame, self.view.bounds);\n    keyboardFrame = intersection;\n    NSLog(@\"%@ %@\", notification.name, @(keyboardFrame));\n    self.hasExternalKeyboard = keyboardFrame.size.height < 100;\n    CGFloat pad = CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(keyboardFrame);\n    // The keyboard appears to be undocked. This means it can either be split or\n    // truly floating. In the former case we want to keep the pad, but in the\n    // latter we should fall back to the input accessory view instead of the\n    // keyboard.\n    if (pad != keyboardFrame.size.height && keyboardFrame.size.width != UIScreen.mainScreen.bounds.size.width) {\n        pad = MAX(self.view.safeAreaInsets.bottom, self.termView.inputAccessoryView.frame.size.height);\n    }\n    // NSLog(@\"pad %f\", pad);\n    self.bottomConstraint.constant = pad;\n\n    BOOL initialLayout = self.termView.needsUpdateConstraints;\n    [self.view setNeedsUpdateConstraints];\n    if (!initialLayout) {\n        // if initial layout hasn't happened yet, the terminal view is going to be at a really weird place, so animating it is going to look really bad\n        NSNumber *interval = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];\n        NSNumber *curve = notification.userInfo[UIKeyboardAnimationCurveUserInfoKey];\n        [UIView animateWithDuration:interval.doubleValue\n                              delay:0\n                            options:curve.integerValue << 16\n                         animations:^{\n                             [self.view layoutIfNeeded];\n                         }\n                         completion:nil];\n    }\n}\n\n- (void)setHasExternalKeyboard:(BOOL)hasExternalKeyboard {\n    _hasExternalKeyboard = hasExternalKeyboard;\n    [self _updateStyleFromPreferences:YES];\n}\n\n- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {\n    if ([segue.identifier isEqualToString:@\"embed\"]) {\n        // You might want to check if this is your embed segue here\n        // in case there are other segues triggered from this view controller.\n        segue.destinationViewController.view.translatesAutoresizingMaskIntoConstraints = NO;\n    }\n}\n\n- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {\n    // Hack to resolve a layering mismatch between the UI and preferences.\n    if (@available(iOS 12.0, *)) {\n        if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {\n            // Ensure that the relevant things listening for this will update.\n            UserPreferences.shared.colorScheme = UserPreferences.shared.colorScheme;\n        }\n    }\n}\n\n#pragma mark Bar\n\n- (IBAction)showAbout:(id)sender {\n    UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@\"About\" bundle:nil] instantiateInitialViewController];\n    if ([sender isKindOfClass:[UIGestureRecognizer class]]) {\n        UIGestureRecognizer *recognizer = sender;\n        if (recognizer.state == UIGestureRecognizerStateBegan) {\n            AboutViewController *aboutViewController = (AboutViewController *) navigationController.topViewController;\n            aboutViewController.includeDebugPanel = YES;\n        } else {\n            return;\n        }\n    }\n    [self presentViewController:navigationController animated:YES completion:nil];\n    [self.termView resignFirstResponder];\n}\n\n- (void)resizeBar {\n    CGSize bar = self.barView.bounds.size;\n    // set sizing parameters on bar\n    // numbers stolen from iVim and modified somewhat\n    if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone) {\n        // phone\n        [self setBarHorizontalPadding:6 verticalPadding:6 buttonWidth:32];\n    } else if (bar.width >= 450) {\n        // wide ipad\n        [self setBarHorizontalPadding:15 verticalPadding:8 buttonWidth:43];\n    } else {\n        // narrow ipad (slide over)\n        [self setBarHorizontalPadding:10 verticalPadding:8 buttonWidth:36];\n    }\n    [UIView performWithoutAnimation:^{\n        [self.barView layoutIfNeeded];\n    }];\n}\n\n- (void)setBarHorizontalPadding:(CGFloat)horizontal verticalPadding:(CGFloat)vertical buttonWidth:(CGFloat)buttonWidth {\n    self.barLeading.constant = self.barTrailing.constant = horizontal;\n    self.barTop.constant = self.barBottom.constant = vertical;\n    self.barButtonWidth.constant = buttonWidth;\n}\n\n- (IBAction)pressEscape:(id)sender {\n    [self pressKey:@\"\\x1b\"];\n}\n- (IBAction)pressTab:(id)sender {\n    [self pressKey:@\"\\t\"];\n}\n- (void)pressKey:(NSString *)key {\n    [self.termView insertText:key];\n}\n\n- (IBAction)pressControl:(id)sender {\n    self.controlKey.selected = !self.controlKey.selected;\n}\n    \n- (IBAction)pressArrow:(ArrowBarButton *)sender {\n    switch (sender.direction) {\n        case ArrowUp: [self pressKey:[self.terminal arrow:'A']]; break;\n        case ArrowDown: [self pressKey:[self.terminal arrow:'B']]; break;\n        case ArrowLeft: [self pressKey:[self.terminal arrow:'D']]; break;\n        case ArrowRight: [self pressKey:[self.terminal arrow:'C']]; break;\n        case ArrowNone: break;\n    }\n}\n\n- (void)switchTerminal:(UIKeyCommand *)sender {\n    unsigned i = (unsigned) sender.input.integerValue;\n    if (i == 7)\n        self.terminal = self.sessionTerminal;\n    else\n        self.terminal = [Terminal terminalWithType:TTY_CONSOLE_MAJOR number:i];\n}\n\n- (void)increaseFontSize:(UIKeyCommand *)command {\n    self.termView.overrideFontSize = self.termView.effectiveFontSize + 1;\n}\n- (void)decreaseFontSize:(UIKeyCommand *)command {\n    self.termView.overrideFontSize = self.termView.effectiveFontSize - 1;\n}\n- (void)resetFontSize:(UIKeyCommand *)command {\n    self.termView.overrideFontSize = 0;\n}\n\n- (NSArray<UIKeyCommand *> *)keyCommands {\n    static NSMutableArray<UIKeyCommand *> *commands = nil;\n    if (commands == nil) {\n        commands = [NSMutableArray new];\n        for (unsigned i = 1; i <= 7; i++) {\n            [commands addObject:\n             [UIKeyCommand keyCommandWithInput:[NSString stringWithFormat:@\"%d\", i]\n                                 modifierFlags:UIKeyModifierCommand|UIKeyModifierAlternate|UIKeyModifierShift\n                                        action:@selector(switchTerminal:)]];\n        }\n        [commands addObject:\n         [UIKeyCommand keyCommandWithInput:@\"+\"\n                             modifierFlags:UIKeyModifierCommand\n                                    action:@selector(increaseFontSize:)\n                      discoverabilityTitle:@\"Increase Font Size\"]];\n        [commands addObject:\n         [UIKeyCommand keyCommandWithInput:@\"=\"\n                             modifierFlags:UIKeyModifierCommand\n                                    action:@selector(increaseFontSize:)]];\n        [commands addObject:\n         [UIKeyCommand keyCommandWithInput:@\"-\"\n                             modifierFlags:UIKeyModifierCommand\n                                    action:@selector(decreaseFontSize:)\n                      discoverabilityTitle:@\"Decrease Font Size\"]];\n        [commands addObject:\n         [UIKeyCommand keyCommandWithInput:@\"0\"\n                             modifierFlags:UIKeyModifierCommand\n                                    action:@selector(resetFontSize:)\n                      discoverabilityTitle:@\"Reset Font Size\"]];\n        [commands addObject:\n         [UIKeyCommand keyCommandWithInput:@\",\"\n                             modifierFlags:UIKeyModifierCommand\n                                    action:@selector(showAbout:)\n                      discoverabilityTitle:@\"Settings\"]];\n    }\n    return commands;\n}\n\n- (void)setTerminal:(Terminal *)terminal {\n    _terminal = terminal;\n    self.termView.terminal = self.terminal;\n}\n\n- (void)setSessionTerminal:(Terminal *)sessionTerminal {\n    if (_terminal == _sessionTerminal)\n        self.terminal = sessionTerminal;\n    _sessionTerminal = sessionTerminal;\n}\n\n@end\n\n@interface BarView : UIInputView\n@property (weak) IBOutlet TerminalViewController *terminalViewController;\n@property (nonatomic) IBInspectable BOOL allowsSelfSizing;\n@end\n@implementation BarView\n@dynamic allowsSelfSizing;\n\n- (void)layoutSubviews {\n    [self.terminalViewController resizeBar];\n}\n\n@end\n"
  },
  {
    "path": "app/Theme.h",
    "content": "//\n//  Theme.h\n//  iSH\n//\n//  Created by Saagar Jha on 2/25/22.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface Palette : NSObject\n@property(readonly) NSString *foregroundColor;\n@property(readonly) NSString *backgroundColor;\n@property(readonly, nullable) NSString *cursorColor;\n@property(readonly, nullable) NSArray<NSString *> *colorPaletteOverrides;\n\n- (instancetype)initWithForegroundColor:(nonnull NSString *)foregroundColor backgroundColor:(nonnull NSString *)backgroundColor cursorColor:(nullable NSString *)cursorColor colorPaletteOverrides:(nullable NSArray<NSString *> *)colorPaletteOverrides;\n@end\n\n@interface ThemeAppearance : NSObject\n@property(readonly) BOOL lightOverride;\n@property(readonly) BOOL darkOverride;\n@property(class, readonly) ThemeAppearance *alwaysLight;\n@property(class, readonly) ThemeAppearance *alwaysDark;\n\n- (instancetype)initWithLightOverride:(BOOL)lightOverride darkOverride:(BOOL)darkOverride;\n@end\n\n@interface Theme : NSObject\n@property(class, readonly) NSArray<Theme *> *defaultThemes;\n@property(class, readonly) NSArray<Theme *> *userThemes;\n\n@property(readonly) NSString *name;\n@property(readonly) Palette *lightPalette;\n@property(readonly) Palette *darkPalette;\n@property(readonly, nullable) ThemeAppearance *appearance;\n\n+ (nullable Theme *)themeForName:(NSString *)name includingDefaultThemes:(BOOL)includingDefaultThemes;\n- (instancetype)initWithName:(nonnull NSString *)name palette:(nonnull Palette *)palette appearance:(nullable ThemeAppearance *)appearance;\n- (instancetype)initWithName:(nonnull NSString *)name lightPalette:(nonnull Palette *)lightPalette darkPalette:(nonnull Palette *)darkPalette appearance:(nullable ThemeAppearance *)appearance;\n- (nullable instancetype)initWithName:(NSString *)name data:(NSData *)data;\n- (void)duplicateAsUserTheme;\n- (BOOL)addUserTheme;\n- (void)deleteUserTheme;\n- (void)replaceWithUserTheme:(Theme *)theme;\n@end\n\n@interface UIColor (iSH)\n- (nullable instancetype)ish_initWithHexString:(NSString *)string;\n@end\n\nextern NSString *const ThemesUpdatedNotification;\nextern NSString *const ThemeUpdatedNotification;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/Theme.m",
    "content": "//\n//  Theme.m\n//  iSH\n//\n//  Created by Saagar Jha on 2/25/22.\n//\n\n#import \"Theme.h\"\n#import \"UserPreferences.h\"\n#import \"fs/proc/ish.h\"\n\nchar *get_documents_directory_impl(void) {\n    return strdup(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject.UTF8String);\n}\n\n#define THEME_VERSION 1\n\n@implementation UIColor (iSH)\n- (nullable instancetype)ish_initWithHexString:(NSString *)string {\n    if (![string hasPrefix:@\"#\"]) {\n        return nil;\n    }\n    NSScanner *scanner = [NSScanner scannerWithString:string];\n    // Skip the leading #\n    [scanner setScanLocation:1];\n    unsigned int value;\n    if (![scanner scanHexInt:&value] || scanner.scanLocation != string.length) {\n        return nil;\n    }\n    unsigned int red;\n    unsigned int green;\n    unsigned int blue;\n    unsigned int alpha;\n    if (string.length == 4) { // RGB\n        blue = ((value & 0x00f) >> 0) * 0x11;\n        green = ((value & 0x0f0) >> 4) * 0x11;\n        red = ((value & 0xf00) >> 8) * 0x11;\n        alpha = 0xff;\n    } else if (string.length == 5) { // RGBA\n        blue = ((value & 0x000f) >> 0) * 0x11;\n        green = ((value & 0x00f0) >> 4) * 0x11;\n        red = ((value & 0x0f00) >> 8) * 0x11;\n        alpha = ((value & 0xf000) >> 12) * 0x11;\n    } else if (string.length == 7) { // RRGGBB\n        blue = (value & 0x0000ff) >> 0;\n        green = (value & 0x00ff00) >> 8;\n        red = (value & 0xff0000) >> 16;\n        alpha = 0xff;\n    } else if (string.length == 9) { // RRGGBBAA\n        blue = (value & 0x000000ff) >> 0;\n        green = (value & 0x0000ff00) >> 8;\n        red = (value & 0x00ff0000) >> 16;\n        alpha = (value & 0xff000000) >> 24;\n    } else {\n        return nil;\n    }\n    return [UIColor colorWithRed:1.0 * red / 0xff green:1.0 * green / 0xff blue:1.0 * blue / 0xff alpha:1.0 * alpha / 0xff];\n}\n@end\n\n@interface DirectoryWatcher: NSObject<NSFilePresenter>\n@property(readonly, copy) NSURL *presentedItemURL;\n- (instancetype)initWithURL:(NSURL *)url handler:(void (^)(void))handler;\n@end\n\n@implementation DirectoryWatcher {\n    void (^_handler)(void);\n}\n- (instancetype)initWithURL:(NSURL *)url handler:(void (^)(void))handler {\n    if (self = [super init]) {\n        self->_presentedItemURL = url;\n        self->_handler = handler;\n    }\n    return self;\n}\n\n- (NSOperationQueue *)presentedItemOperationQueue {\n    return NSOperationQueue.mainQueue;\n}\n\n- (void)presentedItemDidChange {\n    self->_handler();\n}\n@end\n\n@interface Palette ()\n@property(readonly, nonnull) NSDictionary *serializedRepresentation;\n\n- (nullable instancetype)initWithSerializedRepresentation:(nonnull NSDictionary *)serializedRepresentation;\n@end\n\n@implementation Palette\n\n- (instancetype)initWithForegroundColor:(NSString *)foregroundColor backgroundColor:(NSString *)backgroundColor cursorColor:(NSString *)cursorColor colorPaletteOverrides:(NSArray<NSString *> *)colorPaletteOverrides {\n    if (self = [super init]) {\n        self->_foregroundColor = foregroundColor;\n        self->_backgroundColor = backgroundColor;\n        self->_cursorColor = cursorColor;\n        self->_colorPaletteOverrides = colorPaletteOverrides;\n    }\n    return self;\n}\n\n- (instancetype)initWithSerializedRepresentation:(NSDictionary *)serializedRepresentation {\n#define VALID_COLOR(color) (color && [color isKindOfClass:NSString.class] && [[UIColor alloc] ish_initWithHexString:color])\n    id foregroundColor = serializedRepresentation[@\"foregroundColor\"];\n    id backgroundColor = serializedRepresentation[@\"backgroundColor\"];\n    id cursorColor = serializedRepresentation[@\"cursorColor\"];\n    id colorPaletteOverrides = serializedRepresentation[@\"colorPaletteOverrides\"];\n    BOOL validColorPalette = YES;\n    if (colorPaletteOverrides) {\n        if ([colorPaletteOverrides isKindOfClass:NSArray.class]) {\n            for (id color in colorPaletteOverrides) {\n                validColorPalette = validColorPalette || VALID_COLOR(color);\n            }\n        } else {\n            validColorPalette = NO;\n        }\n    }\n    if (VALID_COLOR(foregroundColor) && VALID_COLOR(backgroundColor) && (!cursorColor || VALID_COLOR(cursorColor)) && validColorPalette) {\n        return [self initWithForegroundColor:foregroundColor backgroundColor:backgroundColor cursorColor:cursorColor colorPaletteOverrides:colorPaletteOverrides];\n    } else {\n        return nil;\n    }\n#undef VALID_COLOR\n}\n\n- (NSDictionary *)serializedRepresentation {\n    NSMutableDictionary *representation = [@{\n        @\"foregroundColor\": self.foregroundColor,\n        @\"backgroundColor\": self.backgroundColor,\n    } mutableCopy];\n    if (self.cursorColor) {\n        representation[@\"cursorColor\"] = self.cursorColor;\n    }\n    if (self.colorPaletteOverrides) {\n        representation[@\"colorPaletteOverrides\"] = self.colorPaletteOverrides;\n    }\n    return  representation;\n}\n\n@end\n\n@interface ThemeAppearance ()\n@property(readonly, nonnull) NSDictionary *serializedRepresentation;\n\n- (nullable instancetype)initWithSerializedRepresentation:(nonnull NSDictionary *)serializedRepresentation;\n@end\n\n@implementation ThemeAppearance\n\n- (instancetype)initWithLightOverride:(BOOL)lightOverride darkOverride:(BOOL)darkOverride {\n    if (self = [super init]) {\n        self->_lightOverride = lightOverride;\n        self->_darkOverride = darkOverride;\n    }\n    return self;\n}\n\n- (instancetype)initWithSerializedRepresentation:(NSDictionary *)serializedRepresentation {\n    id lightOverride = serializedRepresentation[@\"lightOverride\"];\n    id darkOverride = serializedRepresentation[@\"darkOverride\"];\n    if ([lightOverride isKindOfClass:NSNumber.class] && [darkOverride isKindOfClass:NSNumber.class]) {\n        return [self initWithLightOverride:[lightOverride boolValue] darkOverride:[darkOverride boolValue]];\n    } else {\n        return nil;\n    }\n}\n\n+ (instancetype)alwaysLight {\n    return [[self alloc] initWithLightOverride:NO darkOverride:YES];\n}\n\n+ (instancetype)alwaysDark {\n    return [[self alloc] initWithLightOverride:YES darkOverride:NO];\n}\n\n- (NSDictionary *)serializedRepresentation {\n    return @{\n        @\"lightOverride\": @(self.lightOverride),\n        @\"darkOverride\": @(self.darkOverride),\n    };\n}\n\n@end\n\nDirectoryWatcher *directoryWatcher;\nNSString *const ThemesUpdatedNotification = @\"ThemesUpdatedNotification\";\nNSString *const ThemeUpdatedNotification = @\"ThemeUpdatedNotification\";\n\n@interface Theme ()\n@property(readonly, nonnull) NSData *data;\n@end\n\n// TODO: Move these to Linux\n#if ISH_LINUX\nchar *(*get_documents_directory)(void);\n#endif\n\n@implementation Theme\n+ (void)initialize {\n    directoryWatcher = [[DirectoryWatcher alloc] initWithURL:self.themesDirectory handler:^{\n        [NSNotificationCenter.defaultCenter postNotificationName:ThemesUpdatedNotification object:nil];\n    }];\n    [NSFileCoordinator addFilePresenter:directoryWatcher];\n    \n    get_documents_directory = get_documents_directory_impl;\n    [NSFileManager.defaultManager createDirectoryAtURL:self.themesDirectory withIntermediateDirectories:YES attributes:nil error:nil];\n}\n\n- (instancetype)initWithName:(NSString *)name palette:(Palette *)palette appearance:(ThemeAppearance *)appearance {\n    Theme *theme = [self initWithName:name lightPalette:palette darkPalette:palette appearance:appearance];\n    return theme;\n}\n\n- (instancetype)initWithName:(NSString *)name lightPalette:(nonnull Palette *)lightPalette darkPalette:(nonnull Palette *)darkPalette appearance:(nullable ThemeAppearance *)appearance {\n    if (self = [super init]) {\n        self->_name = name;\n        self->_lightPalette = lightPalette;\n        self->_darkPalette = darkPalette;\n        self->_appearance = appearance;\n    }\n    return self;\n}\n\n- (nullable instancetype)initWithName:(NSString *)name data:(NSData *)data {\n    id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];\n    if (![json isKindOfClass:NSDictionary.class]) {\n        return nil;\n    }\n    id version = json[@\"version\"];\n    if (![version isKindOfClass:NSNumber.class] || ((NSNumber *)version).integerValue <= 0 || ((NSNumber *)version).integerValue > THEME_VERSION) {\n        NSLog(@\"Rejecting theme %@ with invalid version number\", name);\n        return nil;\n    }\n    id _appearance = json[@\"appearance\"];\n    ThemeAppearance *appearance = [_appearance isKindOfClass:NSDictionary.class] ? [[ThemeAppearance alloc] initWithSerializedRepresentation:_appearance] : nil;\n    id shared = json[@\"shared\"];\n    id light = json[@\"light\"];\n    id dark = json[@\"dark\"];\n    if ([shared isKindOfClass:NSDictionary.class]) {\n        Palette *palette = [[Palette alloc] initWithSerializedRepresentation:shared];\n        return palette ? [self initWithName:name palette:palette appearance:appearance] : nil;\n    } else if ([light isKindOfClass:NSDictionary.class] && [dark isKindOfClass:NSDictionary.class]) {\n        Palette *lightPalette = [[Palette alloc] initWithSerializedRepresentation:light];\n        Palette *darkPalette = [[Palette alloc] initWithSerializedRepresentation:dark];\n        return lightPalette && darkPalette ? [self initWithName:name lightPalette:lightPalette darkPalette:darkPalette appearance:appearance] : nil;\n    } else {\n        NSLog(@\"Rejecting theme %@ with invalid palette(s)\", name);\n        return nil;\n    }\n}\n\n+ (NSArray<Theme *> *)defaultThemes {\n    static NSArray<Theme *> *defaultThemes;\n    if (!defaultThemes) {\n        defaultThemes = @[\n            [[self alloc] initWithName:@\"Default\"\n                          lightPalette:[[Palette alloc] initWithForegroundColor:@\"#000\"\n                                                                backgroundColor:@\"#fff\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:nil]\n                           darkPalette:[[Palette alloc] initWithForegroundColor:@\"#fff\"\n                                                                backgroundColor:@\"#000\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:nil]\n                            appearance:nil],\n            [[self alloc] initWithName:@\"1337\"\n                               palette:[[Palette alloc] initWithForegroundColor:@\"#0f0\"\n                                                                backgroundColor:@\"#000\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:nil]\n                            appearance:ThemeAppearance.alwaysDark],\n            [[self alloc] initWithName:@\"Solarized\"\n                          lightPalette:[[Palette alloc] initWithForegroundColor:@\"#657b83\"\n                                                                backgroundColor:@\"#fdf6e3\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:@[\n                            @\"#073642\",\n                            @\"#dc322f\",\n                            @\"#859900\",\n                            @\"#b58900\",\n                            @\"#268bd2\",\n                            @\"#d33682\",\n                            @\"#2aa198\",\n                            @\"#eee8d5\",\n                            @\"#002b36\",\n                            @\"#cb4b16\",\n                            @\"#586e75\",\n                            @\"#657b83\",\n                            @\"#839496\",\n                            @\"#6c71c4\",\n                            @\"#93a1a1\",\n                            @\"#fdf6e3\",\n                          ]]\n                           darkPalette:[[Palette alloc] initWithForegroundColor:@\"#839496\"\n                                                                backgroundColor:@\"#002b36\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:@[\n                            @\"#073642\",\n                            @\"#dc322f\",\n                            @\"#859900\",\n                            @\"#b58900\",\n                            @\"#268bd2\",\n                            @\"#d33682\",\n                            @\"#2aa198\",\n                            @\"#eee8d5\",\n                            @\"#002b36\",\n                            @\"#cb4b16\",\n                            @\"#586e75\",\n                            @\"#657b83\",\n                            @\"#839496\",\n                            @\"#6c71c4\",\n                            @\"#93a1a1\",\n                            @\"#fdf6e3\",\n                           ]]\n                            appearance:nil\n            ],\n            // Because this is a hidden theme, it needs to be last. There's\n            // logic in UserPreferences and ThemesViewController which will not\n            // work correctly otherwise.\n            [[self alloc] initWithName:@\"Hot Dog Stand\"\n                               palette:[[Palette alloc] initWithForegroundColor:@\"#ff0\"\n                                                                backgroundColor:@\"#f00\"\n                                                                    cursorColor:nil\n                                                          colorPaletteOverrides:nil]\n                            appearance:nil],\n        ];\n    }\n    return defaultThemes;\n}\n\n+ (NSURL *)themesDirectory {\n    return [[NSURL fileURLWithPath:NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject] URLByAppendingPathComponent:@\"themes\"];\n}\n\n+ (NSArray<Theme *> *)userThemes {\n    NSMutableArray<Theme *> *themes = [NSMutableArray new];\n    for (NSURL *file in [NSFileManager.defaultManager contentsOfDirectoryAtURL:self.themesDirectory includingPropertiesForKeys:nil options:0 error:nil]) {\n        NSData *data = [NSData dataWithContentsOfURL:file];\n        if (!data) {\n            continue;\n        }\n        \n        Theme *theme = [[Theme alloc] initWithName:file.lastPathComponent.stringByDeletingPathExtension data:data];\n        if (theme) {\n            [themes addObject:theme];\n        }\n    }\n    [themes sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@\"name\" ascending:YES selector:@selector(localizedStandardCompare:)]]];\n    return themes;\n}\n\n- (NSData *)data {\n    NSMutableDictionary *representation = [@{\n        @\"version\": @(THEME_VERSION),\n    } mutableCopy];\n    if (self.lightPalette == self.darkPalette) {\n        representation[@\"shared\"] = self.lightPalette.serializedRepresentation;\n    } else {\n        representation[@\"light\"] = self.lightPalette.serializedRepresentation;\n        representation[@\"dark\"] = self.darkPalette.serializedRepresentation;\n    }\n    if (self.appearance) {\n        representation[@\"appearance\"] = self.appearance.serializedRepresentation;\n    }\n    return [NSJSONSerialization dataWithJSONObject:representation options:NSJSONWritingSortedKeys | NSJSONWritingPrettyPrinted error:nil];\n}\n\n+ (Theme *)themeForName:(NSString *)name includingDefaultThemes:(BOOL)includingDefaultThemes {\n    // We should pick user themes over default ones, if they have the same name.\n    NSMutableArray<Theme *> *themes = [Theme.userThemes mutableCopy];\n    if (includingDefaultThemes) {\n        [themes addObjectsFromArray:Theme.defaultThemes];\n    }\n    for (Theme *theme in themes) {\n        if ([theme.name isEqualToString:name]) {\n            return theme;\n        }\n    }\n    return nil;\n}\n\n- (void)duplicateAsUserTheme {\n    NSString *name;\n    for (int suffix = 1; [self.class themeForName:name = [NSString stringWithFormat:@\"%@-%d\", self.name, suffix] includingDefaultThemes:NO]; ++suffix);\n    [self.data writeToURL:[self.class.themesDirectory URLByAppendingPathComponent:[name stringByAppendingString:@\".json\"]] atomically:YES];\n}\n\n- (BOOL)addUserTheme {\n    if ([self.class themeForName:self.name includingDefaultThemes:NO]) {\n        return NO;\n    } else {\n        [self.data writeToURL:[self.class.themesDirectory URLByAppendingPathComponent:[self.name stringByAppendingString:@\".json\"]] atomically:YES];\n        return YES;\n    }\n}\n\n- (void)deleteUserTheme {\n    [NSFileManager.defaultManager removeItemAtURL:[self.class.themesDirectory URLByAppendingPathComponent:[self.name stringByAppendingString:@\".json\"]] error:nil];\n}\n\n- (void)replaceWithUserTheme:(Theme *)theme {\n    [theme.data writeToURL:[self.class.themesDirectory URLByAppendingPathComponent:[theme.name stringByAppendingString:@\".json\"]] atomically:YES];\n    if (![self.name isEqualToString:theme.name]) {\n        [self deleteUserTheme];\n        [NSNotificationCenter.defaultCenter postNotificationName:ThemeUpdatedNotification object:theme.name];\n    }\n}\n@end\n"
  },
  {
    "path": "app/ThemeViewController.h",
    "content": "//\n//  ThemeViewController.h\n//  libiSHApp\n//\n//  Created by Saagar Jha on 7/16/22.\n//\n\n#import <UIKit/UIKit.h>\n#import \"Theme.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface ThemeViewController : UITableViewController\n@property Theme *theme;\n@property BOOL isEditable;\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/ThemeViewController.m",
    "content": "//\n//  ThemeViewController.m\n//  libiSHApp\n//\n//  Created by Saagar Jha on 7/16/22.\n//\n\n#import \"ThemeViewController.h\"\n\n#import \"Theme.h\"\n\n#define COLORS 16\n\nstatic NSString *colorNames[] = {\n    @\"Black\",\n    @\"Red\",\n    @\"Green\",\n    @\"Yellow\",\n    @\"Blue\",\n    @\"Magenta\",\n    @\"Cyan\",\n    @\"White\",\n    @\"Bright Black\",\n    @\"Bright Red\",\n    @\"Bright Green\",\n    @\"Bright Yellow\",\n    @\"Bright Blue\",\n    @\"Bright Magenta\",\n    @\"Bright Cyan\",\n    @\"Bright White\",\n};\n\nstruct PaletteTextFields {\n    UITextField *foregroundTextField;\n    UITextField *backgroundTextField;\n    UITextField *cursorTextField;\n    NSArray<UITextField *> *colorTextFields;\n};\n\n@implementation ThemeViewController {\n    UITextField *_nameTextField;\n    UISwitch *_singlePaletteSwitch;\n    UISwitch *_lightOverrideSwitch;\n    UISwitch *_darkOverrideSwitch;\n    BOOL _touchedOverrideSwitches;\n    struct PaletteTextFields _paletteTextFields[2];\n    BOOL _duplicated;\n}\n\n- (UITextField *)detailTextFieldWithText:(NSString *)text monospaced:(BOOL)monospaced {\n    UITextField *textField = [UITextField new];\n    textField.tag = 1;\n    [textField addTarget:self action:@selector(textFieldChanged:) forControlEvents:UIControlEventEditingChanged];\n    textField.text = textField.placeholder = text;\n    textField.translatesAutoresizingMaskIntoConstraints = NO;\n    textField.textAlignment = NSTextAlignmentRight;\n    if (@available(iOS 13.0, *)) {\n        if (monospaced) {\n            textField.font = [UIFont monospacedSystemFontOfSize:textField.font.pointSize weight:UIFontWeightRegular];\n        }\n    }\n    return textField;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \n    self.navigationItem.title = self.theme.name;\n    \n    _nameTextField = [self detailTextFieldWithText:_theme.name monospaced: NO];\n    _singlePaletteSwitch = [UISwitch new];\n    _singlePaletteSwitch.on = self.theme.lightPalette == self.theme.darkPalette;\n    [_singlePaletteSwitch addTarget:self action:@selector(singlePaletteChanged:) forControlEvents:UIControlEventValueChanged];\n    \n    for (int i = 0; i < sizeof(_paletteTextFields) / sizeof(*_paletteTextFields); ++i) {\n        Palette *palette = i ? self.theme.darkPalette : self.theme.lightPalette;\n        _paletteTextFields[i].foregroundTextField = [self detailTextFieldWithText:palette.foregroundColor monospaced:YES];\n        _paletteTextFields[i].backgroundTextField = [self detailTextFieldWithText:palette.backgroundColor monospaced:YES];\n        _paletteTextFields[i].cursorTextField = [self detailTextFieldWithText:palette.cursorColor monospaced:YES];\n        NSMutableArray<UITextField *> *textFields = [NSMutableArray new];\n        for (int j = 0; j < COLORS; ++j) {\n            UITextField *textField = [self detailTextFieldWithText:palette.colorPaletteOverrides ? palette.colorPaletteOverrides[j] : nil monospaced: YES];\n            textField.autocorrectionType = UITextAutocorrectionTypeNo;\n            textField.autocapitalizationType = UITextAutocapitalizationTypeNone;\n            [textFields addObject:textField];\n        }\n        _paletteTextFields[i].colorTextFields = textFields;\n    }\n    \n    if (!self.isEditable) {\n        _singlePaletteSwitch.enabled = NO;\n    }\n    \n    _lightOverrideSwitch = [UISwitch new];\n    _lightOverrideSwitch.on = self.theme.appearance.lightOverride;\n    [_lightOverrideSwitch addTarget:self action:@selector(touchedOverrideSwitch:) forControlEvents:UIControlEventValueChanged];\n    _darkOverrideSwitch = [UISwitch new];\n    _darkOverrideSwitch.on = self.theme.appearance.darkOverride;\n    [_darkOverrideSwitch addTarget:self action:@selector(touchedOverrideSwitch:) forControlEvents:UIControlEventValueChanged];\n    \n    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@\"Duplicate\" style:UIBarButtonItemStylePlain target:self action:@selector(duplicate:)];\n}\n\n- (void)duplicate:(UIBarButtonItem *)sender {\n    [self.theme duplicateAsUserTheme];\n    self->_duplicated = YES;\n    [self.navigationController popViewControllerAnimated:YES];\n}\n\nPalette *createPalette(struct PaletteTextFields *paletteTextFields) {\n    NSMutableArray<NSString *> *colors = [NSMutableArray new];\n    for (UITextField *textField in paletteTextFields->colorTextFields) {\n        if (textField.text.length) {\n            [colors addObject:textField.text];\n        }\n    }\n    return [[Palette alloc] initWithForegroundColor:paletteTextFields->foregroundTextField.text\n                                    backgroundColor:paletteTextFields->backgroundTextField.text\n                                        cursorColor:paletteTextFields->cursorTextField.text.length ? paletteTextFields->cursorTextField.text : nil\n                              colorPaletteOverrides:colors.count == COLORS ? colors : nil];\n}\n\n- (void)viewDidDisappear:(BOOL)animated {\n    [super viewDidDisappear:animated];\n    if (self.isEditable && !self->_duplicated && [self validateTheme]) {\n        Theme *theme;\n        ThemeAppearance *appearance = self->_touchedOverrideSwitches ? [[ThemeAppearance alloc] initWithLightOverride:self->_lightOverrideSwitch.on darkOverride:self->_darkOverrideSwitch.on] : nil;\n        if (_singlePaletteSwitch.on) {\n            theme = [[Theme alloc] initWithName:_nameTextField.text palette:createPalette(_paletteTextFields + 0) appearance:appearance];\n        } else {\n            theme = [[Theme alloc] initWithName:_nameTextField.text lightPalette:createPalette(_paletteTextFields + 0) darkPalette:createPalette(_paletteTextFields + 1) appearance:appearance];\n        }\n        [self.theme replaceWithUserTheme:theme];\n    }\n}\n\n#pragma mark - Table view data source\n\nenum {\n    NameSection,\n    SinglePaletteSection,\n    PaletteSection,\n    PaletteSection2,\n    UIOverrideSection,\n    NumberOfSections,\n};\n\nenum {\n    ForegroundRow,\n    BackgroundRow,\n    CursorRow,\n    NumberOfRows = CursorRow + COLORS + 1,\n};\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {\n    return NumberOfSections;\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    if ([self shouldHideSection:section]) {\n        return 0;\n    }\n    switch (section) {\n        case NameSection:\n            return 1;\n        case SinglePaletteSection:\n            return 1;\n        case PaletteSection:\n        case PaletteSection2:\n            return NumberOfRows;\n        case UIOverrideSection:\n            return 2;\n        default:\n            NSAssert(NO, @\"unhandled section\"); return 0;\n    }\n}\n\n- (BOOL)shouldHideSection:(NSInteger)section {\n    return section == PaletteSection2 && _singlePaletteSwitch.on;\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {\n    if ([self shouldHideSection:section]) {\n        return nil;\n    }\n    switch (section) {\n        case PaletteSection:\n            return _singlePaletteSwitch.on ? @\"Palette\" : @\"Light Palette\";\n        case PaletteSection2:\n            return @\"Dark Palette\";\n        case UIOverrideSection:\n            return @\"UI Overrides\";\n        default:\n            return nil;\n    }\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    if ([self shouldHideSection:section]) {\n        return nil;\n    }\n    switch (section) {\n        case NameSection:\n            return ![_nameTextField.text isEqualToString:self.theme.name] && [Theme themeForName:_nameTextField.text includingDefaultThemes:NO] ? @\"A user theme with this name already exists.\" : nil;\n        case SinglePaletteSection:\n            return @\"When this is enabled, light and dark color schemes will share a single palette.\";\n        case UIOverrideSection:\n            return @\"Use a customized color scheme for user interface elements (keyboard, status bar) rather than one that matches the current palette.\";\n        default:\n            return nil;\n    }\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {\n    return [self shouldHideSection:section] ? CGFLOAT_MIN : UITableViewAutomaticDimension;\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {\n    return [self shouldHideSection:section] ? CGFLOAT_MIN : UITableViewAutomaticDimension;\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@\"ThemeSetting\" forIndexPath:indexPath];\n    [[cell viewWithTag:1] removeFromSuperview];\n    if (self.isEditable) {\n        cell.detailTextLabel.hidden = YES;\n    } else {\n        cell.detailTextLabel.hidden = NO;\n        cell.detailTextLabel.enabled = NO;\n    }\n    cell.accessoryView = nil;\n    switch (indexPath.section) {\n        case NameSection:\n            cell.textLabel.text = @\"Name\";\n            if (self.isEditable) {\n                [cell.contentView addSubview:_nameTextField];\n                [NSLayoutConstraint activateConstraints:@[\n                    [_nameTextField.leadingAnchor constraintEqualToSystemSpacingAfterAnchor:cell.textLabel.trailingAnchor multiplier:1],\n                    [_nameTextField.trailingAnchor constraintEqualToAnchor:cell.detailTextLabel.trailingAnchor],\n                    [_nameTextField.firstBaselineAnchor constraintEqualToAnchor:cell.detailTextLabel.firstBaselineAnchor],\n                ]];\n            } else {\n                cell.detailTextLabel.text = self.theme.name;\n                if (@available(iOS 13.0, *)) {\n                    cell.detailTextLabel.font = [UIFont systemFontOfSize:cell.detailTextLabel.font.pointSize];\n                }\n            }\n            break;\n        case SinglePaletteSection:\n            cell.textLabel.text = @\"Single Palette\";\n            cell.detailTextLabel.hidden = YES;\n            cell.accessoryView = _singlePaletteSwitch;\n            break;\n        case PaletteSection:\n        case PaletteSection2: {\n            UITextField *detailTextField;\n            switch (indexPath.row) {\n                case ForegroundRow:\n                    cell.textLabel.text = @\"Foreground Color\";\n                    detailTextField = _paletteTextFields[indexPath.section - PaletteSection].foregroundTextField;\n                    break;\n                case BackgroundRow:\n                    cell.textLabel.text = @\"Background Color\";\n                    detailTextField = _paletteTextFields[indexPath.section - PaletteSection].backgroundTextField;\n                    break;\n                case CursorRow:\n                    cell.textLabel.text = @\"Cursor Color\";\n                    detailTextField = _paletteTextFields[indexPath.section - PaletteSection].cursorTextField;\n                    break;\n                default:\n                    cell.textLabel.text = colorNames[indexPath.row - CursorRow - 1];\n                    detailTextField = _paletteTextFields[indexPath.section - PaletteSection].colorTextFields[indexPath.row - CursorRow - 1];\n                    break;\n            }\n            if (self.isEditable) {\n                [cell.contentView addSubview:detailTextField];\n                [NSLayoutConstraint activateConstraints:@[\n                    [detailTextField.leadingAnchor constraintEqualToSystemSpacingAfterAnchor:cell.textLabel.trailingAnchor multiplier:1],\n                    [detailTextField.trailingAnchor constraintEqualToAnchor:cell.detailTextLabel.trailingAnchor],\n                    [detailTextField.firstBaselineAnchor constraintEqualToAnchor:cell.detailTextLabel.firstBaselineAnchor],\n                ]];\n            } else {\n                cell.detailTextLabel.text = detailTextField.text;\n                if (@available(iOS 13.0, *)) {\n                    cell.detailTextLabel.font = [UIFont monospacedSystemFontOfSize:cell.detailTextLabel.font.pointSize weight:UIFontWeightRegular];\n                }\n            }\n            break;\n        }\n        case UIOverrideSection:\n            cell.detailTextLabel.hidden = YES;\n            switch (indexPath.row) {\n                case 0:\n                    cell.textLabel.text = @\"Use Dark UI for Light Color Scheme\";\n                    cell.accessoryView = self->_lightOverrideSwitch;\n                    break;\n                case 1:\n                    cell.textLabel.text = @\"Use Light UI for Dark Color Scheme\";\n                    cell.accessoryView = self->_darkOverrideSwitch;\n                    break;\n                default:\n                    NSAssert(NO, @\"Invalid row\");\n            }\n            break;\n    }\n    \n    if (!self.isEditable) {\n        cell.textLabel.enabled = NO;\n    }\n    \n    return cell;\n}\n\n- (BOOL)validateTheme {\n    BOOL validName = _nameTextField.text.length && ([_nameTextField.text isEqualToString:self.theme.name] || ![Theme themeForName:_nameTextField.text includingDefaultThemes:NO]);\n    _nameTextField.textColor = validName ? nil : UIColor.systemRedColor;\n    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:NameSection] withRowAnimation:UITableViewRowAnimationNone];\n    BOOL validColors = YES;\n    BOOL (^validColor)(UITextField *) = ^(UITextField *textField) {\n        BOOL valid = !![[UIColor alloc] ish_initWithHexString:textField.text];\n        textField.textColor = valid ? nil : UIColor.systemRedColor;\n        return valid;\n    };\n    for (int i = 0; i < sizeof(_paletteTextFields) / sizeof(*_paletteTextFields) - _singlePaletteSwitch.on; ++i) {\n        validColors &= validColor(_paletteTextFields[i].foregroundTextField);\n        validColors &= validColor(_paletteTextFields[i].backgroundTextField);\n        validColors &= !_paletteTextFields[i].cursorTextField.text.length || validColor(_paletteTextFields[i].cursorTextField);\n        int empty = 0;\n        int valid = 0;\n        for (int j = 0; j < COLORS; ++j) {\n            empty += !_paletteTextFields[i].colorTextFields[j].text.length;\n            valid += validColor(_paletteTextFields[i].colorTextFields[j]);\n        }\n        validColors &= (empty == COLORS || valid == COLORS);\n    }\n    return !(self.navigationItem.hidesBackButton = !validName || !validColors);\n}\n\n- (void)textFieldChanged:(UITextField *)sender {\n    // Hack to keep the keyboard up across a table view update\n    UITextRange *selectedRange = sender.isFirstResponder ? sender.selectedTextRange : nil;\n    [self validateTheme];\n    if (selectedRange) {\n        [sender becomeFirstResponder];\n        [sender setSelectedTextRange:selectedRange];\n    }\n}\n\n- (void)singlePaletteChanged:(UISwitch *)sender {\n    [self.tableView performBatchUpdates:^{\n        for (int i = 0; i < NumberOfRows; ++i) {\n            if (sender.on) {\n                [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:PaletteSection2]] withRowAnimation:UITableViewRowAnimationFade];\n            } else {\n                [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:PaletteSection2]] withRowAnimation:UITableViewRowAnimationFade];\n            }\n        }\n        [self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(PaletteSection, PaletteSection2 - PaletteSection)] withRowAnimation:UITableViewRowAnimationAutomatic];\n    } completion:nil];\n    [self validateTheme];\n}\n\n- (void)touchedOverrideSwitch:(UISwitch *)sender {\n    self->_touchedOverrideSwitches = YES;\n}\n\n@end\n"
  },
  {
    "path": "app/ThemesViewController.h",
    "content": "//\n//  ThemesViewController.h\n//  iSH\n//\n//  Created by Saagar Jha on 2/25/22.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface ThemesViewController : UITableViewController<UIDocumentPickerDelegate>\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/ThemesViewController.m",
    "content": "//\n//  ThemesViewController.m\n//  iSH\n//\n//  Created by Saagar Jha on 2/25/22.\n//\n\n#import \"ThemesViewController.h\"\n\n#import \"NSObject+SaneKVO.h\"\n#import \"Theme.h\"\n#import \"ThemeViewController.h\"\n#import \"UserPreferences.h\"\n\n@implementation ThemesViewController {\n    BOOL _singleRowEditing;\n    BOOL _importButtonEditingMode;\n    BOOL _pendingUpdate;\n    Theme *_theme;\n    NSMutableArray<Theme *> *_defaultThemes;\n    NSMutableArray<Theme *> *_userThemes;\n    BOOL _preferUserTheme;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \n    [UserPreferences.shared observe:@[@\"theme\"]\n                            options:0 owner:self usingBlock:^(typeof(self) self) {\n        dispatch_async(dispatch_get_main_queue(), ^{\n            [self deferredReload];\n        });\n    }];\n    \n    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updateThemes:) name:ThemesUpdatedNotification object:nil];\n    \n    self.navigationItem.rightBarButtonItem = self.editButtonItem;\n    [self deferredReload];\n}\n\n- (void)updateThemes:(NSNotification *)notification {\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [self deferredReload];\n    });\n}\n\n- (void)deferredReload {\n    if (self.isEditing) {\n        self->_pendingUpdate = YES;\n    } else {\n        self->_defaultThemes = [Theme.defaultThemes mutableCopy];\n        self->_userThemes = [Theme.userThemes mutableCopy];\n        [self updateTheme:UserPreferences.shared.theme];\n        [self.tableView reloadData];\n        self->_pendingUpdate = NO;\n    }\n}\n\n- (void)updateTheme:(Theme *)theme {\n    self->_theme = theme;\n    self->_preferUserTheme = NO;\n    for (Theme *theme in self->_defaultThemes) {\n        if ([self->_theme.name isEqualToString:theme.name]) {\n            for (Theme *theme in self->_userThemes) {\n                if ([self->_theme.name isEqualToString:theme.name]) {\n                    self->_preferUserTheme = YES;\n                }\n            }\n            break;\n        }\n    }\n}\n\n- (void)setEditing:(BOOL)editing animated:(BOOL)animated {\n    self->_importButtonEditingMode = editing;\n    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:ImportSection] withRowAnimation:UITableViewRowAnimationAutomatic];\n    \n    if (!editing && self->_pendingUpdate) {\n        [self deferredReload];\n    }\n\n    [super setEditing:editing animated:animated];\n}\n\n#pragma mark - Table view data source\n\nenum {\n    DefaultSection,\n    UserSection,\n    ImportSection,\n    NumberOfSections,\n};\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {\n    return NumberOfSections;\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {\n    switch (section) {\n        case DefaultSection:\n            return self->_defaultThemes.count - ![self->_theme.name isEqualToString:@\"Hot Dog Stand\"];\n        case UserSection:\n            return self->_userThemes.count;\n        case ImportSection:\n            return self->_importButtonEditingMode && !self->_singleRowEditing;\n        default:\n            NSAssert(NO, @\"unhandled section\"); return 0;\n    }\n}\n\n- (BOOL)shouldHideSection:(NSInteger)section {\n    return (section == UserSection && !self->_userThemes.count) || (section == ImportSection && (self->_singleRowEditing || !self->_importButtonEditingMode));\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {\n    if ([self shouldHideSection:section]) {\n        return nil;\n    }\n    switch (section) {\n        case DefaultSection:\n            return @\"Default Themes\";\n        case UserSection:\n            return @\"User Themes\";\n        case ImportSection:\n            return nil;\n        default:\n            NSAssert(NO, @\"unhandled section\"); return nil;\n    }\n}\n\n- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {\n    if ([self shouldHideSection:section]) {\n        return nil;\n    }\n    switch (section) {\n        case DefaultSection:\n            return self->_preferUserTheme ? [NSString stringWithFormat:@\"The default theme \\\"%@\\\" is currently being overridden by a user theme.\", self->_theme.name] : nil;\n        case ImportSection:\n            return @\"User themes are stored in the iSH documents directory, under the \\\"themes\\\" folder. You can access them within iSH by running\\n\\n# mount -t real \\\"$(cat /proc/ish/documents)/themes\\\" [folder]\\n\\nand manipulating them from there.\";\n        default:\n            return nil;\n    }\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {\n    return [self shouldHideSection:section] ? CGFLOAT_MIN : UITableViewAutomaticDimension;\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {\n    return [self shouldHideSection:section] ? CGFLOAT_MIN : UITableViewAutomaticDimension;\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@\"Theme\" forIndexPath:indexPath];\n    \n    cell.textLabel.textColor = indexPath.section == ImportSection ? cell.tintColor : nil;\n    cell.textLabel.enabled = YES;\n    \n    Theme *theme;\n    \n    switch (indexPath.section) {\n        case DefaultSection:\n            theme = self->_defaultThemes[indexPath.row];\n            break;\n        case UserSection:\n            theme = self->_userThemes[indexPath.row];\n            break;\n        case ImportSection:\n            cell.textLabel.text = @\"Import Theme\";\n            cell.editingAccessoryType = UITableViewCellAccessoryNone;\n            return cell;\n    }\n    \n    cell.textLabel.text = theme.name;\n    cell.accessoryType = [theme.name isEqualToString:self->_theme.name] && (!self->_preferUserTheme || indexPath.section == UserSection) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;\n    cell.textLabel.enabled = ![theme.name isEqualToString:self->_theme.name] || indexPath.section != DefaultSection || !self->_preferUserTheme;\n    cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;\n    \n    return cell;\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (tableView.isEditing) {\n        ThemeViewController *themeViewController = [self.storyboard instantiateViewControllerWithIdentifier:@\"Theme\"];\n        switch (indexPath.section) {\n            case DefaultSection:\n                themeViewController.theme = self->_defaultThemes[indexPath.row];\n                themeViewController.isEditable = NO;\n                break;\n            case UserSection:\n                themeViewController.theme = self->_userThemes[indexPath.row];\n                themeViewController.isEditable = YES;\n                break;\n            case ImportSection:\n                [self importTheme];\n                return;\n        }\n        [self.navigationController pushViewController:themeViewController animated:YES];\n        [self setEditing:NO animated:YES];\n    } else {\n        Theme *theme;\n        switch (indexPath.section) {\n            case DefaultSection:\n                theme = self->_defaultThemes[indexPath.row];\n                break;\n            case UserSection:\n                theme = self->_userThemes[indexPath.row];\n                break;\n        }\n        [self updateTheme:theme];\n        [self.tableView performBatchUpdates:^{\n            [self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(DefaultSection, UserSection - DefaultSection)] withRowAnimation:UITableViewRowAnimationAutomatic];\n        } completion:nil];\n        UserPreferences.shared.theme = theme;\n    }\n}\n\n- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {\n    switch (indexPath.section) {\n        case UserSection:\n            return UITableViewCellEditingStyleDelete;\n        case ImportSection:\n            return UITableViewCellEditingStyleInsert;\n        default:\n            return UITableViewCellEditingStyleNone;\n    }\n}\n\n- (void)deleteUserThemeAtIndexPath:(NSIndexPath *)indexPath {\n    [self->_userThemes[indexPath.row] deleteUserTheme];\n    [self->_userThemes removeObjectAtIndex:indexPath.row];\n    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];\n}\n\n- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {\n    switch (editingStyle) {\n        case UITableViewCellEditingStyleInsert:\n            [self importTheme];\n            break;\n        case UITableViewCellEditingStyleDelete:\n            [self deleteUserThemeAtIndexPath:indexPath];\n            break;\n        default:\n            NSAssert(NO, @\"Invalid editing style\");\n    }\n}\n\n- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {\n    if (self.isEditing) {\n        return nil;\n    } else {\n        NSMutableArray<UIContextualAction *> *actions = [NSMutableArray arrayWithObject:[UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@\"Duplicate\" handler:^(UIContextualAction *action, UIView *sourceView, void (^completionHandler)(BOOL)) {\n            [(indexPath.section == DefaultSection ? self->_defaultThemes : self->_userThemes)[indexPath.row] duplicateAsUserTheme];\n            [tableView performBatchUpdates:^{\n                [tableView reloadSections:[NSIndexSet indexSetWithIndex:UserSection] withRowAnimation:UITableViewRowAnimationAutomatic];\n            } completion:nil];\n            completionHandler(YES);\n        }]];\n        if (indexPath.section == UserSection) {\n            [actions addObject:[UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@\"Delete\" handler:^(UIContextualAction *action, UIView *sourceView, void (^completionHandler)(BOOL)) {\n                [self deleteUserThemeAtIndexPath:indexPath];\n                completionHandler(YES);\n            }]];\n        }\n        return [UISwipeActionsConfiguration configurationWithActions:actions];\n    }\n}\n\n- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath {\n    self->_singleRowEditing = YES;\n    [super tableView:tableView willBeginEditingRowAtIndexPath:indexPath];\n}\n\n- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {\n    [super tableView:tableView didEndEditingRowAtIndexPath:indexPath];\n    self->_singleRowEditing = NO;\n}\n\n- (void)importTheme {\n    UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[ @\"public.json\" ] inMode:UIDocumentPickerModeOpen];\n    picker.delegate = self;\n    if (@available(iOS 13, *)) {\n    } else {\n        picker.allowsMultipleSelection = YES;\n    }\n    [self presentViewController:picker animated:true completion:nil];\n}\n\n- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {\n    [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];\n}\n\n- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {\n    for (NSURL *url in urls) {\n        [url startAccessingSecurityScopedResource];\n        [[[Theme alloc] initWithName:url.lastPathComponent.stringByDeletingPathExtension data:[NSData dataWithContentsOfURL:url]] addUserTheme];\n        [url stopAccessingSecurityScopedResource];\n    }\n    [self documentPickerWasCancelled:controller];\n    [self setEditing:NO animated:YES];\n}\n\n@end\n"
  },
  {
    "path": "app/UIApplication+OpenURL.h",
    "content": "//\n//  UIApplication+OpenURL.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface UIApplication (OpenURL)\n\n+ (void)openURL:(NSString *)url;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/UIApplication+OpenURL.m",
    "content": "//\n//  UIApplication+OpenURL.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import \"UIApplication+OpenURL.h\"\n\n@implementation UIApplication (OpenURL)\n\n+ (void)openURL:(NSString *)url {\n    [[self sharedApplication] openURL:[NSURL URLWithString:url] options:@{} completionHandler:nil];\n}\n\n@end\n"
  },
  {
    "path": "app/UITests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/UITests/Screenshots.m",
    "content": "//\n//  Screenshots.m\n//  iSHUITests\n//\n//  Created by Theodore Dubois on 12/18/20.\n//\n\n#import <UIKit/UIKit.h>\n#import <XCTest/XCTest.h>\n#import \"iSHUITests-Swift.h\"\n\n@interface Screenshots : XCTestCase\n\n@property XCUIApplication *app;\n\n@end\n\n@implementation Screenshots\n\n- (void)setUp {\n    self.continueAfterFailure = NO;\n    XCUIApplication *app = self.app = [XCUIApplication new];\n    [Snapshot setupSnapshot:app waitForAnimations:NO];\n    NSString *hostnameOverride = nil;\n    switch (UIDevice.currentDevice.userInterfaceIdiom) {\n        case UIUserInterfaceIdiomPad: hostnameOverride = @\"iPad\"; break;\n        case UIUserInterfaceIdiomPhone: hostnameOverride = @\"iPhone\"; break;\n        default: XCTFail(@\"unknown UI idiom\");\n    }\n    app.launchArguments = [app.launchArguments arrayByAddingObjectsFromArray:@[@\"-hostnameOverride\", hostnameOverride]];\n    [app launch];\n    XCTAssert([app.webViews.staticTexts.firstMatch waitForExistenceWithTimeout:10]);\n    [self chooseTheme:@\"Solarized\"];\n}\n\n- (XCUIElementQuery *)terminalLines {\n    return self.app.webViews.staticTexts;\n}\n\n- (XCUIElementQuery *)terminalLinesContaining:(NSString *)text {\n    return [self.terminalLines matchingPredicate:[NSPredicate predicateWithFormat:@\"label CONTAINS %@\", text]];\n}\n\n- (void)waitForTerminalText:(NSString *)text timeout:(NSUInteger)timeout {\n    XCTAssert([[self terminalLinesContaining:text].firstMatch waitForExistenceWithTimeout:timeout]);\n}\n\n- (void)waitForPromptWithTimeout:(NSUInteger)timeout {\n    // Waits for the last text thing to be a prompt\n    XCUIElementQuery *terminalLines = self.terminalLines;\n    NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(id _, NSDictionary *__) {\n        XCUIElement *lastLine = nil;\n        // Loop this because -count and -elementBoundByIndex will take two separate UI snapshots, so the index could become invalid.\n        while (!lastLine.exists)\n            lastLine = terminalLines.allElementsBoundByIndex.lastObject;\n        NSString *lastLineText = lastLine.label;\n        XCTAssertNotNil(lastLineText);\n        return [lastLineText hasSuffix:@\":~#\"];\n    }];\n    [self waitForExpectations:@[[self expectationForPredicate:pred evaluatedWithObject:nil handler:nil]] timeout:timeout];\n}\n\n- (void)runCommand:(NSString *)command timeout:(NSUInteger)timeout {\n    [self.app typeText:[NSString stringWithFormat:@\"%@\\n\", command]];\n    [self waitForPromptWithTimeout:timeout];\n}\n\n- (void)chooseTheme:(NSString *)name {\n    [self.app.buttons[@\"Settings\"] tap];\n    [self.app.tables.staticTexts[@\"Appearance\"] tap];\n    [self.app.tables.staticTexts[@\"Theme\"] tap];\n    [self.app.tables.staticTexts[name] tap];\n    [self.app.navigationBars[@\"Themes\"].buttons[@\"Appearance\"] tap];\n    [self.app.navigationBars[@\"Appearance\"].buttons[@\"Settings\"] tap];\n    [self.app.navigationBars[@\"Settings\"].buttons[@\"Done\"] tap];\n}\n\n- (void)snapshot:(NSString *)name order:(NSUInteger)order {\n    name = [NSString stringWithFormat:@\"%02u%@\", (unsigned) order, name];\n    [Snapshot snapshot:name timeWaitingForIdle:10];\n}\n\n- (void)testSystemInfo {\n    [self runCommand:@\"uname -a\" timeout:5];\n    [self snapshot:@\"systeminfo\" order:1];\n}\n\n- (void)testLanguages {\n    [self runCommand:@\"apk add build-base python3\" timeout:120];\n    [self runCommand:@\"printf '#include <stdio.h>\\\\nint main() { printf(\\\"Hello, iSH!\\\\\\\\n\\\"); }' > hello.c\" timeout:5];\n    [self runCommand:@\"gcc hello.c && ./a.out\" timeout:5];\n    [self runCommand:@\"python3 -c 'print(\\\"Hello, iSH!\\\")'\" timeout:5];\n    [self snapshot:@\"languages\" order:2];\n}\n\n- (void)testEditorsInTmux {\n    [self runCommand:@\"apk add vim nano tmux\" timeout:120];\n    [self runCommand:@\"tmux new-session -d -s foo nano\" timeout:5];\n    [self runCommand:@\"tmux split-window -v vim\" timeout:5];\n    [self runCommand:@\"tmux select-layout even-vertical\" timeout:5];\n    [self.app typeText:@\"tmux attach -t foo\\n\"];\n    [self waitForTerminalText:@\"GNU nano\" timeout:30];\n    [self snapshot:@\"editorsintmux\" order:3];\n}\n\n- (void)testEmacs {\n    [self runCommand:@\"apk add emacs\" timeout:120];\n    [self.app typeText:@\"emacs\\n\"];\n    [self waitForTerminalText:@\"Welcome to GNU Emacs\" timeout:30];\n    [self snapshot:@\"emacs\" order:4];\n}\n\n@end\n"
  },
  {
    "path": "app/UITests/Screenshots.xctestplan",
    "content": "{\n  \"configurations\" : [\n    {\n      \"id\" : \"963A2374-D06F-45E9-AD7A-628EE51BB5BF\",\n      \"name\" : \"Configuration 1\",\n      \"options\" : {\n\n      }\n    }\n  ],\n  \"defaultOptions\" : {\n    \"codeCoverage\" : false\n  },\n  \"testTargets\" : [\n    {\n      \"skippedTests\" : [\n        \"Screenshots\\/testEditorsInTmux\",\n        \"Screenshots\\/testEmacs\",\n        \"Screenshots\\/testLanguages\",\n        \"UITests\"\n      ],\n      \"target\" : {\n        \"containerPath\" : \"container:iSH.xcodeproj\",\n        \"identifier\" : \"BB41591B255EF9E300E0950C\",\n        \"name\" : \"iSHUITests\"\n      }\n    }\n  ],\n  \"version\" : 1\n}\n"
  },
  {
    "path": "app/UITests/UITests.m",
    "content": "//\n//  UITests.m\n//  UITests\n//\n//  Created by Theodore Dubois on 11/13/20.\n//\n\n#import <XCTest/XCTest.h>\n\n@interface UITests : XCTestCase\n@end\n\n@implementation UITests\n\n- (void)setUp {\n    self.continueAfterFailure = NO;\n}\n\n@end\n"
  },
  {
    "path": "app/UIViewController+Extras.h",
    "content": "//\n//  UIViewController+Extras.h\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface UIViewController (Extras)\n\n- (void)presentError:(NSError *)error title:(NSString *)title;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/UIViewController+Extras.m",
    "content": "//\n//  UIViewController+Extras.m\n//  iSH\n//\n//  Created by Theodore Dubois on 9/23/18.\n//\n\n#import \"UIViewController+Extras.h\"\n\n@implementation UIViewController (Extras)\n\n- (void)presentError:(NSError *)error title:(NSString *)title {\n    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];\n    [alert addAction:[UIAlertAction actionWithTitle:@\"OK\" style:UIAlertActionStyleDefault handler:nil]];\n    [self presentViewController:alert animated:YES completion:nil];\n}\n\n@end\n"
  },
  {
    "path": "app/UpgradeRootViewController.h",
    "content": "//\n//  UpgradeRootViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 11/27/21.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface UpgradeRootViewController : UIViewController\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/UpgradeRootViewController.m",
    "content": "//\n//  UpgradeRootViewController.m\n//  iSH\n//\n//  Created by Theodore Dubois on 11/27/21.\n//\n\n#import \"UpgradeRootViewController.h\"\n#import \"AppDelegate.h\"\n#import \"TerminalView.h\"\n#import \"CurrentRoot.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/init.h\"\n#include \"fs/devices.h\"\n\n@interface UpgradeRootViewController ()\n\n@property (weak, nonatomic) IBOutlet TerminalView *terminalView;\n@property (weak, nonatomic) IBOutlet UIBarButtonItem *upgradeButton;\n@property (nonatomic) Terminal *terminal;\n@property (nonatomic) struct tty *tty;\n@property (nonatomic) int upgradePid;\n\n@end\n\n@implementation UpgradeRootViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n#if !ISH_LINUX\n    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(processExited:) name:ProcessExitedNotification object:nil];\n\n    lock(&pids_lock);\n    current = pid_get_task(1); // pray\n    unlock(&pids_lock);\n    self.terminal = [Terminal createPseudoTerminal:&self->_tty];\n    current = NULL;\n    \n    self.terminalView.terminal = self.terminal;\n#endif\n    self.upgradeButton.enabled = NO;\n    if (FsNeedsRepositoryUpdate()) {\n        self.upgradeButton.enabled = YES;\n        [self printToTerminal:@\"# /sbin/apk upgrade\"];\n    } else {\n        [self showAlertWithTitle:@\"fuck\" message:@\"No update needed. If you're seeing this message, there's a bug.\"];\n    }\n}\n\n- (void)printToTerminal:(NSString *)message, ... {\n    va_list args;\n    va_start(args, message);\n    message = [[NSString alloc] initWithFormat:message arguments:args];\n    message = [message stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\"\\r\\n\"];\n    [self.terminal sendOutput:message.UTF8String length:(int)[message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];\n}\n\n- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message, ... {\n    va_list args;\n    va_start(args, message);\n    message = [[NSString alloc] initWithFormat:message arguments:args];\n    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];\n    [alert addAction:[UIAlertAction actionWithTitle:@\"OK\" style:UIAlertActionStyleDefault handler:nil]];\n    [self presentViewController:alert animated:YES completion:nil];\n}\n\n#if !ISH_LINUX\n- (void)processExited:(NSNotification *)notif {\n    int pid = [notif.userInfo[@\"pid\"] intValue];\n    if (pid != self.upgradePid)\n        return;\n    self.upgradePid = 0;\n    [self setDismissable:YES];\n    int code = [notif.userInfo[@\"code\"] intValue];\n    [self printToTerminal:@\"\\n\"];\n    if (code != 0) {\n        [self printToTerminal:@\"Upgrade failed with exit status %d.\\nPlease send a bug report.\\n\", code];\n    } else {\n        lock(&pids_lock);\n        current = pid_get_task(1); // pray\n        unlock(&pids_lock);\n        FsUpdateRepositories();\n        current = NULL;\n        [self printToTerminal:@\"Upgrade complete!\\nIf anything that was working before stops working, please send a bug report.\\n\"];\n    }\n    [self.terminal destroy];\n    self.terminal = nil;\n}\n#endif\n\n- (int)startUpgrade {\n    if (self.upgradePid != 0)\n        return _EEXIST;\n#if !ISH_LINUX\n    int err = become_new_init_child();\n    if (err < 0)\n        return err;\n    FsUpdateOnlyRepositoriesFile();\n    NSString *stdioFile = [NSString stringWithFormat:@\"/dev/pts/%d\", self.tty->num];\n    err = create_stdio(stdioFile.fileSystemRepresentation, TTY_PSEUDO_SLAVE_MAJOR, self.tty->num);\n    if (err < 0)\n        return err;\n    err = do_execve(\"/sbin/apk\", 2, \"/sbin/apk\\0upgrade\\0\", \"TERM=xterm-256color\\0\");\n    if (err < 0)\n        return err;\n    self.upgradePid = current->pid;\n    task_start(current);\n    current = NULL;\n    return 0;\n#else\n    return _ENOSYS;\n#endif\n}\n\n- (IBAction)upgrade:(id)sender {\n    self.upgradeButton.enabled = NO;\n    [self setDismissable:NO];\n    [self printToTerminal:@\"\\n\"];\n    int err = [self startUpgrade];\n    if (err < 0) {\n        [self showAlertWithTitle:@\"Failed to start upgrade\" message:@\"error %d\", err];\n    }\n}\n\n- (void)setDismissable:(BOOL)dismissable {\n    [self.navigationItem setHidesBackButton:!dismissable animated:YES];\n    self.navigationController.interactivePopGestureRecognizer.enabled = dismissable;\n    if (@available(iOS 13, *)) {\n        self.modalInPresentation = !dismissable;\n    }\n}\n\n- (void)dealloc {\n    [self.terminal destroy];\n    if (self.tty != NULL)\n        tty_release(self.tty);\n}\n\n@end\n"
  },
  {
    "path": "app/UserPreferences.h",
    "content": "//\n//  UserPreferences.h\n//  iSH\n//\n//  Created by Charlie Melbye on 11/12/18.\n//\n\n#import <UIKit/UIKit.h>\n#import \"Theme.h\"\n\ntypedef NS_ENUM(NSInteger, CapsLockMapping) {\n    __CapsLockMapFirst = 0,\n    CapsLockMapNone = 0,\n    CapsLockMapControl,\n    CapsLockMapEscape,\n    __CapsLockMapLast,\n};\n\ntypedef enum : NSUInteger {\n    __OptionMapFirst = 0,\n    OptionMapNone = 0,\n    OptionMapEsc,\n    __OptionMapLast,\n} OptionMapping;\n\ntypedef NS_ENUM(NSInteger, CursorStyle) {\n    __CursorStyleFirst = 0,\n    CursorStyleBlock = 0,\n    CursorStyleBeam,\n    CursorStyleUnderline,\n    __CursorStyleLast,\n};\n\ntypedef NS_ENUM(NSInteger, ColorScheme) {\n    __ColorSchemeFirst = 0,\n    ColorSchemeMatchSystem = 0,\n    ColorSchemeAlwaysLight,\n    ColorSchemeAlwaysDark,\n    __ColorSchemeLast,\n};\n\nNS_ASSUME_NONNULL_BEGIN\n\nextern NSString *const kThemeForegroundColor;\nextern NSString *const kThemeBackgroundColor;\n\n@interface UserPreferences : NSObject\n\n@property CapsLockMapping capsLockMapping;\n@property OptionMapping optionMapping;\n@property BOOL backtickMapEscape;\n@property BOOL hideExtraKeysWithExternalKeyboard;\n@property BOOL overrideControlSpace;\n@property BOOL hideStatusBar;\n@property (nonatomic) Theme *theme;\n@property (nonatomic) Palette *palette;\n@property BOOL shouldDisableDimming;\n@property (null_resettable) NSString *fontFamily;\n@property (readonly) NSString *fontFamilyUserFacingName;\n@property (readonly) UIFont *approximateFont;\n@property NSNumber *fontSize;\n@property ColorScheme colorScheme;\n@property (readonly) BOOL requestingDarkAppearance;\n@property (readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(ios(12.0));\n@property (readonly) UIKeyboardAppearance keyboardAppearance;\n@property CursorStyle cursorStyle;\n@property (readonly) NSString *htermCursorShape;\n@property BOOL blinkCursor;\n@property (readonly) UIStatusBarStyle statusBarStyle;\n@property NSArray<NSString *> *launchCommand;\n@property NSArray<NSString *> *bootCommand;\n@property NSString *hostnameOverride;\n// Same as above but returns nil if the user has never set the hostname\n@property (readonly) NSString *_hostnameOverride;\n\n+ (instancetype)shared;\n\n- (BOOL)hasChangedLaunchCommand;\n\n@end\n\nextern NSString *const kPreferenceLaunchCommandKey;\nextern NSString *const kPreferenceBootCommandKey;\nextern NSString *const kHostnameOverrideKey;\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "app/UserPreferences.m",
    "content": "//\n//  UserPreferences.m\n//  iSH\n//\n//  Created by Charlie Melbye on 11/12/18.\n//\n\n#import \"UserPreferences.h\"\n#import \"fs/proc/ish.h\"\n\n// IMPORTANT: If you add a constant here and expose it via UserPreferences,\n// consider if it also needs to be exposed as a friendly preference and included\n// in the KVO list below. (In most circumstances, the answer is \"yes\".)\nstatic NSString *const kPreferenceCapsLockMappingKey = @\"Caps Lock Mapping\";\nstatic NSString *const kPreferenceOptionMappingKey = @\"Option Mapping\";\nstatic NSString *const kPreferenceBacktickEscapeKey = @\"Backtick Mapping Escape\";\nstatic NSString *const kPreferenceHideExtraKeysWithExternalKeyboardKey = @\"Hide Extra Keys With External Keyboard\";\nstatic NSString *const kPreferenceOverrideControlSpaceKey = @\"Override Control Space\";\nstatic NSString *const kPreferenceFontFamilyKey = @\"Font Family\";\nstatic NSString *const kPreferenceFontSizeKey = @\"Font Size\";\nstatic NSString *const kPreferenceThemeKey = @\"ModernTheme\";\nstatic NSString *const kPreferenceDisableDimmingKey = @\"Disable Dimming\";\nNSString *const kPreferenceLaunchCommandKey = @\"Init Command\";\nNSString *const kPreferenceBootCommandKey = @\"Boot Command\";\nstatic NSString *const kPreferenceCursorStyleKey = @\"Cursor Style\";\nstatic NSString *const kPreferenceBlinkCursorKey = @\"Blink Cursor\";\nNSString *const kPreferenceHideStatusBarKey = @\"Status Bar\";\nstatic NSString *const kPreferenceColorSchemeKey = @\"Color Scheme\";\n// This key has a different naming scheme because it's used in UI tests as a\n// CLI argument.\nNSString *const kHostnameOverrideKey = @\"hostnameOverride\";\n\nNSDictionary<NSString *, NSString *> *friendlyPreferenceMapping;\nNSDictionary<NSString *, NSString *> *friendlyPreferenceReverseMapping;\nNSDictionary<NSString *, NSString *> *kvoProperties;\n\nstatic NSString *const kSystemMonospacedFontName = @\"ui-monospace\";\n\n@interface UserPreferences () {\n    BOOL _hostnameIsOverridden;\n}\n- (void)updateTheme;\n@end\n\nchar **get_all_defaults_keys_impl(void) {\n    NSArray<NSString *> *preferenceKeys = NSUserDefaults.standardUserDefaults.dictionaryRepresentation.allKeys;\n    char **entries = malloc((preferenceKeys.count + 1) * sizeof(*entries));\n    for (NSUInteger i = 0; i < preferenceKeys.count; ++i)\n        entries[i] = strdup(preferenceKeys[i].UTF8String);\n    entries[preferenceKeys.count] = NULL;\n    return entries;\n}\n\nchar *get_friendly_name_impl(const char *name) {\n    const char *friendly_name = friendlyPreferenceReverseMapping[[NSString stringWithUTF8String:name]].UTF8String;\n    if (friendly_name == NULL)\n        return NULL;\n    return strdup(friendly_name);\n}\n\nchar *get_underlying_name_impl(const char *name) {\n    return strdup(friendlyPreferenceMapping[[NSString stringWithUTF8String:name]].UTF8String);\n}\n\nbool get_user_default_impl(const char *name, char **buffer, size_t *size) {\n    id value = [NSUserDefaults.standardUserDefaults objectForKey:[NSString stringWithUTF8String:name]];\n    // Since we are writing with fragments, wrap the object in an array to have\n    // a top-level object to check.\n    if (!value || ![NSJSONSerialization isValidJSONObject:@[value]]) {\n        return false;\n    }\n    NSError *error;\n    NSJSONWritingOptions options = NSJSONWritingFragmentsAllowed | NSJSONWritingSortedKeys | NSJSONWritingPrettyPrinted;\n    if (@available(iOS 13.0, *)) {\n        options |= NSJSONWritingWithoutEscapingSlashes;\n    }\n    NSData *data = [NSJSONSerialization dataWithJSONObject:value options:options error:&error];\n    if (error) {\n        return false;\n    }\n    *buffer = malloc(data.length + 1);\n    memcpy(*buffer, data.bytes, data.length);\n    (*buffer)[data.length] = '\\n';\n    *size = data.length + 1;\n    return true;\n}\n\nbool set_user_default_impl(const char *name, char *buffer, size_t size) {\n    NSString *key = [NSString stringWithUTF8String:name];\n    NSData *data = [NSData dataWithBytesNoCopy:buffer length:size freeWhenDone:NO];\n    NSError *error;\n    id value = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingFragmentsAllowed error:&error];\n    if (error) {\n        return false;\n    }\n    NSString *property = kvoProperties[key];\n    if (property) {\n        if ([UserPreferences.shared validateValue:&value forKey:property error:nil]) {\n            [UserPreferences.shared setValue:value forKey:property];\n        } else {\n            return false;\n        }\n    } else {\n        [NSUserDefaults.standardUserDefaults setValue:value forKey:key];\n    }\n    return true;\n}\n\nbool remove_user_default_impl(const char *name) {\n    NSString *key = [NSString stringWithUTF8String:name];\n    NSString *property = kvoProperties[key];\n    if (property) {\n        [UserPreferences.shared willChangeValueForKey:property];\n    }\n    [NSUserDefaults.standardUserDefaults removeObjectForKey:key];\n    if (property) {\n        [UserPreferences.shared didChangeValueForKey:property];\n    }\n    \n    // This particular property needs special handling to stay up-to-date\n    if ([property isEqualToString:@\"userTheme\"]) {\n        [UserPreferences.shared updateTheme];\n    }\n    return true;\n}\n\n// TODO: Move these to Linux\n#if ISH_LINUX\nchar **(*get_all_defaults_keys)(void);\nchar *(*get_friendly_name)(const char *name);\nchar *(*get_underlying_name)(const char *name);\nbool (*get_user_default)(const char *name, char **buffer, size_t *size);\nbool (*set_user_default)(const char *name, char *buffer, size_t size);\nbool (*remove_user_default)(const char *name);\n#endif\n\n@implementation UserPreferences {\n    NSUserDefaults *_defaults;\n}\n\n+ (instancetype)shared {\n    static UserPreferences *shared = nil;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        shared = [[self alloc] init];\n    });\n    return shared;\n}\n\n- (instancetype)init {\n    self = [super init];\n    self->_hostnameIsOverridden = !![NSUserDefaults.standardUserDefaults stringForKey:kHostnameOverrideKey];\n    if (self) {\n        _defaults = [NSUserDefaults standardUserDefaults];\n        [_defaults registerDefaults:@{\n            kPreferenceFontSizeKey: @(12),\n            kPreferenceCapsLockMappingKey: @(CapsLockMapControl),\n            kPreferenceOptionMappingKey: @(OptionMapNone),\n            kPreferenceBacktickEscapeKey: @(NO),\n            kPreferenceHideExtraKeysWithExternalKeyboardKey: @(NO),\n            kPreferenceOverrideControlSpaceKey: @(NO),\n            kPreferenceDisableDimmingKey: @(NO),\n            kPreferenceLaunchCommandKey: @[@\"/bin/login\", @\"-f\", @\"root\"],\n            kPreferenceBootCommandKey: @[@\"/sbin/init\"],\n            kPreferenceBlinkCursorKey: @(NO),\n            kPreferenceCursorStyleKey: @(CursorStyleBlock),\n            kPreferenceHideStatusBarKey: @(NO),\n            kPreferenceColorSchemeKey: @(ColorSchemeMatchSystem),\n            kPreferenceThemeKey: @\"Default\",\n            kHostnameOverrideKey: UIDevice.currentDevice.name,\n        }];\n        // https://webkit.org/blog/10247/new-webkit-features-in-safari-13-1/\n        if (@available(iOS 13.4, *)) {\n            [_defaults registerDefaults:@{\n                kPreferenceFontFamilyKey: kSystemMonospacedFontName,\n            }];\n        } else {\n            [_defaults registerDefaults:@{\n                kPreferenceFontFamilyKey: @\"Menlo\",\n            }];\n        }\n        get_all_defaults_keys = get_all_defaults_keys_impl;\n        get_friendly_name = get_friendly_name_impl;\n        get_underlying_name = get_underlying_name_impl;\n        get_user_default = get_user_default_impl;\n        set_user_default = set_user_default_impl;\n        remove_user_default = remove_user_default_impl;\n        friendlyPreferenceMapping = @{\n            @\"caps_lock_mapping\": kPreferenceCapsLockMappingKey,\n            @\"option_mapping\": kPreferenceOptionMappingKey,\n            @\"backtick_mapping_escape\": kPreferenceBacktickEscapeKey,\n            @\"hide_extra_keys_with_external_keyboard\": kPreferenceHideExtraKeysWithExternalKeyboardKey,\n            @\"override_control_space\": kPreferenceOverrideControlSpaceKey,\n            @\"font_family\": kPreferenceFontFamilyKey,\n            @\"font_size\": kPreferenceFontSizeKey,\n            @\"disable_dimming\": kPreferenceDisableDimmingKey,\n            @\"launch_command\": kPreferenceLaunchCommandKey,\n            @\"boot_command\": kPreferenceBootCommandKey,\n            @\"cursor_style\": kPreferenceCursorStyleKey,\n            @\"blink_cursor\": kPreferenceBlinkCursorKey,\n            @\"hide_status_bar\": kPreferenceHideStatusBarKey,\n            @\"color_scheme\": kPreferenceColorSchemeKey,\n            @\"theme\": kPreferenceThemeKey,\n            @\"hostname_override\": kHostnameOverrideKey,\n        };\n        NSMutableDictionary <NSString *, NSString *> *reverseMapping = [NSMutableDictionary new];\n        for (NSString *key in friendlyPreferenceMapping) {\n            reverseMapping[friendlyPreferenceMapping[key]] = key;\n        }\n        friendlyPreferenceReverseMapping = reverseMapping;\n        // Helps a bit with compile-time safety and autocompletion\n#define property(x) NSStringFromSelector(@selector(x))\n        kvoProperties = @{\n            kPreferenceCapsLockMappingKey: property(capsLockMapping),\n            kPreferenceOptionMappingKey: property(optionMapping),\n            kPreferenceBacktickEscapeKey: property(backtickMapEscape),\n            kPreferenceHideExtraKeysWithExternalKeyboardKey: property(hideExtraKeysWithExternalKeyboard),\n            kPreferenceOverrideControlSpaceKey: property(overrideControlSpace),\n            kPreferenceFontFamilyKey: property(fontFamily),\n            kPreferenceFontSizeKey: property(fontSize),\n            kPreferenceDisableDimmingKey: property(shouldDisableDimming),\n            kPreferenceLaunchCommandKey: property(launchCommand),\n            kPreferenceBootCommandKey: property(bootCommand),\n            kPreferenceCursorStyleKey: property(cursorStyle),\n            kPreferenceBlinkCursorKey: property(blinkCursor),\n            kPreferenceHideStatusBarKey: property(hideStatusBar),\n            kPreferenceColorSchemeKey: property(colorScheme),\n            // This one is a little bit special, so it needs extra handling.\n            // The backing property for this is intentionally underscored.\n            kPreferenceThemeKey: @\"userTheme\",\n        };\n#undef property\n        \n        [self updateTheme];\n        \n        [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updateTheme:) name:ThemesUpdatedNotification object:nil];\n        [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updateTheme:) name:ThemeUpdatedNotification object:nil];\n    }\n    return self;\n}\n\n// MARK: - Preference properties\n\n// MARK: capsLockMapping\n- (CapsLockMapping)capsLockMapping {\n    return [_defaults integerForKey:kPreferenceCapsLockMappingKey];\n}\n\n- (void)setCapsLockMapping:(CapsLockMapping)capsLockMapping {\n    [_defaults setInteger:capsLockMapping forKey:kPreferenceCapsLockMappingKey];\n}\n\n- (BOOL)validateCapsLockMapping:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSNumber.class]) {\n        return NO;\n    }\n    int _value = [(NSNumber *)(*value) intValue];\n    return _value >= __CapsLockMapFirst && _value < __CapsLockMapLast;\n}\n\n// MARK: optionMapping\n- (OptionMapping)optionMapping {\n    return [_defaults integerForKey:kPreferenceOptionMappingKey];\n}\n\n- (void)setOptionMapping:(OptionMapping)optionMapping {\n    [_defaults setInteger:optionMapping forKey:kPreferenceOptionMappingKey];\n}\n\n- (BOOL)validateOptionMapping:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSNumber.class]) {\n        return NO;\n    }\n    int _value = [(NSNumber *)(*value) intValue];\n    return _value >= __OptionMapFirst && _value < __OptionMapLast;\n}\n\n// MARK: backtickMapEscape\n- (BOOL)backtickMapEscape {\n    return [_defaults boolForKey:kPreferenceBacktickEscapeKey];\n}\n\n- (void)setBacktickMapEscape:(BOOL)backtickMapEscape {\n    [_defaults setBool:backtickMapEscape forKey:kPreferenceBacktickEscapeKey];\n}\n\n- (BOOL)validateBacktickMapEscape:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: hideExtraKeysWithExternalKeyboard\n- (BOOL)hideExtraKeysWithExternalKeyboard {\n    return [_defaults boolForKey:kPreferenceHideExtraKeysWithExternalKeyboardKey];\n}\n\n- (void)setHideExtraKeysWithExternalKeyboard:(BOOL)hideExtraKeysWithExternalKeyboard {\n    [_defaults setBool:hideExtraKeysWithExternalKeyboard forKey:kPreferenceHideExtraKeysWithExternalKeyboardKey];\n}\n\n- (BOOL)validateHideExtraKeysWithExternalKeyboard:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: overrideControlSpace\n- (BOOL)overrideControlSpace {\n    return [_defaults boolForKey:kPreferenceOverrideControlSpaceKey];\n}\n\n- (void)setOverrideControlSpace:(BOOL)overrideControlSpace {\n    [_defaults setBool:overrideControlSpace forKey:kPreferenceOverrideControlSpaceKey];\n}\n\n- (BOOL)validateOverrideControlSpace:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: fontSize\n- (NSNumber *)fontSize {\n    return [_defaults objectForKey:kPreferenceFontSizeKey];\n}\n\n- (void)setFontSize:(NSNumber *)fontSize {\n    [_defaults setObject:fontSize forKey:kPreferenceFontSizeKey];\n}\n\n- (BOOL)validateFontSize:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: fontFamily\n- (NSString *)fontFamily {\n    return [_defaults objectForKey:kPreferenceFontFamilyKey];\n}\n\n- (void)setFontFamily:(NSString *)fontFamily {\n    if (fontFamily) {\n        [_defaults setObject:fontFamily forKey:kPreferenceFontFamilyKey];\n    } else {\n        [_defaults removeObjectForKey:kPreferenceFontFamilyKey];\n    }\n}\n\n- (BOOL)validateFontFamily:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSString.class];\n}\n\n- (NSString *)fontFamilyUserFacingName {\n    return [self.fontFamily isEqualToString:kSystemMonospacedFontName] ? @\"System\" : self.fontFamily;\n}\n\n- (UIFont *)approximateFont {\n    if (@available(iOS 13.4, *)) {\n        if ([self.fontFamily isEqualToString:kSystemMonospacedFontName]) {\n            return [UIFont monospacedSystemFontOfSize:self.fontSize.doubleValue weight:UIFontWeightRegular];\n        }\n    }\n    UIFont *font = [UIFont fontWithName:self.fontFamily size:self.fontSize.doubleValue];\n    return font ? font : [UIFont fontWithName:@\"Menlo\" size:self.fontSize.doubleValue];\n}\n\n// MARK: theme\n- (void)setTheme:(Theme *)theme {\n    _theme = theme;\n    [_defaults setObject:theme.name forKey:kPreferenceThemeKey];\n}\n\n// These are provided because user theme validation is done with strings\n- (NSString *)_userTheme {\n    return self.theme.name;\n}\n\n- (void)_setUserTheme:(NSString *)userTheme {\n    Theme *theme;\n    if ((theme = [Theme themeForName:userTheme includingDefaultThemes:YES])) {\n        self.theme = theme;\n    } else {\n        self.theme = Theme.defaultThemes.lastObject;\n    }\n}\n\n- (BOOL)validateUserTheme:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSString.class];\n}\n\n- (void)updateTheme:(NSNotification *)notification {\n    if (notification.object) {\n        [_defaults setValue:notification.object forKey:kPreferenceThemeKey];\n    }\n    [self updateTheme];\n}\n\n- (void)updateTheme {\n    [self _setUserTheme:[_defaults valueForKey:kPreferenceThemeKey]];\n}\n\n- (Palette *)palette {\n    switch (self.colorScheme) {\n        case ColorSchemeMatchSystem:\n            return self.class.systemThemeIsDark ? self.theme.darkPalette : self.theme.lightPalette;\n        case ColorSchemeAlwaysDark:\n            return self.theme.darkPalette;\n        default:\n            NSAssert(NO, @\"invalid color scheme\");\n        case ColorSchemeAlwaysLight:\n            return self.theme.lightPalette;\n    }\n}\n\n// MARK: shouldDisableDimming\n- (BOOL)shouldDisableDimming {\n    return [_defaults boolForKey:kPreferenceDisableDimmingKey];\n}\n\n- (void)setShouldDisableDimming:(BOOL)dim {\n    [_defaults setBool:dim forKey:kPreferenceDisableDimmingKey];\n}\n\n- (BOOL)validateShouldDisableDimming:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: launchCommand\n- (NSArray<NSString *> *)launchCommand {\n    return [_defaults stringArrayForKey:kPreferenceLaunchCommandKey];\n}\n\n- (void)setLaunchCommand:(NSArray<NSString *> *)launchCommand {\n    [_defaults setObject:launchCommand forKey:kPreferenceLaunchCommandKey];\n}\n\n- (BOOL)validateLaunchCommand:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSArray.class]) {\n        return NO;\n    }\n    for (id element in (NSArray *)(*value)) {\n        if (![element isKindOfClass:NSString.class]) {\n            return NO;\n        }\n    }\n    return YES;\n}\n\n- (BOOL)hasChangedLaunchCommand {\n    NSArray *defaultLaunchCommand = [[[NSUserDefaults alloc] initWithSuiteName:NSRegistrationDomain] stringArrayForKey:kPreferenceLaunchCommandKey];\n    return ![self.launchCommand isEqual:defaultLaunchCommand];\n}\n\n// MARK: bootCommand\n- (NSArray<NSString *> *)bootCommand {\n    return [_defaults stringArrayForKey:kPreferenceBootCommandKey];\n}\n\n- (void)setBootCommand:(NSArray<NSString *> *)bootCommand {\n    [_defaults setObject:bootCommand forKey:kPreferenceBootCommandKey];\n}\n\n- (BOOL)validateBootCommand:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSArray.class]) {\n        return NO;\n    }\n    for (id element in (NSArray *)(*value)) {\n        if (![element isKindOfClass:NSString.class]) {\n            return NO;\n        }\n    }\n    return YES;\n}\n\n// MARK: cursorStyle\n\n- (CursorStyle)cursorStyle {\n    return [_defaults integerForKey:kPreferenceCursorStyleKey];\n}\n\n- (void)setCursorStyle:(CursorStyle)cursorStyle {\n    [_defaults setInteger:cursorStyle forKey:kPreferenceCursorStyleKey];\n}\n\n- (BOOL)validateCursorStyle:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSNumber.class]) {\n        return NO;\n    }\n    int _value = [(NSNumber *)(*value) intValue];\n    return _value >= __CursorStyleFirst && _value < __CursorStyleLast;\n}\n\n- (NSString *)htermCursorShape {\n    switch (self.cursorStyle) {\n        case CursorStyleBlock:\n            return @\"BLOCK\";\n        case CursorStyleBeam:\n            return @\"BEAM\";\n        case CursorStyleUnderline:\n            return @\"UNDERLINE\";\n        default:\n            NSAssert(NO, @\"Invalid cursor style\");\n            return nil;\n    }\n}\n\n// MARK: blinkCursor\n\n- (BOOL)blinkCursor {\n    return [_defaults boolForKey:kPreferenceBlinkCursorKey];\n}\n\n- (void)setBlinkCursor:(BOOL)blinkCursor {\n    [_defaults setBool:blinkCursor forKey:kPreferenceBlinkCursorKey];\n}\n\n- (BOOL)validateBlinkCursor:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: hideStatusBar\n- (BOOL)hideStatusBar {\n    return [_defaults boolForKey:kPreferenceHideStatusBarKey];\n}\n\n- (void)setHideStatusBar:(BOOL)showStatusBar {\n    [_defaults setBool:showStatusBar forKey:kPreferenceHideStatusBarKey];\n}\n\n- (BOOL)validateHideStatusBar:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSNumber.class];\n}\n\n// MARK: colorScheme\n- (ColorScheme)colorScheme {\n    return [_defaults integerForKey:kPreferenceColorSchemeKey];\n}\n\n- (void)setColorScheme:(ColorScheme)colorScheme {\n    [_defaults setInteger:colorScheme forKey:kPreferenceColorSchemeKey];\n}\n\n- (BOOL)validateColorScheme:(id *)value error:(NSError **)error {\n    if (![*value isKindOfClass:NSNumber.class]) {\n        return NO;\n    }\n    int _value = [(NSNumber *)(*value) intValue];\n    return _value >= __ColorSchemeFirst && _value < __ColorSchemeLast;\n}\n\n// MARK: hostnameOverride\n- (NSString *)hostnameOverride {\n    return [_defaults stringForKey:kHostnameOverrideKey];\n}\n\n- (void)setHostnameOverride:(NSString *)hostnameOverride {\n    [_defaults setValue:hostnameOverride forKey:kHostnameOverrideKey];\n}\n\n- (BOOL)validateHostnameOverride:(id *)value error:(NSError **)error {\n    return [*value isKindOfClass:NSString.class];\n}\n\n- (NSString *)_hostnameOverride {\n    return _hostnameIsOverridden ? self.hostnameOverride : nil;\n}\n\n+ (BOOL)systemThemeIsDark {\n    if (@available(iOS 12.0, *)) {\n        switch (UIScreen.mainScreen.traitCollection.userInterfaceStyle) {\n            case UIUserInterfaceStyleLight:\n                return NO;\n            case UIUserInterfaceStyleDark:\n                return YES;\n            default:\n                break;\n        }\n    }\n    return NO;\n}\n\n- (BOOL)requestingDarkAppearance {\n    return (self.class.systemThemeIsDark && !self.theme.appearance.darkOverride) || (!self.class.systemThemeIsDark && self.theme.appearance.lightOverride);\n}\n\n- (UIUserInterfaceStyle)userInterfaceStyle {\n    return self.requestingDarkAppearance ? UIUserInterfaceStyleDark : UIUserInterfaceStyleLight;\n}\n\n- (UIKeyboardAppearance)keyboardAppearance {\n    return self.requestingDarkAppearance ? UIKeyboardAppearanceDark : UIKeyboardAppearanceLight;\n}\n\n- (UIStatusBarStyle)statusBarStyle {\n    return self.requestingDarkAppearance ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;\n}\n\n@end\n"
  },
  {
    "path": "app/ViewController.h",
    "content": "//\n//  ViewController.h\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface TerminalViewController : UIViewController\n\n\n@end\n\n"
  },
  {
    "path": "app/XcodeDebug.xcconfig",
    "content": "#include \"XcodeDefault.xcconfig\"\n\nDEBUG_INFORMATION_FORMAT = dwarf;\nENABLE_TESTABILITY = YES;\nGCC_DYNAMIC_NO_PIC = NO;\nGCC_OPTIMIZATION_LEVEL = 0;\nGCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1\nMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\nONLY_ACTIVE_ARCH = YES;\n"
  },
  {
    "path": "app/XcodeDefault.xcconfig",
    "content": "ALWAYS_SEARCH_USER_PATHS = NO;\nCLANG_CXX_LANGUAGE_STANDARD = gnu++14;\nCLANG_CXX_LIBRARY = libc++;\nCLANG_ENABLE_MODULES = YES;\nCLANG_ENABLE_OBJC_ARC = YES;\nCLANG_ENABLE_OBJC_WEAK = YES;\nCOPY_PHASE_STRIP = NO;\nGCC_C_LANGUAGE_STANDARD = gnu11;\nMTL_FAST_MATH = YES;\n\n// Compiler warnings that are not enabled by default for no apparent reason\nCLANG_ANALYZER_NONNULL = YES;\nCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\nCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\nCLANG_WARN_BOOL_CONVERSION = YES;\nCLANG_WARN_COMMA = YES;\nCLANG_WARN_CONSTANT_CONVERSION = YES;\nCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\nCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\nCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\nCLANG_WARN_EMPTY_BODY = YES;\nCLANG_WARN_ENUM_CONVERSION = YES;\nCLANG_WARN_INFINITE_RECURSION = YES;\nCLANG_WARN_INT_CONVERSION = YES;\nCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\nCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\nCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\nCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\nCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\nCLANG_WARN_STRICT_PROTOTYPES = YES;\nCLANG_WARN_SUSPICIOUS_MOVE = YES;\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\nCLANG_WARN_UNREACHABLE_CODE = YES;\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\nDEAD_CODE_STRIPPING = YES;\nENABLE_STRICT_OBJC_MSGSEND = YES;\nGCC_NO_COMMON_BLOCKS = YES;\nGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\nGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\nGCC_WARN_UNDECLARED_SELECTOR = YES;\nGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\nGCC_WARN_UNUSED_FUNCTION = YES;\nGCC_WARN_UNUSED_VARIABLE = YES;\n"
  },
  {
    "path": "app/XcodeRelease.xcconfig",
    "content": "#include \"XcodeDefault.xcconfig\"\n\nDEBUG_INFORMATION_FORMAT = dwarf-with-dsym;\nENABLE_NS_ASSERTIONS = NO;\nMTL_ENABLE_DEBUG_INFO = NO;\nVALIDATE_PRODUCT = YES;\n"
  },
  {
    "path": "app/gen_apk_repositories.py",
    "content": "import os\n\ndef trim(x, start, end):\n    assert x.startswith(start)\n    assert x.endswith(end)\n    return x[len(start):-len(end)]\n\nAPK_REPOSITORIES = [\n    ('v3.19', 'main'),\n    ('v3.19', 'community'),\n]\nARCH = 'x86' # TODO: support more archs\n\nrepos_file = []\nfor version, repo in APK_REPOSITORIES:\n    with open(f'{os.environ[\"SRCROOT\"]}/deps/aports/{version}/{repo}/{ARCH}/index.txt') as f:\n        index_name = f.read()\n    index_name = trim(index_name, 'APKINDEX-', '.tar.gz\\n')\n    repos_file.append(f'http://apk.ish.app/{index_name}/{repo}')\n\nwith open(os.path.join(os.environ['BUILT_PRODUCTS_DIR'], os.environ['CONTENTS_FOLDER_PATH'], 'repositories.txt'), 'w') as f:\n    for line in repos_file:\n        print(line, file=f)\n"
  },
  {
    "path": "app/hook.c",
    "content": "//\n//  hook.c\n//  iSH\n//\n//  Created by Saagar Jha on 12/29/22.\n//\n\n#include \"hook.h\"\n#include \"mach_excServer.h\"\n#include <CoreFoundation/CoreFoundation.h>\n#include <dlfcn.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/nlist.h>\n#include <mach/mach.h>\n#include <pthread.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/sysctl.h>\n\n#if __arm64__\n\n// No Foundation.h\nextern void NSLog(CFStringRef, ...);\n\nkern_return_t catch_mach_exception_raise(\n    mach_port_t exception_port,\n    mach_port_t thread,\n    mach_port_t task,\n    exception_type_t exception,\n    mach_exception_data_t code,\n    mach_msg_type_number_t codeCnt) {\n    abort();\n}\n\nkern_return_t catch_mach_exception_raise_state_identity(\n    mach_port_t exception_port,\n    mach_port_t thread,\n    mach_port_t task,\n    exception_type_t exception,\n    mach_exception_data_t code,\n    mach_msg_type_number_t codeCnt,\n    int *flavor,\n    thread_state_t old_state,\n    mach_msg_type_number_t old_stateCnt,\n    thread_state_t new_state,\n    mach_msg_type_number_t *new_stateCnt) {\n    abort();\n}\n\nstatic bool initialized;\n\nstruct hook {\n    uintptr_t old;\n    uintptr_t new;\n};\nstatic struct hook hooks[16];\nstatic int active_hooks;\nstatic int breakpoints;\n\nmach_port_t server;\n\nkern_return_t catch_mach_exception_raise_state(\n    mach_port_t exception_port,\n    exception_type_t exception,\n    const mach_exception_data_t code,\n    mach_msg_type_number_t codeCnt,\n    int *flavor,\n    const thread_state_t old_state,\n    mach_msg_type_number_t old_stateCnt,\n    thread_state_t new_state,\n    mach_msg_type_number_t *new_stateCnt) {\n    arm_thread_state64_t *old = (arm_thread_state64_t *)old_state;\n    arm_thread_state64_t *new = (arm_thread_state64_t *)new_state;\n\n    for (int i = 0; i < active_hooks; ++i) {\n        if (hooks[i].old == arm_thread_state64_get_pc(*old)) {\n            *new = *old;\n            *new_stateCnt = old_stateCnt;\n            arm_thread_state64_set_pc_fptr(*new, hooks[i].new);\n            return KERN_SUCCESS;\n        }\n    }\n\n    return KERN_FAILURE;\n}\n\nvoid *exception_handler(void *unused) {\n    mach_msg_server(mach_exc_server, sizeof(union __RequestUnion__catch_mach_exc_subsystem), server, MACH_MSG_OPTION_NONE);\n    abort();\n}\n\nstatic bool initialize_if_needed(void) {\n    if (initialized) {\n        return true;\n    }\n\n#define CHECK(x)          \\\n    do {                  \\\n        if (!(x)) {       \\\n            NSLog(CFSTR(\"hook failed: \" #x)); \\\n            return false; \\\n        }                 \\\n    } while (0)\n\n    size_t size = sizeof(breakpoints);\n    CHECK(!sysctlbyname(\"hw.optional.breakpoint\", &breakpoints, &size, NULL, 0));\n\n    CHECK(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server) == KERN_SUCCESS);\n    CHECK(mach_port_insert_right(mach_task_self(), server, server, MACH_MSG_TYPE_MAKE_SEND) == KERN_SUCCESS);\n\n    // This will break any connected debuggers. Unfortunately the workarounds\n    // for this are not very good, so we're not going to bother with them.\n    CHECK(task_set_exception_ports(mach_task_self(), EXC_MASK_BREAKPOINT, server, EXCEPTION_STATE | MACH_EXCEPTION_CODES, ARM_THREAD_STATE64) == KERN_SUCCESS);\n\n    pthread_t thread;\n    CHECK(!pthread_create(&thread, NULL, exception_handler, NULL));\n\n#undef CHECK\n\n    return initialized = true;\n}\n\n// This is marked as available on iPhone in libproc.h but pulling in the header\n// on iOS is difficult, so we should just forward declare it.\nstatic int (*proc_regionfilename)(int pid, uint64_t address, void *buffer, uint32_t buffersize);\n\n// Pulled from https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h.\n// The format hasn't changed yet but we should check the magic value if it does,\n// since Apple's tools use it to detect if the layout will be updated.\nstruct dyld_cache_header {\n    char magic[16];\n    char padding[56];\n    uint64_t localSymbolsOffset;\n    uint64_t localSymbolsSize;\n};\n\nstruct dyld_cache_local_symbols_info {\n    uint32_t nlistOffset;\n    uint32_t nlistCount;\n    uint32_t stringsOffset;\n};\n\nvoid *find_symbol(void *base, char *symbol) {\n    if (!proc_regionfilename) {\n        proc_regionfilename = dlsym(dlopen(NULL, RTLD_LAZY), \"proc_regionfilename\");\n    }\n\n#define CHECK(x)         \\\n    do {                 \\\n        if (!(x)) {      \\\n            NSLog(CFSTR(\"hook failed: \" #x)); \\\n            return NULL; \\\n        }                \\\n    } while (0)\n\n    CHECK(proc_regionfilename);\n\n    task_dyld_info_data_t dyld_info;\n    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n    CHECK(task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) == KERN_SUCCESS);\n    struct dyld_all_image_infos *all_image_infos = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr;\n\n    // proc_regionfilename doesn't seem to produce results unless the shared\n    // region has been modified in some way. This \"no-op\" forces the mapping\n    // to be backed by a vnode whose path we can query.\n    vm_protect(mach_task_self(), (vm_address_t)base, 1, false, VM_PROT_READ | VM_PROT_COPY);\n    vm_protect(mach_task_self(), (vm_address_t)base, 1, false, VM_PROT_READ | VM_PROT_EXECUTE);\n\n    char path[MAXPATHLEN];\n    int size = proc_regionfilename(getpid(), all_image_infos->sharedCacheBaseAddress, path, sizeof(path));\n    CHECK(size > 0);\n\n    CFURLRef region = CFURLCreateWithBytes(NULL, (UInt8 *)path, size, kCFStringEncodingUTF8, NULL);\n    CFURLRef shared_cache = CFURLCreateCopyDeletingPathExtension(NULL, region);\n    CFURLRef symbols_file = CFURLCreateCopyAppendingPathExtension(NULL, shared_cache, CFSTR(\"symbols\"));\n    CFStringGetCString(CFURLGetString(symbols_file), path, sizeof(path), kCFStringEncodingUTF8);\n    CFRelease(region);\n    CFRelease(shared_cache);\n    CFRelease(symbols_file);\n\n    int fd = open(path, O_RDONLY);\n    CHECK(fd >= 0);\n\n    struct stat buffer;\n    CHECK(!stat(path, &buffer));\n\n    void *file = mmap(NULL, buffer.st_size, PROT_READ, MAP_PRIVATE, fd, 0);\n    close(fd);\n    CHECK(file);\n\n#undef CHECK\n\n    void *address = NULL;\n\n    struct dyld_cache_header *header = (struct dyld_cache_header *)file;\n    if (strcmp(header->magic, \"dyld_v1   arm64\") && strcmp(header->magic, \"dyld_v1  arm64e\")) {\n        NSLog(CFSTR(\"hook failed: unknown shared cache magic %s\"), header->magic); \\\n        goto done;\n    }\n\n    struct dyld_cache_local_symbols_info *symbols = (struct dyld_cache_local_symbols_info *)(file + header->localSymbolsOffset);\n\n    struct nlist_64 *list = (struct nlist_64 *)(file + header->localSymbolsOffset + symbols->nlistOffset);\n    char *strings = (char *)(file + header->localSymbolsOffset + symbols->stringsOffset);\n    for (size_t i = 0; i < symbols->nlistCount; ++i) {\n        if (!strcmp(strings + list[i].n_un.n_strx, symbol)) {\n            uintptr_t _address = list[i].n_value + all_image_infos->sharedCacheSlide;\n            Dl_info info;\n            dladdr((void *)_address, &info);\n            if (info.dli_fbase == base) {\n                address = (void *)_address;\n                break;\n            }\n        }\n    }\n\ndone:\n    munmap(file, buffer.st_size);\n    return address;\n}\n\nbool hook(void *old, void *new) {\n    initialize_if_needed();\n\n#define CHECK(x)          \\\n    do {                  \\\n        if (!(x)) {       \\\n            NSLog(CFSTR(\"hook failed: \" #x)); \\\n            return false; \\\n        }                 \\\n    } while (0)\n\n    CHECK(active_hooks < breakpoints);\n\n    arm_debug_state64_t state = {};\n    state.__bvr[active_hooks] = (uintptr_t)old;\n    // DBGBCR_EL1\n    //  .BT = 0b0000 << 20 (unlinked address match)\n    //  .BAS = 0xF << 5 (A64)\n    //  .PMC = 0b10 << 1 (user)\n    //  .E = 0b1 << 0 (enable)\n    state.__bcr[active_hooks] = 0x1e5;\n\n    CHECK(task_set_state(mach_task_self(), ARM_DEBUG_STATE64, (thread_state_t)&state, ARM_DEBUG_STATE64_COUNT) == KERN_SUCCESS);\n\n#undef CHECK\n\n    bool success = true;\n\n    thread_act_array_t threads;\n    mach_msg_type_number_t thread_count = ARM_DEBUG_STATE64_COUNT;\n    task_threads(mach_task_self(), &threads, &thread_count);\n    for (int i = 0; i < thread_count; ++i) {\n        if (thread_set_state(threads[i], ARM_DEBUG_STATE64, (thread_state_t)&state, ARM_DEBUG_STATE64_COUNT) != KERN_SUCCESS) {\n            NSLog(CFSTR(\"hook failed: could not set thread 0x%x debug state\"), threads[i]);\n            success = false;\n            goto done;\n        }\n    }\n\n    hooks[active_hooks++] = (struct hook){(uintptr_t)old, (uintptr_t) new};\n\ndone:\n    for (int i = 0; i < thread_count; ++i) {\n        mach_port_deallocate(mach_task_self(), threads[i]);\n    }\n    vm_deallocate(mach_task_self(), (vm_address_t)threads, thread_count * sizeof(*threads));\n\n    return success;\n}\n\n#else\n\nvoid *find_symbol(void *base, char *symbol) {\n    return NULL;\n}\n\nbool hook(void *old, void *new) {\n    return false;\n}\n\n#endif\n"
  },
  {
    "path": "app/hook.h",
    "content": "//\n//  hook.h\n//  iSH\n//\n//  Created by Saagar Jha on 12/29/22.\n//\n\n#ifndef hook_h\n#define hook_h\n\n#include <stdbool.h>\n\nvoid *find_symbol(void *base, char *symbol);\nbool hook(void *old, void *new);\n\n#endif /* hook_h */\n"
  },
  {
    "path": "app/iOS.xcconfig",
    "content": "SDKROOT = iphoneos\nTARGETED_DEVICE_FAMILY = 1,2 // iPhone, iPad\nSUPPORTED_PLATFORMS = iphonesimulator iphoneos\n\nLD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks\nHEADER_SEARCH_PATHS = $(SRCROOT)\n"
  },
  {
    "path": "app/iOSFS.h",
    "content": "//\n//  iOSFS.h\n//  iSH\n//\n//  Created by Noah Peeters on 26.10.19.\n//\n\nextern const struct fs_ops iosfs;\nextern const struct fs_ops iosfs_unsafe;\n\nvoid iosfs_init(void);\nvoid iosfs_clear_all_bookmarks(void); // for recovery\n"
  },
  {
    "path": "app/iOSFS.m",
    "content": "//\n//  iOSFS.m\n//  iSH\n//\n//  Created by Noah Peeters on 26.10.19.\n//\n\n#import <Foundation/Foundation.h>\n#import <UIKit/UIKit.h>\n#include <sys/stat.h>\n#include \"SceneDelegate.h\"\n#include \"iOSFS.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/errno.h\"\n#include \"fs/path.h\"\n#include \"fs/real.h\"\n\nconst NSFileCoordinatorWritingOptions NSFileCoordinatorWritingForCreating = NSFileCoordinatorWritingForMerging;\n\n@interface DirectoryPicker : NSObject <UIDocumentPickerDelegate, UIAdaptivePresentationControllerDelegate>\n\n@property NSArray<NSURL *> *urls;\n@property lock_t lock;\n@property cond_t cond;\n\n@end\n\n@implementation DirectoryPicker\n\n- (instancetype)init {\n    if (self = [super init]) {\n        lock_init(&_lock);\n        cond_init(&_cond);\n    }\n    return self;\n}\n\n- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {\n    [self documentPicker:controller didPickDocumentsAtURLs:@[]];\n}\n\n- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController {\n    [self documentPickerWasCancelled:(UIDocumentPickerViewController *)presentationController];\n}\n\n- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {\n    lock(&_lock);\n    self.urls = urls;\n    notify(&_cond);\n    unlock(&_lock);\n}\n\n- (int)askForURL:(NSURL **)url {\n    TerminalViewController *terminalViewController = currentTerminalViewController;\n    if (!terminalViewController)\n        return _ENODEV;\n\n    dispatch_async(dispatch_get_main_queue(), ^(void) {\n        UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[ @\"public.folder\" ] inMode:UIDocumentPickerModeOpen];\n        picker.delegate = self;\n        if (@available(iOS 13, *)) {\n        } else {\n            picker.allowsMultipleSelection = YES;\n        }\n        picker.presentationController.delegate = self;\n        [terminalViewController presentViewController:picker animated:true completion:nil];\n    });\n\n    lock(&_lock);\n    while (_urls == nil) {\n        int err = wait_for(&_cond, &_lock, NULL);\n        if (err < 0) {\n            unlock(&_lock);\n            return err;\n        }\n    }\n    NSArray<NSURL *> *urls = _urls;\n    _urls = nil;\n    unlock(&_lock);\n    \n    if (@available(iOS 13, *)) {\n        assert(urls.count <= 1);\n    }\n    if (urls.count == 0)\n        return _ECANCELED;\n    *url = urls[0];\n    return 0;\n}\n\n- (void)dealloc {\n    cond_destroy(&_cond);\n}\n\n@end\n\nstatic NSURL *url_for_mount(struct mount *mount) {\n    return (__bridge NSURL *) mount->data;\n}\n\nstatic NSString *const kMountBookmarks = @\"iOS Mount Bookmarks\";\n#define BOOKMARK_PATH_ENCODING NSISOLatin1StringEncoding\n// To avoid locking issues, only access from the main thread\nstatic NSMutableDictionary<NSString *, NSData *> *ios_mount_bookmarks;\nstatic bool mount_from_bookmarks = false; // This is a hack because I am bad at parameter passing\nstatic void sync_bookmarks(void) {\n    [NSUserDefaults.standardUserDefaults setObject:ios_mount_bookmarks forKey:kMountBookmarks];\n}\nvoid iosfs_init(void) {\n    ios_mount_bookmarks = [NSUserDefaults.standardUserDefaults dictionaryForKey:kMountBookmarks].mutableCopy;\n    if (ios_mount_bookmarks == nil)\n        ios_mount_bookmarks = [NSMutableDictionary new];\n\n    mount_from_bookmarks = true;\n    for (NSString *path in ios_mount_bookmarks.allKeys) {\n        const char *point = [path cStringUsingEncoding:BOOKMARK_PATH_ENCODING];\n        int err = do_mount(&iosfs, point, point, \"\", 0);\n        if (err < 0) {\n            NSLog(@\"restoring bookmark %@ failed with error %d\", path, err);\n            [ios_mount_bookmarks removeObjectForKey:path];\n        }\n    }\n    mount_from_bookmarks = false;\n    sync_bookmarks();\n}\n\nvoid iosfs_clear_all_bookmarks(void) {\n    [ios_mount_bookmarks removeAllObjects];\n    sync_bookmarks();\n}\n\nstatic int iosfs_mount(struct mount *mount) {\n    NSURL *url = nil;\n    if (mount_from_bookmarks) {\n        NSString *bookmarkName = [NSString stringWithCString:mount->source encoding:BOOKMARK_PATH_ENCODING];\n        url = [NSURL URLByResolvingBookmarkData:ios_mount_bookmarks[bookmarkName]\n                                        options:0\n                                  relativeToURL:nil\n                            bookmarkDataIsStale:NULL\n                                          error:nil];\n        if (url != nil && ![url startAccessingSecurityScopedResource]) {\n            return _EPERM;\n        }\n    }\n\n    if (url == nil) {\n        DirectoryPicker *picker = [DirectoryPicker new];\n        int err = [picker askForURL:&url];\n        if (err)\n            return err;\n        if (![url startAccessingSecurityScopedResource])\n            return _EPERM;\n    }\n\n    // Overwrite url & base path\n    mount->data = (void *) CFBridgingRetain(url);\n    free((void *) mount->source);\n    mount->source = strdup([[url path] UTF8String]);\n\n    if (mount_param_flag(mount->info, \"unsafe\")) {\n        mount->fs = &iosfs_unsafe;\n    }\n\n    if (!mount_from_bookmarks) {\n        NSData *bookmark = [url bookmarkDataWithOptions:0 includingResourceValuesForKeys:nil relativeToURL:nil error:nil];\n        NSString *path = [NSString stringWithCString:mount->point encoding:BOOKMARK_PATH_ENCODING];\n        if (bookmark != nil) {\n            dispatch_async(dispatch_get_main_queue(), ^{\n                ios_mount_bookmarks[path] = bookmark;\n                sync_bookmarks();\n            });\n        }\n    }\n\n    return realfs.mount(mount);\n}\n\nstatic int iosfs_umount(struct mount *mount) {\n    NSString *path = [NSString stringWithCString:mount->point encoding:BOOKMARK_PATH_ENCODING];\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [ios_mount_bookmarks removeObjectForKey:path];\n        sync_bookmarks();\n    });\n    NSURL *url = url_for_mount(mount);\n    [url stopAccessingSecurityScopedResource];\n    CFBridgingRelease(mount->data);\n    return 0;\n}\n\nstatic NSURL *url_for_path_in_mount(struct mount *mount, const char *path) {\n    if (path[0] == '/')\n        path++;\n    return [url_for_mount(mount) URLByAppendingPathComponent:[NSString stringWithUTF8String:path] isDirectory:NO];\n}\n\nconst char *path_for_url_in_mount(struct mount *mount, NSURL *url, const char *fallback) {\n    NSString *mount_path = url_for_mount(mount).path;\n    NSString *url_path = url.path;\n\n    // The `/private` prefix is a special case as described in the documentation of `URLByStandardizingPath`.\n    if ([mount_path hasPrefix:@\"/private/\"]) mount_path = [mount_path substringFromIndex:8];\n    if ([url_path   hasPrefix:@\"/private/\"]) url_path   = [url_path   substringFromIndex:8];\n\n    if (![url_path hasPrefix:mount_path])\n        return fallback;\n\n    return [url_path substringFromIndex:[mount_path length]].UTF8String;\n}\n\nstatic int iosfs_stat(struct mount *mount, const char *path, struct statbuf *fake_stat);\nextern const struct fd_ops iosfs_fdops;\n\nstatic int posixErrorFromNSError(NSError *error) {\n    if (error == nil)\n        return 0;\n    while (error != nil) {\n        if ([error.domain isEqualToString:NSPOSIXErrorDomain]) {\n            return err_map((int) error.code);\n        }\n        error = error.userInfo[NSUnderlyingErrorKey];\n    }\n    return _EINVAL;\n}\n\nstatic int combine_error(NSError *coordinatorError, int err) {\n    int posix_error = posixErrorFromNSError(coordinatorError);\n    return posix_error ? posix_error : err;\n}\n\nstatic struct fd *iosfs_open(struct mount *mount, const char *path, int flags, int mode) {\n    NSURL *url = url_for_path_in_mount(mount, path);\n\n    // FIXME: this does a redundant file coordination operation\n    struct statbuf stats;\n    int err = iosfs_stat(mount, path, &stats);\n\n    if (err == 0 && S_ISREG(stats.mode)) {\n        NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n\n        __block NSError *error = nil;\n        __block struct fd *fd;\n        __block dispatch_semaphore_t file_opened = dispatch_semaphore_create(0);\n\n        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){\n            void (^operation)(NSURL *url) = ^(NSURL *url) {\n                fd = realfs_open(mount, path_for_url_in_mount(mount, url, path), flags, mode);\n\n                if (IS_ERR(fd)) {\n                    dispatch_semaphore_signal(file_opened);\n                } else {\n                    fd->ops = &iosfs_fdops;\n                    dispatch_semaphore_t file_closed = dispatch_semaphore_create(0);\n                    fd->data = (__bridge void *) file_closed;\n                    dispatch_semaphore_signal(file_opened);\n                    dispatch_semaphore_wait(file_closed, DISPATCH_TIME_FOREVER);\n                }\n            };\n\n            int options;\n            if (!(flags & O_WRONLY_) && !(flags & O_RDWR_)) {\n                options = NSFileCoordinatorReadingWithoutChanges;\n            } else if (flags & O_CREAT_) {\n                options = NSFileCoordinatorWritingForCreating;\n            } else {\n                options = NSFileCoordinatorWritingForMerging;\n            }\n            [coordinator coordinateReadingItemAtURL:url options:options error:&error byAccessor:operation];\n        });\n        \n        dispatch_semaphore_wait(file_opened, DISPATCH_TIME_FOREVER);\n\n        int posix_error = posixErrorFromNSError(error);\n        return posix_error ? ERR_PTR(posix_error) : fd;\n    }\n        \n    struct fd *fd = realfs_open(mount, path, flags, mode);\n    if (!IS_ERR(fd))\n        fd->ops = &iosfs_fdops;\n    return fd;\n}\n\nint iosfs_close(struct fd *fd) {\n    int err = realfs.close(fd);\n    if (fd->data != NULL) {\n        dispatch_semaphore_t file_closed = (__bridge dispatch_semaphore_t) fd->data;\n        dispatch_semaphore_signal(file_closed);\n    }\n    return err;\n}\n\nstatic int iosfs_rename(struct mount *mount, const char *src, const char *dst) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *src_url = url_for_path_in_mount(mount, src);\n    NSURL *dst_url = url_for_path_in_mount(mount, dst);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:src_url options:NSFileCoordinatorWritingForMoving error:&error byAccessor:^(NSURL *url) {\n        [coordinator itemAtURL:url willMoveToURL:dst_url];\n        err = realfs.rename(mount, path_for_url_in_mount(mount, url, src), dst);\n        [coordinator itemAtURL:url didMoveToURL:dst_url];\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_symlink(struct mount *mount, const char *target, const char *link) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *dst_url = url_for_path_in_mount(mount, link);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:dst_url options:NSFileCoordinatorWritingForCreating error:&error byAccessor:^(NSURL *url) {\n        err = realfs.symlink(mount, path_for_url_in_mount(mount, url, target), link);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_mknod(struct mount *mount, const char *path, mode_t_ mode, dev_t_ dev) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingForCreating error:&error byAccessor:^(NSURL *url) {\n        err = realfs.mknod(mount, path_for_url_in_mount(mount, url, path), mode, dev);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_setattr(struct mount *mount, const char *path, struct attr attr) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingContentIndependentMetadataOnly error:&error byAccessor:^(NSURL *url) {\n        err = realfs.setattr(mount, path_for_url_in_mount(mount, url, path), attr);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_fsetattr(struct fd *fd, struct attr attr) {\n    return realfs.fsetattr(fd, attr);\n}\n\nstatic ssize_t iosfs_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block ssize_t size;\n\n    [coordinator coordinateReadingItemAtURL:in_url options:NSFileCoordinatorReadingWithoutChanges error:&error byAccessor:^(NSURL *url) {\n        size = realfs.readlink(mount, path_for_url_in_mount(mount, url, path), buf, bufsize);\n    }];\n\n    int posix_error = posixErrorFromNSError(error);\n    return posix_error ? posix_error : size;\n}\n\nstatic int iosfs_getpath(struct fd *fd, char *buf) {\n    return realfs.getpath(fd, buf);\n}\n\nstatic int iosfs_link(struct mount *mount, const char *src, const char *dst) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *dst_url = url_for_path_in_mount(mount, dst);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:dst_url options:NSFileCoordinatorWritingForCreating error:&error byAccessor:^(NSURL *url) {\n        err = realfs.link(mount, src, path_for_url_in_mount(mount, url, dst));\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_unlink(struct mount *mount, const char *path) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingForDeleting error:&error byAccessor:^(NSURL *url) {\n        err = realfs.unlink(mount, path_for_url_in_mount(mount, url, path));\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_rmdir(struct mount *mount, const char *path) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingForDeleting error:&error byAccessor:^(NSURL *url) {\n        err = realfs.rmdir(mount, path_for_url_in_mount(mount, url, path));\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_stat(struct mount *mount, const char *path, struct statbuf *fake_stat) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateReadingItemAtURL:in_url options:NSFileCoordinatorReadingWithoutChanges error:&error byAccessor:^(NSURL *url) {\n        err = realfs.stat(mount, path_for_url_in_mount(mount, url, path), fake_stat);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_fstat(struct fd *fd, struct statbuf *fake_stat) {\n    int err = realfs.fstat(fd, fake_stat);\n    return err;\n}\n\nstatic int iosfs_utime(struct mount *mount, const char *path, struct timespec atime, struct timespec mtime) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingContentIndependentMetadataOnly error:&error byAccessor:^(NSURL *url) {\n        err = realfs.utime(mount, path_for_url_in_mount(mount, url, path), atime, mtime);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_mkdir(struct mount *mount, const char *path, mode_t_ mode) {\n    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];\n    NSURL *in_url = url_for_path_in_mount(mount, path);\n\n    NSError *error;\n    __block int err;\n\n    [coordinator coordinateWritingItemAtURL:in_url options:NSFileCoordinatorWritingForCreating error:&error byAccessor:^(NSURL *url) {\n        err = realfs.mkdir(mount, path_for_url_in_mount(mount, url, path), mode);\n    }];\n\n    return combine_error(error, err);\n}\n\nstatic int iosfs_flock(struct fd *fd, int operation) {\n    return realfs.flock(fd, operation);\n}\n\nconst struct fs_ops iosfs = {\n    .name = \"ios\", .magic = 0x694f5320,\n    .mount = iosfs_mount,\n    .umount = iosfs_umount,\n    .statfs = realfs_statfs,\n\n    .open = iosfs_open,\n    .readlink = iosfs_readlink,\n    .link = iosfs_link,\n    .unlink = iosfs_unlink,\n    .rmdir = iosfs_rmdir,\n    .rename = iosfs_rename,\n    .symlink = iosfs_symlink,\n    .mknod = iosfs_mknod,\n\n    .close = iosfs_close,\n    .stat = iosfs_stat,\n    .fstat = iosfs_fstat,\n    .setattr = iosfs_setattr,\n    .fsetattr = iosfs_fsetattr,\n    .utime = iosfs_utime,\n    .getpath = iosfs_getpath,\n    .flock = iosfs_flock,\n\n    .mkdir = iosfs_mkdir,\n};\n\nconst struct fs_ops iosfs_unsafe = {\n    .name = \"ios-unsafe\", .magic = 0x694f5321,\n    .mount = iosfs_mount,\n    .umount = iosfs_umount,\n    .statfs = realfs_statfs,\n\n    .open = realfs_open,\n    .readlink = realfs_readlink,\n    .link = realfs_link,\n    .unlink = realfs_unlink,\n    .rmdir = realfs_rmdir,\n    .rename = realfs_rename,\n    .symlink = realfs_symlink,\n    .mknod = realfs_mknod,\n\n    .close = realfs_close,\n    .stat = realfs_stat,\n    .fstat = realfs_fstat,\n    .setattr = realfs_setattr,\n    .fsetattr = realfs_fsetattr,\n    .utime = realfs_utime,\n    .getpath = realfs_getpath,\n    .flock = realfs_flock,\n\n    .mkdir = realfs_mkdir,\n};\n\nconst struct fd_ops iosfs_fdops = {\n    .read = realfs_read,\n    .write = realfs_write,\n    .readdir = realfs_readdir,\n    .telldir = realfs_telldir,\n    .seekdir = realfs_seekdir,\n    .lseek = realfs_lseek,\n    .mmap = realfs_mmap,\n    .poll = realfs_poll,\n    .fsync = realfs_fsync,\n    .close = iosfs_close,\n    .getflags = realfs_getflags,\n    .setflags = realfs_setflags,\n};\n"
  },
  {
    "path": "app/iSH.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.developer.user-fonts</key>\n\t<array>\n\t\t<string>app-usage</string>\n\t</array>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>$(PRODUCT_APP_GROUP_IDENTIFIER)</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "app/iSH.xcconfig",
    "content": "// Change this to change all the bundle IDs and app groups\nROOT_BUNDLE_IDENTIFIER = app.ish.iSH\n// It's easiest to specify your development team ID in the project build settings, but you can alternatively put it here to reduce merge conflicts\nDEVELOPMENT_TEAM =\n\n// Choose logging channels to enable. Separate by spaces. Try \"verbose strace\".\nISH_LOG =\nISH_LOGGER = $(ISH_LOGGER_$(PLATFORM_NAME))\nISH_LOGGER_iphoneos = nslog\nISH_LOGGER_iphonesimulator = nslog\nISH_LOGGER_macosx = dprintf\n\nROOTFS_URL = github.com/ish-app/roots/releases/download/g00712ff0a54b2839c5aa1a8ed758003ca65357dc/appstore-apk.tar.gz\n"
  },
  {
    "path": "app/main.m",
    "content": "//\n//  main.m\n//  iSH\n//\n//  Created by Theodore Dubois on 10/17/17.\n//\n\n#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n#import \"ExceptionExfiltrator.h\"\n\nint main(int argc, char * argv[]) {\n    NSSetUncaughtExceptionHandler(iSHExceptionHandler);\n    @autoreleasepool {\n        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n    }\n}\n"
  },
  {
    "path": "app/terminal/term.css",
    "content": "body {\n    margin: 0;\n    background: transparent;\n    overflow: hidden;\n}\n\n#terminal {\n    position: absolute;\n    top: 0; bottom: 0;\n    left: 0; right: 0;\n}\n"
  },
  {
    "path": "app/terminal/term.html",
    "content": "<!doctype html>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"height=device-height, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0\">\n<link rel=\"stylesheet\" href=\"term.css\">\n<div id=\"terminal\"></div>\n<script src=\"hterm_all.js\"></script>\n<script src=\"term.js\"></script>\n"
  },
  {
    "path": "app/terminal/term.js",
    "content": "hterm.defaultStorage = new lib.Storage.Memory();\nwindow.onload = async function() {\n    await lib.init();\n    window.term = new hterm.Terminal();\n\n    // make everything invisible so as to not be embarrassing\n    term.getPrefs().set('background-color', 'transparent');\n    term.getPrefs().set('foreground-color', 'transparent');\n    term.getPrefs().set('cursor-color', 'transparent');\n\n    term.getPrefs().set('terminal-encoding', 'iso-2022');\n    term.getPrefs().set('enable-resize-status', false);\n    term.getPrefs().set('copy-on-select', false);\n    term.getPrefs().set('enable-clipboard-notice', false);\n    term.getPrefs().set('user-css-text', termCss);\n    term.getPrefs().set('screen-padding-size', 4);\n    // Creating and preloading the <audio> element for this sometimes hangs WebKit on iOS 16 for some reason. Can be most easily reproduced by resetting a simulator and starting the app. System logs show Fig hanging while trying to do work.\n    term.getPrefs().set('audible-bell-sound', '');\n\n    term.onTerminalReady = onTerminalReady;\n    term.decorate(document.getElementById('terminal'));\n};\n\nvar termCss = `\nx-screen {\n    background: transparent !important;\n    overflow: hidden !important;\n    -webkit-tap-highlight-color: transparent;\n}\nx-row {\n  text-rendering: optimizeLegibility;\n  font-variant-ligatures: normal;\n}\n.uri-node {\n  text-decoration: underline;\n}\n`;\n\nfunction onTerminalReady() {\n\n// Shorthand for JS -> native IPC\nconst native = new Proxy({}, {\n    get(obj, prop) {\n        return (...args) => {\n            if (args.length == 0)\n                args = null;\n            else if (args.length == 1)\n                args = args[0];\n            webkit.messageHandlers[prop].postMessage(args);\n        };\n    },\n});\n\n// Functions for native -> JS\nwindow.exports = {};\n\nterm.io.push();\nterm.reset();\n\nlet oldProps = {};\nfunction syncProp(name, value) {\n    if (oldProps[name] !== value)\n        native.propUpdate(name, value);\n}\nlet decoder = new TextDecoder();\nexports.write = (data) => {\n    term.io.writeUTF16(decoder.decode(lib.codec.stringToCodeUnitArray(data)));\n    syncProp('applicationCursor', term.keyboard.applicationCursor);\n};\nterm.io.sendString = term.io.onVTKeyStroke = (data) => {\n    native.sendInput(data);\n};\n\n// hterm size updates native size\nterm.io.onTerminalResize = () => native.resize();\nexports.getSize = () => [term.screenSize.width, term.screenSize.height];\n\n// selection, copying\nterm.scrollPort_.screen_.contentEditable = false;\nterm.blur();\nterm.focus();\nexports.copy = () => term.copySelectionToClipboard();\n\n// focus\n// This listener blocks blur events that come in because the webview has lost first responder\nterm.scrollPort_.screen_.addEventListener('blur', (e) => {\n    if (e.target.ownerDocument.activeElement == e.target) {\n        e.stopPropagation();\n    }\n}, {capture: true});\nterm.scrollPort_.screen_.addEventListener('mousedown', (e) => {\n    // Taps while there is a selection should be left to the selection view\n    if ((document.getSelection().rangeCount != 0) &&\n        (!document.getSelection().isCollapsed)) return;\n    native.focus();\n});\nexports.setFocused = (focus) => {\n    if (focus)\n        term.focus();\n    else\n        term.blur();\n};\nterm.scrollPort_.screen_.addEventListener('focus', (e) => native.syncFocus());\n\n// scrolling\n// Disable hterm builtin touch scrolling\nterm.scrollPort_.onTouch = (e) => {\n    // Convince hterm that we called preventDefault() and that it shouldn't do more handling, but don't actually call it because that would break text selection\n    Object.defineProperty(e, 'defaultPrevented', {value: true});\n};\n// Scroll to bottom wrapper\nexports.scrollToBottom = () => term.scrollEnd();\n// Set scroll position\nexports.newScrollTop = (y) => {\n    // two lines instead of one because the value you read out of scrollTop can be different from the value you write into it\n    term.scrollPort_.screen_.scrollTop = y;\n    lastScrollTop = term.scrollPort_.screen_.scrollTop;\n};\n\n// Send scroll height and position to native code\nlet lastScrollHeight, lastScrollTop;\nfunction syncScroll() {\n    const scrollHeight = parseFloat(term.scrollPort_.scrollArea_.style.height);\n    if (scrollHeight != lastScrollHeight)\n        native.newScrollHeight(scrollHeight);\n    lastScrollHeight = scrollHeight;\n\n    const scrollTop = term.scrollPort_.screen_.scrollTop;\n    if (scrollTop != lastScrollTop)\n        native.newScrollTop(scrollTop);\n    lastScrollTop = scrollTop;\n}\n\nconst realSyncScrollHeight = hterm.ScrollPort.prototype.syncScrollHeight;\nhterm.ScrollPort.prototype.syncScrollHeight = function() {\n    realSyncScrollHeight.call(this);\n    syncScroll();\n};\nterm.scrollPort_.screen_.addEventListener('scroll', syncScroll);\n\nexports.updateStyle = ({foregroundColor, backgroundColor, fontFamily, fontSize, colorPaletteOverrides, blinkCursor, cursorShape}) => {\n    term.getPrefs().set('background-color', backgroundColor);\n    term.getPrefs().set('foreground-color', foregroundColor);\n    term.getPrefs().set('cursor-color', foregroundColor);\n    term.getPrefs().set('font-family', fontFamily);\n    term.getPrefs().set('font-size', fontSize);\n    term.getPrefs().set('color-palette-overrides', colorPaletteOverrides);\n    term.getPrefs().set('cursor-blink', blinkCursor);\n    term.getPrefs().set('cursor-shape', cursorShape);\n};\n\nexports.getCharacterSize = () => {\n    return [term.scrollPort_.characterSize.width, term.scrollPort_.characterSize.height];\n};\n\nexports.clearScrollback = () => term.clearScrollback();\nexports.setUserGesture = () => term.accessibilityReader_.hasUserGesture = true;\n\nhterm.openUrl = (url) => native.openLink(url);\n\nnative.load();\nnative.syncFocus();\n\n}\n"
  },
  {
    "path": "app/xcode-meson.sh",
    "content": "#!/bin/bash\n\n# Try to figure out the user's PATH to pick up their installed utilities.\nexport PATH=\"$PATH:$(sudo -u \"$USER\" -i printenv PATH)\"\n\nmkdir -p \"$MESON_BUILD_DIR\"\ncd \"$MESON_BUILD_DIR\"\n\nconfig=$(meson introspect --buildoptions)\nif [[ $? -ne 0 ]]; then\n    export CC_FOR_BUILD=\"env -u SDKROOT -u IPHONEOS_DEPLOYMENT_TARGET xcrun clang\"\n    export CC=\"$CC_FOR_BUILD\" # compatibility with meson < 0.54.0\n    crossfile=cross.txt\n    for arch in $ARCHS; do\n        arch_args=\"'-arch', '$arch', $arch_args\"\n    done\n    arch_args=\"${arch_args%%, }\"\n    meson_arch=${ARCHS%% *}\n    case \"$meson_arch\" in\n        arm64) meson_arch=aarch64 ;;\n    esac\n    cat | tee $crossfile <<-EOF\n    [binaries]\n    c = 'clang'\n    ar = 'ar'\n\n    [host_machine]\n    system = 'darwin'\n    cpu_family = '$meson_arch'\n    cpu = '$meson_arch'\n    endian = 'little'\n\n    [built-in options]\n    c_args = [$arch_args]\n    \n    [properties]\n    needs_exe_wrapper = true\nEOF\n    (set -x; meson $SRCROOT --cross-file $crossfile) || exit $?\n    config=$(meson introspect --buildoptions)\nfi\n\nbuildtype=debug\nb_ndebug=false\nif [[ $CONFIGURATION == Release ]]; then\n    buildtype=debugoptimized\nfi\nb_sanitize=none\nif [[ -n \"$ENABLE_ADDRESS_SANITIZER\" ]]; then\n    b_sanitize=address\nfi\nlog=$ISH_LOG\nlog_handler=$ISH_LOGGER\nkernel=ish\nif [[ -n \"$ISH_KERNEL\" ]]; then\n    kernel=$ISH_KERNEL\nfi\nkconfig=\"\"\nfor var in buildtype log b_ndebug b_sanitize log_handler kernel kconfig; do\n    old_value=$(python3 -c \"import sys, json; v = next(x['value'] for x in json.load(sys.stdin) if x['name'] == '$var'); print(str(v).lower() if isinstance(v, bool) else ','.join(v) if isinstance(v, list) else v)\" <<< $config)\n    new_value=${!var}\n    if [[ $old_value != $new_value ]]; then\n        set -x; meson configure \"-D$var=$new_value\"\n    fi\ndone\n"
  },
  {
    "path": "app/xcode-ninja.sh",
    "content": "#!/bin/sh\n\n# Try to figure out the user's PATH to pick up their installed utilities.\nexport PATH=\"$PATH:$(sudo -u \"$USER\" -i printenv PATH)\"\n\nninja \"$@\"\n"
  },
  {
    "path": "asbestos/asbestos.c",
    "content": "#define DEFAULT_CHANNEL instr\n#include \"debug.h\"\n#include \"asbestos/asbestos.h\"\n#include \"asbestos/gen.h\"\n#include \"asbestos/frame.h\"\n#include \"emu/cpu.h\"\n#include \"emu/interrupt.h\"\n#include \"util/list.h\"\n\nextern int current_pid(void);\n\nstatic void fiber_block_disconnect(struct asbestos *asbestos, struct fiber_block *block);\nstatic void fiber_block_free(struct asbestos *asbestos, struct fiber_block *block);\nstatic void fiber_free_jetsam(struct asbestos *asbestos);\nstatic void fiber_resize_hash(struct asbestos *asbestos, size_t new_size);\n\nstruct asbestos *asbestos_new(struct mmu *mmu) {\n    struct asbestos *asbestos = calloc(1, sizeof(struct asbestos));\n    asbestos->mmu = mmu;\n    fiber_resize_hash(asbestos, FIBER_INITIAL_HASH_SIZE);\n    asbestos->page_hash = calloc(FIBER_PAGE_HASH_SIZE, sizeof(*asbestos->page_hash));\n    list_init(&asbestos->jetsam);\n    lock_init(&asbestos->lock);\n    wrlock_init(&asbestos->jetsam_lock);\n    return asbestos;\n}\n\nvoid asbestos_free(struct asbestos *asbestos) {\n    for (size_t i = 0; i < asbestos->hash_size; i++) {\n        struct fiber_block *block, *tmp;\n        if (list_null(&asbestos->hash[i]))\n            continue;\n        list_for_each_entry_safe(&asbestos->hash[i], block, tmp, chain) {\n            fiber_block_free(asbestos, block);\n        }\n    }\n    fiber_free_jetsam(asbestos);\n    free(asbestos->page_hash);\n    free(asbestos->hash);\n    free(asbestos);\n}\n\nstatic inline struct list *blocks_list(struct asbestos *asbestos, page_t page, int i) {\n    // TODO is this a good hash function?\n    return &asbestos->page_hash[page % FIBER_PAGE_HASH_SIZE].blocks[i];\n}\n\nvoid asbestos_invalidate_range(struct asbestos *absestos, page_t start, page_t end) {\n    lock(&absestos->lock);\n    struct fiber_block *block, *tmp;\n    for (page_t page = start; page < end; page++) {\n        for (int i = 0; i <= 1; i++) {\n            struct list *blocks = blocks_list(absestos, page, i);\n            if (list_null(blocks))\n                continue;\n            list_for_each_entry_safe(blocks, block, tmp, page[i]) {\n                fiber_block_disconnect(absestos, block);\n                block->is_jetsam = true;\n                list_add(&absestos->jetsam, &block->jetsam);\n            }\n        }\n    }\n    unlock(&absestos->lock);\n}\n\nvoid asbestos_invalidate_page(struct asbestos *asbestos, page_t page) {\n    asbestos_invalidate_range(asbestos, page, page + 1);\n}\nvoid asbestos_invalidate_all(struct asbestos *asbestos) {\n    asbestos_invalidate_range(asbestos, 0, MEM_PAGES);\n}\n\nstatic void fiber_resize_hash(struct asbestos *asbestos, size_t new_size) {\n    TRACE_(verbose, \"%d resizing hash to %lu, using %lu bytes for gadgets\\n\", current_pid(), new_size, asbestos->mem_used);\n    struct list *new_hash = calloc(new_size, sizeof(struct list));\n    for (size_t i = 0; i < asbestos->hash_size; i++) {\n        if (list_null(&asbestos->hash[i]))\n            continue;\n        struct fiber_block *block, *tmp;\n        list_for_each_entry_safe(&asbestos->hash[i], block, tmp, chain) {\n            list_remove(&block->chain);\n            list_init_add(&new_hash[block->addr % new_size], &block->chain);\n        }\n    }\n    free(asbestos->hash);\n    asbestos->hash = new_hash;\n    asbestos->hash_size = new_size;\n}\n\nstatic void fiber_insert(struct asbestos *asbestos, struct fiber_block *block) {\n    asbestos->mem_used += block->used;\n    asbestos->num_blocks++;\n    // target an average hash chain length of 1-2\n    if (asbestos->num_blocks >= asbestos->hash_size * 2)\n        fiber_resize_hash(asbestos, asbestos->hash_size * 2);\n\n    list_init_add(&asbestos->hash[block->addr % asbestos->hash_size], &block->chain);\n    list_init_add(blocks_list(asbestos, PAGE(block->addr), 0), &block->page[0]);\n    if (PAGE(block->addr) != PAGE(block->end_addr))\n        list_init_add(blocks_list(asbestos, PAGE(block->end_addr), 1), &block->page[1]);\n}\n\nstatic struct fiber_block *fiber_lookup(struct asbestos *asbestos, addr_t addr) {\n    struct list *bucket = &asbestos->hash[addr % asbestos->hash_size];\n    if (list_null(bucket))\n        return NULL;\n    struct fiber_block *block;\n    list_for_each_entry(bucket, block, chain) {\n        if (block->addr == addr)\n            return block;\n    }\n    return NULL;\n}\n\nstatic struct fiber_block *fiber_block_compile(addr_t ip, struct tlb *tlb) {\n    struct gen_state state;\n    TRACE(\"%d %08x --- compiling:\\n\", current_pid(), ip);\n    gen_start(ip, &state);\n    while (true) {\n        if (!gen_step(&state, tlb))\n            break;\n        // no block should span more than 2 pages\n        // guarantee this by limiting total block size to 1 page\n        // guarantee that by stopping as soon as there's less space left than\n        // the maximum length of an x86 instruction\n        // TODO refuse to decode instructions longer than 15 bytes\n        if (state.ip - ip >= PAGE_SIZE - 15) {\n            gen_exit(&state);\n            break;\n        }\n    }\n    gen_end(&state);\n    assert(state.ip - ip <= PAGE_SIZE);\n    state.block->used = state.capacity;\n    return state.block;\n}\n\n// Remove all pointers to the block. It can't be freed yet because another\n// thread may be executing it.\nstatic void fiber_block_disconnect(struct asbestos *asbestos, struct fiber_block *block) {\n    if (asbestos != NULL) {\n        asbestos->mem_used -= block->used;\n        asbestos->num_blocks--;\n    }\n    list_remove(&block->chain);\n    for (int i = 0; i <= 1; i++) {\n        list_remove(&block->page[i]);\n        list_remove_safe(&block->jumps_from_links[i]);\n\n        struct fiber_block *prev_block, *tmp;\n        list_for_each_entry_safe(&block->jumps_from[i], prev_block, tmp, jumps_from_links[i]) {\n            if (prev_block->jump_ip[i] != NULL)\n                *prev_block->jump_ip[i] = prev_block->old_jump_ip[i];\n            list_remove(&prev_block->jumps_from_links[i]);\n        }\n    }\n}\n\nstatic void fiber_block_free(struct asbestos *asbestos, struct fiber_block *block) {\n    fiber_block_disconnect(asbestos, block);\n    free(block);\n}\n\nstatic void fiber_free_jetsam(struct asbestos *asbestos) {\n    struct fiber_block *block, *tmp;\n    list_for_each_entry_safe(&asbestos->jetsam, block, tmp, jetsam) {\n        list_remove(&block->jetsam);\n        free(block);\n    }\n}\n\nint fiber_enter(struct fiber_block *block, struct fiber_frame *frame, struct tlb *tlb);\n\nstatic inline size_t fiber_cache_hash(addr_t ip) {\n    return (ip ^ (ip >> 12)) % FIBER_CACHE_SIZE;\n}\n\nstatic int cpu_step_to_interrupt(struct cpu_state *cpu, struct tlb *tlb) {\n    struct asbestos *asbestos = cpu->mmu->asbestos;\n    read_wrlock(&asbestos->jetsam_lock);\n\n    struct fiber_block **cache = calloc(FIBER_CACHE_SIZE, sizeof(*cache));\n    struct fiber_frame *frame = malloc(sizeof(struct fiber_frame));\n    memset(frame, 0, sizeof(*frame));\n    frame->cpu = *cpu;\n    assert(asbestos->mmu == cpu->mmu);\n\n    int interrupt = INT_NONE;\n    while (interrupt == INT_NONE) {\n        addr_t ip = frame->cpu.eip;\n        size_t cache_index = fiber_cache_hash(ip);\n        struct fiber_block *block = cache[cache_index];\n        if (block == NULL || block->addr != ip) {\n            lock(&asbestos->lock);\n            block = fiber_lookup(asbestos, ip);\n            if (block == NULL) {\n                block = fiber_block_compile(ip, tlb);\n                fiber_insert(asbestos, block);\n            } else {\n                TRACE(\"%d %08x --- missed cache\\n\", current_pid(), ip);\n            }\n            cache[cache_index] = block;\n            unlock(&asbestos->lock);\n        }\n        struct fiber_block *last_block = frame->last_block;\n        if (last_block != NULL &&\n                (last_block->jump_ip[0] != NULL ||\n                 last_block->jump_ip[1] != NULL)) {\n            lock(&asbestos->lock);\n            // can't mint new pointers to a block that has been marked jetsam\n            // and is thus assumed to have no pointers left\n            if (!last_block->is_jetsam && !block->is_jetsam) {\n                for (int i = 0; i <= 1; i++) {\n                    if (last_block->jump_ip[i] != NULL &&\n                            (*last_block->jump_ip[i] & 0xffffffff) == block->addr) {\n                        *last_block->jump_ip[i] = (unsigned long) block->code;\n                        list_add(&block->jumps_from[i], &last_block->jumps_from_links[i]);\n                    }\n                }\n            }\n\n            unlock(&asbestos->lock);\n        }\n        frame->last_block = block;\n\n        // block may be jetsam, but that's ok, because it can't be freed until\n        // every thread on this asbestos is not executing anything\n\n        TRACE(\"%d %08x --- cycle %ld\\n\", current_pid(), ip, frame->cpu.cycle);\n\n        interrupt = fiber_enter(block, frame, tlb);\n        if (interrupt == INT_NONE && __atomic_exchange_n(cpu->poked_ptr, false, __ATOMIC_SEQ_CST))\n            interrupt = INT_TIMER;\n        if (interrupt == INT_NONE && ++frame->cpu.cycle % (1 << 10) == 0)\n            interrupt = INT_TIMER;\n        *cpu = frame->cpu;\n    }\n\n    free(frame);\n    free(cache);\n    read_wrunlock(&asbestos->jetsam_lock);\n    return interrupt;\n}\n\nstatic int cpu_single_step(struct cpu_state *cpu, struct tlb *tlb) {\n    struct gen_state state;\n    gen_start(cpu->eip, &state);\n    gen_step(&state, tlb);\n    gen_exit(&state);\n    gen_end(&state);\n\n    struct fiber_block *block = state.block;\n    struct fiber_frame frame = {.cpu = *cpu};\n    int interrupt = fiber_enter(block, &frame, tlb);\n    *cpu = frame.cpu;\n    fiber_block_free(NULL, block);\n    if (interrupt == INT_NONE)\n        interrupt = INT_DEBUG;\n    return interrupt;\n}\n\nint cpu_run_to_interrupt(struct cpu_state *cpu, struct tlb *tlb) {\n    if (cpu->poked_ptr == NULL)\n        cpu->poked_ptr = &cpu->_poked;\n    tlb_refresh(tlb, cpu->mmu);\n    int interrupt = (cpu->tf ? cpu_single_step : cpu_step_to_interrupt)(cpu, tlb);\n    cpu->trapno = interrupt;\n\n    struct asbestos *asbestos = cpu->mmu->asbestos;\n    lock(&asbestos->lock);\n    if (!list_empty(&asbestos->jetsam)) {\n        // write-lock the jetsam_lock to wait until other asbestos threads get\n        // to this point, so they will all clear out their block pointers\n        // TODO: use RCU for better performance\n        unlock(&asbestos->lock);\n        write_wrlock(&asbestos->jetsam_lock);\n        lock(&asbestos->lock);\n        fiber_free_jetsam(asbestos);\n        write_wrunlock(&asbestos->jetsam_lock);\n    }\n    unlock(&asbestos->lock);\n\n    return interrupt;\n}\n\nvoid cpu_poke(struct cpu_state *cpu) {\n    __atomic_store_n(cpu->poked_ptr, true, __ATOMIC_SEQ_CST);\n}\n"
  },
  {
    "path": "asbestos/asbestos.h",
    "content": "#ifndef ASBESTOS_H\n#define ASBESTOS_H\n#include \"misc.h\"\n#include \"emu/mmu.h\"\n#include \"util/list.h\"\n#include \"util/sync.h\"\n\n#define FIBER_INITIAL_HASH_SIZE (1 << 10)\n#define FIBER_CACHE_SIZE (1 << 10)\n#define FIBER_PAGE_HASH_SIZE (1 << 10)\n\nstruct asbestos {\n    // there is one asbestos per address space\n    struct mmu *mmu;\n    size_t mem_used;\n    size_t num_blocks;\n\n    struct list *hash;\n    size_t hash_size;\n\n    // list of fiber_blocks that should be freed soon (at the next RCU grace\n    // period, if we had such a thing)\n    struct list jetsam;\n\n    // A way to look up blocks in a page\n    struct {\n        struct list blocks[2];\n    } *page_hash;\n\n    lock_t lock;\n    wrlock_t jetsam_lock;\n};\n\n// this is roughly the average number of instructions in a basic block according to anonymous sources\n// times 4, roughly the average number of gadgets/parameters in an instruction, according to anonymous sources\n#define FIBER_BLOCK_INITIAL_CAPACITY 16\n\nstruct fiber_block {\n    addr_t addr;\n    addr_t end_addr;\n    size_t used;\n\n    // pointers to the ip values in the last gadget\n    unsigned long *jump_ip[2];\n    // original values of *jump_ip[]\n    unsigned long old_jump_ip[2];\n    // blocks that jump to this block\n    struct list jumps_from[2];\n\n    // hashtable bucket links\n    struct list chain;\n    // list of blocks in a page\n    struct list page[2];\n    // links for jumps_from\n    struct list jumps_from_links[2];\n    // links for free list\n    struct list jetsam;\n    bool is_jetsam;\n\n    unsigned long code[];\n};\n\n// Create a new asbestos\nstruct asbestos *asbestos_new(struct mmu *mmu);\nvoid asbestos_free(struct asbestos *asbestos);\n\n// Invalidate all fiber blocks in pages start (inclusive) to end (exclusive).\n// Locks the asbestos. Should only be called by memory.c in conjunction with\n// mem_changed.\nvoid asbestos_invalidate_range(struct asbestos *asbestos, page_t start, page_t end);\nvoid asbestos_invalidate_page(struct asbestos *asbestos, page_t page);\nvoid asbestos_invalidate_all(struct asbestos *asbestos);\n\n#endif\n"
  },
  {
    "path": "asbestos/frame.h",
    "content": "#include <stdatomic.h>\n#include \"emu/cpu.h\"\n\n// keep in sync with asm\n#define FIBER_RETURN_CACHE_SIZE 4096\n#define FIBER_RETURN_CACHE_HASH(x) ((x) & 0xFFF0) >> 4)\n\nstruct fiber_frame {\n    struct cpu_state cpu;\n    void *bp;\n    addr_t value_addr;\n    uint64_t value[2]; // buffer for crosspage crap\n    struct fiber_block *last_block;\n    long ret_cache[FIBER_RETURN_CACHE_SIZE]; // a map of ip to pointer-to-call-gadget-arguments\n};\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/bits.S",
    "content": "#include \"gadgets.h\"\n\n.macro do_shift type, size, s\n    .irp arg, reg_c,imm\n        .gadget \\type\\size\\()_\\arg\n            .ifc \\arg,imm\n                ldr w8, [_ip]\n                ands w8, w8, 31\n            .else\n                ands w8, ecx, 31\n            .endif\n            b.eq 1f\n\n            # shift by one less, then by one more\n            # that way we can retrieve the last bit shifted out for calculating CF and OF\n            .ifc \\type,shl\n                sub w8, w8, 1\n                lsl _tmp, _tmp, w8\n                ubfx w9, _tmp, \\size-1, 1\n                ubfx w10, _tmp, \\size-2, 1\n                lsl _tmp, _tmp, 1\n                eor w10, w10, w9\n                strb w9, [_cpu, CPU_cf]\n                strb w10, [_cpu, CPU_of]\n            .endif\n            .ifc \\type,shr\n                ubfx w10, _tmp, \\size-1, 1\n                sub w8, w8, 1\n                lsr _tmp, _tmp, w8\n                and w9, _tmp, 1\n                lsr _tmp, _tmp, 1\n                strb w9, [_cpu, CPU_cf]\n                strb w10, [_cpu, CPU_of]\n            .endif\n            .ifc \\type,sar\n                # lazy ass copy paste job\n                .ifnb \\s\n                    sxt\\s _tmp, _tmp\n                .endif\n                sub w8, w8, 1\n                asr _tmp, _tmp, w8\n                and w9, _tmp, 1\n                asr _tmp, _tmp, 1\n                strb w9, [_cpu, CPU_cf]\n                strb wzr, [_cpu, CPU_of]\n            .endif\n\n            # regrets\n            .ifin(\\type, rol,ror)\n                .ifb \\s\n                    .ifc \\type,rol\n                        neg w8, w8\n                    .endif\n                    ror _tmp, _tmp, w8\n                .else\n                    # kill me\n                    uxt\\s _tmp, _tmp\n                    neg w9, w8\n                    and w8, w8, \\size-1\n                    and w9, w9, \\size-1\n                    .ifc \\type,rol\n                        lsl w8, _tmp, w8\n                        lsr w9, _tmp, w9\n                    .else\n                        lsr w8, _tmp, w8\n                        lsl w9, _tmp, w9\n                    .endif\n                    orr _tmp, w8, w9\n                .endif\n                .ifc \\type,rol\n                    ubfx w9, _tmp, 0, 1\n                    ubfx w10, _tmp, \\size-1, 1\n                .else\n                    ubfx w9, _tmp, \\size-1, 1\n                    ubfx w10, _tmp, \\size-2, 1\n                .endif\n                eor w10, w10, w9\n                strb w9, [_cpu, CPU_cf]\n                strb w10, [_cpu, CPU_of]\n            .endifin\n\n            # aaaaaaaaaaaaaa\n            .ifin(\\type, rcl,rcr)\n                .ifc \\type,rcr\n                    ubfx w9, _tmp, \\size-1, 1\n                    ldrb w10, [_cpu, CPU_cf]\n                    eor w9, w9, w10\n                    strb w9, [_cpu, CPU_of]\n                .endif\n\n                ldrb w9, [_cpu, CPU_cf]\n                lsl x9, x9, \\size\n                orr _xtmp, _xtmp, x9\n                # so ok we mask the shift count, not too hard\n                and w8, w8, 31\n                # ...now mod by \\size+1 oof\n                .if \\size == 8\n                    .irpc _, 123\n                        subs w10, w8, \\size+1\n                        csel w8, w10, w8, gt\n                    .endr\n                .elseif \\size == 16\n                    subs w10, w8, \\size+1\n                    csel w8, w10, w8, gt\n                .endif\n                mov w9, \\size+1\n                sub w9, w9, w8\n                .ifc \\type,rcl\n                    lsl x8, _xtmp, x8\n                    lsr x9, _xtmp, x9\n                .else\n                    lsr x8, _xtmp, x8\n                    lsl x9, _xtmp, x9\n                .endif\n                orr _xtmp, x8, x9\n                ubfx x9, _xtmp, \\size, 1\n                strb w9, [_cpu, CPU_cf]\n                .ifc \\type,rcl\n                    ubfx w10, _tmp, \\size-1, 1\n                    eor w10, w10, w9\n                    strb w10, [_cpu, CPU_of]\n                .endif\n            .endifin\n\n            .ifin(\\type, shl,shr,sar)\n                setf_zsp \\s\n                clearf_a\n            .endifin\n        1:\n            .ifc \\arg,imm\n                gret 1\n            .else\n                gret\n            .endif\n    .endr\n.endm\n\n.irp type, shl,shr,sar,rol,ror,rcl,rcr\n    .irp size, 8,16,32\n        ss \\size, do_shift, \\type\n    .endr\n    .gadget_array \\type\n.endr\n\n.macro do_shiftd op, arg\n    .macro x name, reg\n        .gadget \\op\\()_\\arg\\()32_\\name\n            .ifc \\arg,imm\n                ldrb w8, [_ip]\n            .else\n                uxtb w8, ecx\n            .endif\n            tst w8, 31\n            b.eq 1f\n            mov w9, 32\n            sub w9, w9, w8\n            sub w8, w8, 1 /* shift by one less */\n            .ifc \\op,shrd\n                lsr w8, _tmp, w8\n                # and by one more\n                and w10, w8, 1\n                lsr w8, w8, 1\n                lsl w9, \\reg, w9\n            .else\n                lsl w8, _tmp, w8\n                # and by one more\n                ubfx w10, w8, 31, 1\n                lsl w8, w8, 1\n                lsr w9, \\reg, w9\n            .endif\n            orr _tmp, w8, w9\n            strb w10, [_cpu, CPU_cf]\n            setf_zsp\n        1:\n            .ifc \\arg,imm\n                gret 1\n            .else\n                gret\n            .endif\n    .endm\n    .each_reg x\n    .purgem x\n    .gadget_array \\op\\()_\\arg\n.endm\n.irp op, shrd,shld\n    .irp arg, imm,cl\n        do_shiftd \\op, \\arg\n    .endr\n.endr\n\n.macro do_bt_op op, arg, size, s\n    .ifc \\op,bt\n        .ifnc \\arg, w8\n            mov w8, \\arg\n        .endif\n        and _tmp, _tmp, \\size-1\n        lsr w8, w8, _tmp\n        and w8, w8, 1\n        strb w8, [_cpu, CPU_cf]\n    .else\n        mov w9, 1\n        and _tmp, _tmp, \\size-1\n        lsl w9, w9, _tmp\n        tst \\arg, w9\n        .ifc \\op,btc\n            eor \\arg, \\arg, w9\n        .else N .ifc \\op,bts\n            orr \\arg, \\arg, w9\n        .else N .ifc \\op,btr\n            bic \\arg, \\arg, w9\n        .endif N .endif N .endif\n        cset w9, ne\n        strb w9, [_cpu, CPU_cf]\n    .endif\n.endm\n\n.macro do_bt op, size, s\n    .gadget \\op\\size\\()_mem\n        bic w8, _tmp, 0x1f\n        add _addr, _addr, w8, lsr 3\n        # hell {{{\n        .ifin(\\op, bt)\n            read_prep \\size, \\op\\size\\()_mem\n        .endifin\n        .ifin(\\op, btc,bts,btr)\n            write_prep \\size, \\op\\size\\()_mem\n        .endifin\n        # }}}\n        ldr w8, [_xaddr]\n        do_bt_op \\op, w8, \\size, \\s\n        .ifin(\\op, btc,bts,btr)\n            str w8, [_xaddr]\n            write_done \\size, \\op\\size\\()_mem\n        .endifin\n        gret 1\n        # also hell {{{\n        .ifin(\\op, bt)\n            read_bullshit \\size, \\op\\size\\()_mem\n        .endifin\n        .ifin(\\op, btc,bts,btr)\n            write_bullshit \\size, \\op\\size\\()_mem\n        .endifin\n        # }}}\n\n    .macro x name, reg\n        .gadget \\op\\size\\()_\\name\n            do_bt_op \\op, \\reg, \\size, \\s\n            gret\n    .endm\n    .each_reg x\n    .purgem x\n.endm\n\n.irp op, bt,btc,bts,btr\n    .irp size, 16,32\n        ss \\size, do_bt, \\op\n    .endr\n    .gadget_array \\op\n.endr\n\n# atomic versions of the above\n\n.macro do_bt_atomic op, size, s\n    .gadget atomic_\\op\\size\\()_mem\n        bic w8, _tmp, 0x1f\n        add _addr, _addr, w8, lsr 3\n        write_prep \\size, atomic_\\op\\size\\()_mem\n        # this is simple enough that I'm comfortable doing it with ldaxr/stlxr\n    1:\n        ldaxr w8, [_xaddr]\n        mov w9, 1\n        and _tmp, _tmp, \\size-1\n        lsl w9, w9, _tmp\n        tst w8, w9\n        .ifc \\op,btc\n            eor w8, w8, w9\n        .else N .ifc \\op,bts\n            orr w8, w8, w9\n        .else N .ifc \\op,btr\n            bic w8, w8, w9\n        .endif N .endif N .endif\n        cset w9, ne\n        stlxr w10, w8, [_xaddr]\n        cbnz w10, 1b\n        strb w9, [_cpu, CPU_cf]\n        write_done \\size, atomic_\\op\\size\\()_mem\n        gret 1\n        write_bullshit \\size, atomic_\\op\\size\\()_mem\n.endm\n\n.irp op, btc,bts,btr\n    .irp size, 16,32\n        ss \\size, do_bt_atomic, \\op\n    .endr\n    .gadget_array atomic_\\op\n.endr\n\n.macro x name reg\n    .gadget bswap_\\name\n        rev \\reg, \\reg\n        gret\n.endm\n.each_reg x\n.purgem x\n.gadget_list bswap, REG_LIST\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/control.S",
    "content": "#include \"gadgets.h\"\n\n.gadget call\n    // save return address\n    sub _addr, esp, 4\n    write_prep 32, call\n    ldr w8, [_ip, 16]\n    str w8, [_xaddr]\n    // push stack pointer\n    sub esp, esp, 4\n    // save ip-to-arguments to return cache\n    ubfx w12, w8, 4, 12\n    write_done 32, call // clobbers w8\n    add x13, _cpu, LOCAL_ret_cache\n    str _ip, [x13, x12, lsl 3]\n    // jump to target\n    ldr _ip, [_ip, 32]\n    b fiber_ret_chain\n    write_bullshit 32, call\n\n.gadget call_indir\n    // save return address\n    sub _addr, esp, 4\n    write_prep 32, call_indir\n    ldr w8, [_ip, 16]\n    str w8, [_xaddr]\n    // push stack pointer\n    sub esp, esp, 4\n    // save ip-to-arguments to return cache\n    ubfx w12, w8, 4, 12\n    write_done 32, call_indir // clobbers w8\n    add x13, _cpu, LOCAL_ret_cache\n    str _ip, [x13, x12, lsl 3]\n    // jump to target\n    mov eip, _tmp\n    b fiber_ret\n    write_bullshit 32, call_indir\n\n.gadget ret\n    mov _addr, esp\n    // load return address and save to _tmp\n    read_prep 32, ret\n    ldr _tmp, [_xaddr]\n    // pop stack pointer\n    ldr w8, [_ip, 8]\n    add esp, esp, w8\n    // load saved ip in return cache\n    ubfx w12, _tmp, 4, 12\n    add x13, _cpu, LOCAL_ret_cache\n    ldr _ip, [x13, x12, lsl 3]\n    // found?\n    cbz _ip, 2f\n    // check if we jumped to the correct CALL instruction\n    ldr w9, [_ip, 16]\n    ldr x8, [_ip, 8]\n    cmp _tmp, w9\n    b.ne 1f\n    // good, now do return chaining, the logic is similar to `fiber_ret_chain`\n    ldr _ip, [_ip, 24]\n    cmp _ip, 0\n    b.lt 1f\n    sub x8, _ip, FIBER_BLOCK_code\n    str x8, [_cpu, LOCAL_last_block]\n    gret\n1:\n    str x8, [_cpu, LOCAL_last_block]\n    // fallthrough\n2:\n    mov eip, _tmp\n    b fiber_ret\n    read_bullshit 32, ret\n\n.gadget jmp_indir\n    mov eip, _tmp\n    b fiber_ret\n.gadget jmp\n    ldr _ip, [_ip]\n    b fiber_ret_chain\n.gadget jcxz\n    cbnz ecx, 1f\n    ldr _ip, [_ip]\n    b fiber_ret_chain\n1:\n    ldr _ip, [_ip, 8]\n    b fiber_ret_chain\n\n#define COND_LIST o,c,z,cz,s,p,sxo,sxoz\n\n.macro check_res\n    cmpl $0, CPU_res(%_cpu)\n.endm\n.macro check_cf\n    cmpb $0, CPU_cf(%_cpu)\n.endm\n\n.macro check_res_or_flag resflag, flag, target, fallthrough\n    ldr w8, [_cpu, CPU_flags_res]\n    tbnz w8, \\resflag, 2f\n    ldr w8, [_cpu, CPU_eflags]\n    tbnz w8, \\flag, \\target\n    b \\fallthrough\n2:\n.endm\n\n.macro do_jump cond, target\n    # please tell me if you know a better way\n    .ifc \\cond,o\n        ldrb w8, [_cpu, CPU_of]\n        cbnz w8, \\target\n    .else N .ifc \\cond,c\n        ldrb w8, [_cpu, CPU_cf]\n        cbnz w8, \\target\n    .else N .ifc \\cond,z\n        check_res_or_flag 1/*ZF_RES*/, 6/*ZF_FLAG*/, \\target, 3f\n        ldr w8, [_cpu, CPU_res]\n        cbz w8, \\target\n    3:\n    .else N .ifc \\cond,cz\n        ldrb w8, [_cpu, CPU_cf]\n        cbnz w8, \\target\n        check_res_or_flag 1/*ZF_RES*/, 6/*ZF_FLAG*/, \\target, 3f\n        ldr w8, [_cpu, CPU_res]\n        cbz w8, \\target\n    3:\n    .else N .ifc \\cond,s\n        check_res_or_flag 2/*SF_RES*/, 7/*SF_FLAG*/, \\target, 3f\n        ldr w8, [_cpu, CPU_res]\n        cmp w8, 0\n        b.lt \\target\n    3:\n    .else N .ifc \\cond,p\n        check_res_or_flag 0/*PF_RES*/, 2/*PF_FLAG*/, \\target, 3f\n        # this is so sad\n        ldr w8, [_cpu, CPU_res]\n        uxtb w8, w8\n        fmov s0, w8\n        cnt v0.8b, v0.8b\n        uaddlv h0, v0.8b\n        fmov w8, s0\n        tbz w8, 0, \\target\n    3:\n    .else N .ifc \\cond,sxo\n        ldr w8, [_cpu, CPU_res]\n        cmp w8, 0\n        cset w8, lt\n        ldrb w9, [_cpu, CPU_of]\n        cmp w8, w9\n        b.ne \\target\n    .else N .ifc \\cond,sxoz\n        ldr w8, [_cpu, CPU_res]\n        cmp w8, 0\n        b.eq \\target\n        cset w8, lt\n        ldrb w9, [_cpu, CPU_of]\n        cmp w8, w9\n        b.ne \\target\n    .endif N .endif N .endif N .endif N .endif N .endif N .endif N .endif\n.endm\n\n.irp cond, COND_LIST\n    .gadget jmp_\\cond\n        do_jump \\cond, 1f\n        ldr _ip, [_ip, 8]\n        b fiber_ret_chain\n    1:  ldr _ip, [_ip]\n        b fiber_ret_chain\n\n    .gadget set_\\cond\n        do_jump \\cond, 1f\n        mov _tmp, 0\n        gret\n    1:  mov _tmp, 1\n        gret\n    .gadget setn_\\cond\n        do_jump \\cond, 1f\n        mov _tmp, 1\n        gret\n    1:  mov _tmp, 0\n        gret\n\n    .gadget skip_\\cond\n        do_jump \\cond, 1f\n        gret 1\n    1:  ldr x8, [_ip]\n        add _ip, _ip, x8\n        gret 1\n    .gadget skipn_\\cond\n        do_jump \\cond, 1f\n        ldr x8, [_ip]\n        add _ip, _ip, x8\n    1:  gret 1\n.endr\n.gadget_list jmp, COND_LIST\n.gadget_list set, COND_LIST\n.gadget_list setn, COND_LIST\n.gadget_list skip, COND_LIST\n.gadget_list skipn, COND_LIST\n\n.gadget pushf\n    save_c\n    mov x0, _cpu\n    bl NAME(helper_collapse_flags)\n    restore_c\n\n    sub esp, esp, 4\n    mov _addr, esp\n    write_prep 32, pushf\n    ldr w8, [_cpu, CPU_eflags]\n    str w8, [_xaddr]\n    write_done 32, pushf\n    gret\n    write_bullshit 32, pushf\n\n.gadget popf\n    mov _addr, esp\n    read_prep 32, popf\n    ldr w8, [_xaddr]\n    str w8, [_cpu, CPU_eflags]\n    add esp, esp, 4\n\n    save_c\n    mov x0, _cpu\n    bl NAME(helper_expand_flags)\n    restore_c\n    gret\n    read_bullshit 32, popf\n\n.gadget sahf\n    ubfx w8, eax, 8, 8\n    strb w8, [_cpu, CPU_eflags]\n    save_c\n    mov x0, _cpu\n    bl NAME(helper_expand_flags)\n    restore_c\n    gret\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/entry.S",
    "content": "#include \"emu/interrupt.h\"\n#include \"gadgets.h\"\n\n.global NAME(fiber_enter)\n.type_compat fiber_enter,function\nNAME(fiber_enter):\n    stp x18, x19, [sp, -0x70]!\n    stp x20, x21, [sp, 0x10]\n    stp x22, x23, [sp, 0x20]\n    stp x24, x25, [sp, 0x30]\n    stp x26, x27, [sp, 0x40]\n    stp x28, x29, [sp, 0x50]\n    str lr, [sp, 0x60]\n    add _ip, x0, FIBER_BLOCK_code\n    # cpu is already x1\n    add _tlb, x2, TLB_entries\n    load_regs\n    gret\n\n.global fiber_ret_chain\nfiber_ret_chain:\n    cmp _ip, 0\n    b.lt fiber_ret\n    ldr x8, [_cpu, CPU_poked_ptr]\n    ldrb w8, [x8]\n    cmp w8, 0\n    b.ne poke\n    sub x8, _ip, FIBER_BLOCK_code\n    str x8, [_cpu, LOCAL_last_block]\n    gret\n\npoke:\n    ldr eip, [_ip, -FIBER_BLOCK_code+FIBER_BLOCK_addr]\n    # fallthrough\n\n.global fiber_ret\nfiber_ret:\n    # load -1\n    mvn _tmp, wzr\n    # fallthrough\n\n.global fiber_exit\nfiber_exit:\n    save_regs\n    ldr lr, [sp, 0x60]\n    ldp x28, x29, [sp, 0x50]\n    ldp x26, x27, [sp, 0x40]\n    ldp x24, x25, [sp, 0x30]\n    ldp x22, x23, [sp, 0x20]\n    ldp x20, x21, [sp, 0x10]\n    ldp x18, x19, [sp], 0x70\n    # _tmp is already x0\n    ret\n\n.gadget interrupt\n    ldr _tmp, [_ip]\n    ldr w8, [_ip, 16]\n    str w8, [_cpu, CPU_segfault_addr]\n    ldr eip, [_ip, 8]\n    strb wzr, [_cpu, CPU_segfault_was_write]\n    b fiber_exit\n\n.gadget exit\n    ldr eip, [_ip]\n    b fiber_ret\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/gadgets.h",
    "content": "#include \"../gadgets-generic.h\"\n#include \"cpu-offsets.h\"\n\n# register assignments\neax .req w20\nxax .req x20\nebx .req w21\necx .req w22\nxcx .req x22\nedx .req w23\nxdx .req x23\nesi .req w24\nedi .req w25\nebp .req w26\nesp .req w27\n_ip .req x28\neip .req w28\n_tmp .req w0\n_xtmp .req x0\n_cpu .req x1\n_tlb .req x2\n_addr .req w3\n_xaddr .req x3\n\n.extern fiber_exit\n\n.macro .gadget name\n    .global NAME(gadget_\\()\\name)\n    .align 4\n    NAME(gadget_\\()\\name) :\n.endm\n.macro gret pop=0\n    ldr x8, [_ip, \\pop*8]!\n    add _ip, _ip, 8 /* TODO get rid of this */\n    br x8\n.endm\n\n# memory reading and writing\n.irp type, read,write\n\n.macro \\type\\()_prep size, id\n    and w8, _addr, 0xfff\n    cmp x8, (0x1000-(\\size/8))\n    b.hi crosspage_load_\\id\n    and w8, _addr, 0xfffff000\n    str w8, [_tlb, (-TLB_entries+TLB_dirty_page)]\n    ubfx x9, _xaddr, 12, 10\n    eor x9, x9, _xaddr, lsr 22\n    lsl x9, x9, 4\n    add x9, x9, _tlb\n    .ifc \\type,read\n        ldr w10, [x9, TLB_ENTRY_page]\n    .else\n        ldr w10, [x9, TLB_ENTRY_page_if_writable]\n    .endif\n    cmp w8, w10\n    b.ne handle_miss_\\id\n    ldr x10, [x9, TLB_ENTRY_data_minus_addr]\n    add _xaddr, x10, _xaddr, uxtx\nback_\\id:\n.endm\n\n.macro \\type\\()_bullshit size, id\nhandle_miss_\\id :\n    bl handle_\\type\\()_miss\n    b back_\\id\ncrosspage_load_\\id :\n    mov x19, (\\size/8)\n    bl crosspage_load\n    b back_\\id\n.ifc \\type,write\ncrosspage_store_\\id :\n    mov x19, (\\size/8)\n    bl crosspage_store\n    b back_write_done_\\id\n.endif\n.endm\n\n.endr\n.macro write_done size, id\n    add x8, _cpu, LOCAL_value\n    cmp x8, _xaddr\n    b.eq crosspage_store_\\id\nback_write_done_\\id :\n.endm\n\n.macro .each_reg macro:vararg\n    \\macro reg_a, eax\n    \\macro reg_b, ebx\n    \\macro reg_c, ecx\n    \\macro reg_d, edx\n    \\macro reg_si, esi\n    \\macro reg_di, edi\n    \\macro reg_bp, ebp\n    \\macro reg_sp, esp\n.endm\n\n.macro ss size, macro, args:vararg\n    .ifnb \\args\n        .if \\size == 8\n            \\macro \\args, \\size, b\n        .elseif \\size == 16\n            \\macro \\args, \\size, h\n        .elseif \\size == 32\n            \\macro \\args, \\size,\n        .else\n            .error \"bad size\"\n        .endif\n    .else\n        .if \\size == 8\n            \\macro \\size, b\n        .elseif \\size == 16\n            \\macro \\size, h\n        .elseif \\size == 32\n            \\macro \\size,\n        .else\n            .error \"bad size\"\n        .endif\n    .endif\n.endm\n\n.macro setf_c\n    cset w10, cc\n    strb w10, [_cpu, CPU_cf]\n.endm\n.macro setf_oc\n    cset w10, vs\n    strb w10, [_cpu, CPU_of]\n    setf_c\n.endm\n.macro setf_a src, dst\n    str \\src, [_cpu, CPU_op1]\n    str \\dst, [_cpu, CPU_op2]\n    ldr w10, [_cpu, CPU_flags_res]\n    orr w10, w10, AF_OPS\n    str w10, [_cpu, CPU_flags_res]\n.endm\n.macro clearf_a\n    ldr w10, [_cpu, CPU_eflags]\n    ldr w11, [_cpu, CPU_flags_res]\n    bic w10, w10, AF_FLAG\n    bic w11, w11, AF_OPS\n    str w10, [_cpu, CPU_eflags]\n    str w11, [_cpu, CPU_flags_res]\n.endm\n.macro clearf_oc\n    strb wzr, [_cpu, CPU_of]\n    strb wzr, [_cpu, CPU_cf]\n.endm\n.macro setf_zsp s, val=_tmp\n    .ifnb \\s\n        sxt\\s \\val, \\val\n    .endif\n    str \\val, [_cpu, CPU_res]\n    ldr w10, [_cpu, CPU_flags_res]\n    orr w10, w10, (ZF_RES|SF_RES|PF_RES)\n    str w10, [_cpu, CPU_flags_res]\n.endm\n\n.macro save_c\n    stp x0, x1, [sp, -0x60]!\n    stp x2, x3, [sp, 0x10]\n    stp x8, x9, [sp, 0x20]\n    stp x10, x11, [sp, 0x30]\n    stp x12, x13, [sp, 0x40]\n    str lr, [sp, 0x50]\n.endm\n.macro restore_c\n    ldr lr, [sp, 0x50]\n    ldp x12, x13, [sp, 0x40]\n    ldp x10, x11, [sp, 0x30]\n    ldp x8, x9, [sp, 0x20]\n    ldp x2, x3, [sp, 0x10]\n    ldp x0, x1, [sp], 0x60\n.endm\n\n.macro movs dst, src, s\n    .ifc \\s,h\n        bfxil \\dst, \\src, 0, 16\n    .else N .ifc \\s,b\n        bfxil \\dst, \\src, 0, 8\n    .else\n        mov \\dst, \\src\n    .endif N .endif\n.endm\n.macro op_s op, dst, src1, src2, s\n    .ifb \\s\n        \\op \\dst, \\src1, \\src2\n    .else\n        movs w10, \\dst, \\s\n        \\op w10, \\src1, \\src2\n        movs \\dst, w10, \\s\n    .endif\n.endm\n.macro ldrs src, dst, s\n    ldr\\s w10, \\dst\n    movs \\src, w10, \\s\n.endm\n\n.macro uxts dst, src, s=\n    .ifnb \\s\n        uxt\\s \\dst, \\src\n        .exitm\n    .endif\n    .ifnc \\dst,\\src\n        mov \\dst, \\src\n    .endif\n.endm\n\n.macro load_regs\n    ldr eax, [_cpu, CPU_eax]\n    ldr ebx, [_cpu, CPU_ebx]\n    ldr ecx, [_cpu, CPU_ecx]\n    ldr edx, [_cpu, CPU_edx]\n    ldr esi, [_cpu, CPU_esi]\n    ldr edi, [_cpu, CPU_edi]\n    ldr ebp, [_cpu, CPU_ebp]\n    ldr esp, [_cpu, CPU_esp]\n.endm\n\n.macro save_regs\n    str eax, [_cpu, CPU_eax]\n    str ebx, [_cpu, CPU_ebx]\n    str ecx, [_cpu, CPU_ecx]\n    str edx, [_cpu, CPU_edx]\n    str edi, [_cpu, CPU_edi]\n    str esi, [_cpu, CPU_esi]\n    str ebp, [_cpu, CPU_ebp]\n    str esp, [_cpu, CPU_esp]\n    str eip, [_cpu, CPU_eip]\n.endm\n\n# vim: ft=gas\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/math.S",
    "content": "#include \"gadgets.h\"\n#include \"math.h\"\n\n.gadget load32_addr\n    mov _tmp, _addr\n    gret\n\n.gadget load16_gs\n    ldrh _tmp, [_cpu, #CPU_gs]\n    gret\n\n.gadget store16_gs\n    strh _tmp, [_cpu, #CPU_gs]\n    gret\n\n# this would have been just a few nice compact nested loops, but gas said \"nuh uh\"\n\n.macro _do_op op, arg, size, s\n    .ifc \\op,load\n        movs _tmp, \\arg, \\s\n        uxts _tmp, _tmp, \\s\n        .exitm\n    .else N .ifc \\op,store\n        movs \\arg, _tmp, \\s\n        .exitm\n    .endif N .endif\n\n    .ifin(\\op, add,sub,adc,sbc)\n        setf_a \\arg, _tmp\n    .endifin\n    .ifin(\\op, and,orr,eor)\n        clearf_a\n        clearf_oc\n    .endifin\n    .ifin(\\op, adc,sbc)\n        ldrb w10, [_cpu, CPU_cf]\n        .ifc \\op,adc\n            cmp w10, 1\n        .else\n            mvn w10, w10\n            cmn w10, 1\n        .endif\n    .endifin\n\n    .ifin(\\op, and,orr,eor)\n        \\op _tmp, _tmp, \\arg\n    .endifin\n\n    .ifin(\\op, add,sub,adc,sbc)\n        do_add \\op, _tmp, \\arg, \\s\n    .endifin\n\n    .ifc \\op,imul\n        .ifnb \\s\n            sxt\\s w10, \\arg\n            mul _tmp, _tmp, w10\n            cmp _tmp, _tmp, sxt\\s\n        .else\n            smull _xtmp, _tmp, \\arg\n            cmp _xtmp, _tmp, sxtw\n        .endif\n        cset w10, ne\n        strb w10, [_cpu, CPU_cf]\n        strb w10, [_cpu, CPU_of]\n    .endif\n\n    .ifin(\\op, bsf,bsr)\n        .ifnb \\s\n            uxt\\s w10, \\arg\n        .else\n            mov w10, \\arg\n        .endif\n        .ifc \\op,bsf\n            .if \\size != 32\n                orr w10, w10, 1<<\\size\n            .endif\n            rbit w10, w10\n            clz w10, w10\n            cmp w10, \\size\n        .else\n            clz w10, w10\n            .if \\size != 32\n                sub w10, w10, 32-\\size\n            .endif\n            cmp w10, \\size\n            mov w9, \\size-1\n            sub w10, w9, w10\n        .endif\n        csel _tmp, w10, _tmp, ne\n        cset w10, eq\n        ldrb w9, [_cpu, CPU_eflags]\n        bic w9, w9, ZF_FLAG\n        orr w9, w9, w10, lsl 6\n        strb w9, [_cpu, CPU_eflags]\n        ldrb w9, [_cpu, CPU_flags_res]\n        bic w9, w9, ZF_RES\n        strb w9, [_cpu, CPU_flags_res]\n    .endifin\n\n    .ifc \\op,xchg\n        mov w9, _tmp\n        mov _tmp, \\arg\n        movs \\arg, w9, \\s\n    .endif\n\n    .ifin(\\op, add,sub,adc,sbc,and,orr,eor)\n        setf_zsp \\s\n    .endifin\n.endm\n.macro do_op op, size, arg\n    ss \\size, _do_op, \\op, \\arg\n.endm\n\n.macro do_reg_op op, armop, size, reg\n    .gadget \\op\\size\\()_reg_\\reg\n        do_op \\armop, \\size, e\\reg\\()x\n        gret\n.endm\n\n.macro do_hi_op op, size, reg\n    ubfx w12, e\\reg\\()x, 8, 8\n    do_op \\op, \\size, w12\n    bfi e\\reg\\()x, w12, 8, 8\n.endm\n\n.macro do_op_size op, armop, size, s\n    .ifnc \\op,store\n        .gadget \\op\\size\\()_imm\n            ldr\\s w8, [_ip]\n            do_op \\armop, \\size, w8\n            gret 1\n    .endif\n\n    .ifnc \\op,xchg\n        .gadget \\op\\size\\()_mem\n            .ifc \\op,store\n                write_prep \\size, \\op\\size\\()_mem\n            .else N .ifc \\op,xchg\n                write_prep \\size, \\op\\size\\()_mem\n            .else\n                read_prep \\size, \\op\\size\\()_mem\n            .endif N .endif\n            ldr\\s w8, [_xaddr]\n            do_op \\armop, \\size, w8\n            .ifc \\op,store\n                str\\s w8, [_xaddr]\n                write_done \\size, \\op\\size\\()_mem\n            .endif\n            gret 1\n            .ifc \\op,store\n                write_bullshit \\size, \\op\\size\\()_mem\n            .else N .ifc \\op,xchg\n                write_bullshit \\size, \\op\\size\\()_mem\n            .else\n                read_bullshit \\size, \\op\\size\\()_mem\n            .endif N .endif\n    .else\n        # xchg must be atomic\n        .gadget \\op\\size\\()_mem\n            write_prep \\size, \\op\\size\\()_mem\n        1:\n            ldaxr\\s w8, [_xaddr]\n            stlxr\\s w10, _tmp, [_xaddr]\n            cbnz w10, 1b\n            movs _tmp, w8\n            write_done \\size, \\op\\size\\()_mem\n            gret 1\n            write_bullshit \\size, \\op\\size\\()_mem\n    .endif\n\n    .irp reg, a,b,c,d\n        do_reg_op \\op, \\armop, \\size, \\reg\n    .endr\n\n    .irp reg, si,di,sp,bp\n        .gadget \\op\\size\\()_reg_\\reg\n            .if \\size == 8\n                .ifc \\reg,sp N do_hi_op \\armop, \\size, a N .else\n                .ifc \\reg,bp N do_hi_op \\armop, \\size, c N .else\n                .ifc \\reg,si N do_hi_op \\armop, \\size, d N .else\n                .ifc \\reg,di N do_hi_op \\armop, \\size, b\n                .endif N .endif N .endif N .endif\n            .else\n                do_op \\armop, \\size, e\\reg\n            .endif\n            gret\n    .endr\n\n.endm\n\n.irp op, load,store,xchg,add,sub,adc,sbb,and,or,xor\n    .irp size, SIZE_LIST\n        # a couple operations have slightly different names on arm\n        .ifc \\op,xor\n            ss \\size, do_op_size, \\op, eor\n        .else N .ifc \\op,sbb\n            ss \\size, do_op_size, \\op, sbc\n        .else N .ifc \\op,or\n            ss \\size, do_op_size, \\op, orr\n        .else\n            ss \\size, do_op_size, \\op, \\op\n        .endif N .endif N .endif\n    .endr\n    .gadget_array \\op\n.endr\n.irp op, imul,bsf,bsr\n    .irp size, 16,32\n        ss \\size, do_op_size, \\op, \\op\n    .endr\n    .gadget_array \\op\n.endr\n\n# atomics. oof\n\n.macro do_op_size_atomic opname, op, size, s\n    .gadget atomic_\\opname\\size\\()_mem\n        # There's so much stuff going on inside most of these operations that\n        # the implementation is a compare-and-swap loop, instead of just ldaxr/stlxr\n        write_prep \\size, atomic_\\opname\\size\\()_mem\n        ldr\\s w12, [_xaddr]\n    1:\n        mov w8, w12\n\n        # do the operation\n        # dest = w8, src = _tmp\n        .ifin(\\op, add,sub,adc,sbc)\n            setf_a src=_tmp, dst=w8\n        .endifin\n        .ifin(\\op, and,orr,eor)\n            clearf_a\n            clearf_oc\n        .endifin\n        .ifin(\\op, adc,sbc)\n            ldrb w10, [_cpu, CPU_cf]\n            .ifc \\op,adc\n                cmp w10, 1\n            .else\n                mvn w10, w10\n                cmn w10, 1\n            .endif\n        .endifin\n\n        .ifin(\\op, and,orr,eor)\n            \\op w8, w8, _tmp\n        .endifin\n        .ifin(\\op, add,sub,adc,sbc)\n            do_add \\op, w8, _tmp, \\s\n        .endifin\n        .ifc \\op,xadd\n            # exchange, then add\n            mov w9, w8\n            mov w8, _tmp\n            do_add add, w8, w9, \\s\n        .endif\n\n        .ifin(\\op, add,sub,adc,sbc,and,orr,eor,xadd)\n            setf_zsp \\s, val=w8\n        .endifin\n\n        .ifin(\\op, inc,dec)\n            mov w10, 1\n            setf_a src=w10, dst=w8\n            .ifb \\s\n                .ifc \\op,inc\n                    adds w8, w8, 1\n                .else\n                    subs w8, w8, 1\n                .endif\n                cset w9, vs\n            .else\n                sxt\\s w8, w8\n                .ifc \\op,inc\n                    adds w8, w8, 1\n                .else\n                    subs w8, w8, 1\n                .endif\n                cmp w8, w8, sxt\\s\n                cset w9, ne\n            .endif\n            strb w9, [_cpu, CPU_of]\n            setf_zsp \\s, val=w8\n        .endifin\n\n    2:\n        ldaxr\\s w13, [_xaddr]\n        cmp w12, w13\n        b.ne 3f\n        stlxr\\s w13, w8, [_xaddr]\n        cbnz w13, 2b\n        .ifc \\op,xadd\n            mov _tmp, w9\n        .endif\n        write_done \\size, atomic_\\opname\\size\\()_mem\n        gret 1\n        write_bullshit \\size, atomic_\\opname\\size\\()_mem\n    3:\n        dmb ish\n        mov w12, w13\n        b 1b\n.endm\n\n.irp op, add,sub,adc,sbb,and,or,xor,inc,dec,xadd\n    .irp size, SIZE_LIST\n        .ifc \\op,xor\n            ss \\size, do_op_size_atomic, \\op, eor\n        .else N .ifc \\op,sbb\n            ss \\size, do_op_size_atomic, \\op, sbc\n        .else N .ifc \\op,or\n            ss \\size, do_op_size_atomic, \\op, orr\n        .else\n            ss \\size, do_op_size_atomic, \\op, \\op\n        .endif N .endif N .endif\n    .endr\n    .gadget_array atomic_\\op\n.endr\n\n# unary operations (well, only one explicit operand)\n\n.macro do_inc size, s\n    mov w10, 1\n    setf_a w10, _tmp\n    .ifb \\s\n        adds _tmp, _tmp, 1\n        cset w8, vs\n    .else\n        sxt\\s _tmp, _tmp\n        add _tmp, _tmp, 1\n        cmp _tmp, _tmp, sxt\\s\n        cset w8, ne\n    .endif\n    strb w8, [_cpu, CPU_of]\n    setf_zsp \\s\n.endm\n.macro do_dec size, s\n    mov w10, 1\n    setf_a w10, _tmp\n    .ifb \\s\n        subs _tmp, _tmp, 1\n        cset w8, vs\n    .else\n        sxt\\s _tmp, _tmp\n        sub _tmp, _tmp, 1\n        cmp _tmp, _tmp, sxt\\s\n        cset w8, ne\n    .endif\n    strb w8, [_cpu, CPU_of]\n    setf_zsp \\s\n.endm\n\n.macro do_sign_extend size, s\n    .if \\size != 32\n        # movs\\ss\\()l %tmp\\s, %tmpd\n        sxt\\s _tmp, _tmp\n    .endif\n.endm\n.macro do_zero_extend size, s\n    .if \\size != 32\n        uxt\\s _tmp, _tmp\n    .endif\n.endm\n.macro do_div size, s\n    .if \\size == 8\n        uxth w8, eax\n        uxtb _tmp, _tmp\n        udiv w9, w8, _tmp\n        msub w10, w9, _tmp, w8\n        bfi eax, w9, 0, 8\n        bfi eax, w10, 8, 8\n    .elseif \\size == 16\n        bfi w8, eax, 0, 16\n        bfi w8, edx, 16, 16\n        uxth _tmp, _tmp\n        udiv w9, w8, _tmp\n        msub w10, w9, _tmp, w8\n        bfi eax, w9, 0, 16\n        bfi edx, w10, 0, 16\n    .elseif \\size == 32\n        bfi x8, xax, 0, 32\n        bfi x8, xdx, 32, 32\n        uxtw _xtmp, _tmp\n        udiv x9, x8, _xtmp\n        msub x10, x9, _xtmp, x8\n        mov eax, w9\n        mov edx, w10\n    .endif\n.endm\n.macro do_idiv size, s\n    # another lazy ass copy paste job\n    .if \\size == 8\n        sxth w8, eax\n        sxtb _tmp, _tmp\n        sdiv w9, w8, _tmp\n        msub w10, w9, _tmp, w8\n        bfi eax, w9, 0, 8\n        bfi eax, w10, 8, 8\n    .elseif \\size == 16\n        bfi w8, eax, 0, 16\n        bfi w8, edx, 16, 16\n        sxth _tmp, _tmp\n        sdiv w9, w8, _tmp\n        msub w10, w9, _tmp, w8\n        bfi eax, w9, 0, 16\n        bfi edx, w10, 0, 16\n    .elseif \\size == 32\n        bfi x8, xax, 0, 32\n        bfi x8, xdx, 32, 32\n        sxtw _xtmp, _tmp\n        sdiv x9, x8, _xtmp\n        msub x10, x9, _xtmp, x8\n        mov eax, w9\n        mov edx, w10\n    .endif\n.endm\n.macro do_mul size, s\n    .ifb \\s\n        umull xax, eax, _tmp\n        lsr xdx, xax, 32\n        cmp xax, eax, uxtw\n    .else\n        uxt\\s w8, eax\n        uxt\\s _tmp, _tmp\n        mul w8, w8, _tmp\n        cmp w8, w8, uxt\\s\n        .if \\size == 8\n            bfxil eax, w8, 0, \\size*2\n        .else\n            bfxil eax, w8, 0, \\size\n            bfxil edx, w8, \\size, \\size\n        .endif\n    .endif\n    cset w8, ne\n    strb w8, [_cpu, CPU_cf]\n    strb w8, [_cpu, CPU_of]\n.endm\n.macro do_imul1 size, s\n    .ifb \\s\n        smull xax, eax, _tmp\n        lsr xdx, xax, 32\n        cmp xax, eax, sxtw\n    .else\n        sxt\\s w8, eax\n        sxt\\s _tmp, _tmp\n        mul w8, w8, _tmp\n        cmp w8, w8, sxt\\s\n        .if \\size == 8\n            bfxil eax, w8, 0, \\size*2\n        .else\n            bfxil eax, w8, 0, \\size\n            bfxil edx, w8, \\size, \\size\n        .endif\n    .endif\n    cset w8, ne\n    strb w8, [_cpu, CPU_cf]\n    strb w8, [_cpu, CPU_of]\n.endm\n.macro do_not size, s\n    .ifb \\s\n        mvn _tmp, _tmp\n    .else\n        movs w10, _tmp, \\s\n        mvn w10, w10\n        movs _tmp, w10, \\s\n    .endif\n.endm\n\n.irp op, inc,dec,sign_extend,zero_extend,div,idiv,mul,imul1,not\n    .irp size, SIZE_LIST\n        .gadget \\op\\()_\\size\n            ss \\size, do_\\op\n            gret\n    .endr\n    .gadget_list \\op, SIZE_LIST\n.endr\n\n.gadget cvt_16\n    tst eax, 0x8000\n    cinv w8, wzr, ne\n    bfxil edx, w8, 0, 16\n    gret\n.gadget cvt_32\n    tst eax, 0x80000000\n    cinv edx, wzr, ne\n    gret\n.gadget_list cvt, SIZE_LIST\n\n.gadget cvte_16\n    sxtb w8, eax\n    bfxil eax, w8, 0, 16\n    gret\n.gadget cvte_32\n    sxth eax, eax\n    gret\n.gadget_list cvte, SIZE_LIST\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/math.h",
    "content": ".macro do_add op, dst, src, s\n    # setting flags: a horror story\n    .ifb \\s\n        # for 32-bit operands, we can just do the operation and the chip\n        # will set v and c right, which we copy\n        \\op\\()s \\dst, \\dst, \\src\n        cset w10, vs\n        strb w10, [_cpu, CPU_of]\n        .ifin(\\op, add,adc)\n            cset w10, cs\n        .endifin\n        .ifin(\\op, sub,sbc)\n            cset w10, cc\n        .endifin\n        strb w10, [_cpu, CPU_cf]\n    .else\n        # for 16 or 8 bit operands...\n        # first figure out unsigned overflow\n        uxt\\s w10, \\dst\n        .ifin(\\op, add,sub)\n            \\op w10, w10, \\src, uxt\\s\n        .endifin\n        .ifin(\\op, adc,sbc)\n            uxt\\s w9, \\src\n            \\op w10, w10, w9\n        .endifin\n        .ifc \\s,b\n            lsr w10, w10, 8\n        .else\n            lsr w10, w10, 16\n        .endif\n        strb w10, [_cpu, CPU_cf]\n        # now signed overflow\n        sxt\\s w10, \\dst\n        .ifin(\\op, add,sub)\n            \\op \\dst, w10, \\src, sxt\\s\n        .endifin\n        .ifin(\\op, adc,sbc)\n            # help me\n            sxt\\s w9, \\src\n            \\op \\dst, w10, w9\n        .endifin\n        cmp \\dst, \\dst, sxt\\s\n        cset w10, ne\n        strb w10, [_cpu, CPU_of]\n    .endif\n.endm\n\n# vim: ft=gas\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/memory.S",
    "content": "#include \"gadgets.h\"\n#include \"emu/interrupt.h\"\n\n.gadget push\n    sub _addr, esp, 4\n    write_prep 32, push\n    str _tmp, [_xaddr]\n    write_done 32, push\n    sub esp, esp, 4\n    gret 1\n    write_bullshit 32, push\n.gadget pop\n    mov _addr, esp\n    read_prep 32, pop\n    ldr _tmp, [_xaddr]\n    add esp, esp, 4\n    gret 1\n    read_bullshit 32, pop\n\n.macro x name, reg\n    .gadget addr_\\name\n        ldr _addr, [_ip]\n        add _addr, _addr, \\reg\n        gret 1\n.endm\n.each_reg x\n.purgem x\n.gadget addr_none\n    ldr _addr, [_ip]\n    gret 1\n.gadget_list addr, REG_LIST\n\n.macro x name, reg\n    .irp times, 1,2,4,8\n        .gadget si_\\name\\()_\\times\n            .ifc \\times,1\n                add _addr, _addr, \\reg\n            .else N .ifc \\times,2\n                add _addr, _addr, \\reg, lsl 1\n            .else N .ifc \\times,4\n                add _addr, _addr, \\reg, lsl 2\n            .else N .ifc \\times,8\n                add _addr, _addr, \\reg, lsl 3\n            .endif N .endif N .endif N .endif\n            gret\n    .endr\n.endm\n.each_reg x\n.purgem x\n\n.pushsection_rodata\n.global NAME(si_gadgets)\nNAME(si_gadgets):\n.irp reg, REG_LIST\n    .irp times, 1,2,4,8\n        .quad NAME(gadget_si_\\reg\\()_\\times)\n    .endr\n.endr\n.popsection\n\n.gadget seg_gs\n    ldr w8, [_cpu, CPU_tls_ptr]\n    add _addr, _addr, w8\n    gret\n\n.irp type, read,write\n\n.global handle_\\type\\()_miss\nhandle_\\type\\()_miss:\n    save_c\n    # %tlb actually points to tlb->entries\n    sub x0, _tlb, TLB_entries\n    mov w1, _addr\n    .ifc \\type,read\n        mov x2, xzr\n    .else\n        mov x2, 1\n    .endif\n    bl NAME(tlb_handle_miss)\n    mov x19, x0\n    restore_c\n    cbz x19, segfault_\\type\n    mov _xaddr, x19\n    ret\n\n.global segfault_\\type\nsegfault_\\type:\n    ldr _addr, [_tlb, -TLB_entries+TLB_segfault_addr]\n    str _addr, [_cpu, CPU_segfault_addr]\n    .ifc \\type,read\n        mov w0, 0\n    .else\n        mov w0, 1\n    .endif\n    strb w0, [_cpu, CPU_segfault_was_write]\n    ldr x0, [_ip]\n    mov eip, w0\n    sub w8, esp, 4\n    tst x0, 1 << 62\n    csel esp, esp, w8, eq\n    mov x0, INT_GPF\n    b fiber_exit\n\n.endr\n\n.global crosspage_load\ncrosspage_load:\n    save_c\n    sub x0, _tlb, TLB_entries\n    add x2, _cpu, LOCAL_value\n    mov w1, _addr\n    mov x3, x19\n    bl NAME(__tlb_read_cross_page)\n    mov x19, x0\n    restore_c\n    cbz x19, segfault_read\n    str _addr, [_cpu, LOCAL_value_addr]\n    add _xaddr, _cpu, LOCAL_value\n    ret\n\n.global crosspage_store\ncrosspage_store:\n    save_c\n    sub x0, _tlb, TLB_entries\n    add x2, _cpu, LOCAL_value\n    ldr w1, [_cpu, LOCAL_value_addr]\n    mov x3, x19\n    bl NAME(__tlb_write_cross_page)\n    mov x19, x0\n    restore_c\n    cbz x19, segfault_write\n    ret\n"
  },
  {
    "path": "asbestos/gadgets-aarch64/misc.S",
    "content": "#include \"gadgets.h\"\n#include \"math.h\"\n\n.gadget cpuid\n    # regrettable\n    save_c\n    sub sp, sp, 0x10\n    str eax, [sp, 0xc]\n    str ebx, [sp, 0x8]\n    str ecx, [sp, 0x4]\n    str edx, [sp, 0x0]\n    add x0, sp, 0xc\n    add x1, sp, 0x8\n    add x2, sp, 0x4\n    mov x3, sp\n    bl NAME(helper_cpuid)\n    ldr eax, [sp, 0xc]\n    ldr ebx, [sp, 0x8]\n    ldr ecx, [sp, 0x4]\n    ldr edx, [sp, 0x0]\n    add sp, sp, 0x10\n    restore_c\n    gret\n\n.macro do_cmpxchg size, s\n    .gadget cmpxchg\\size\\()_mem\n        write_prep \\size, cmpxchg\\size\\()_mem\n        ldr\\s w8, [_xaddr]\n        setf_a eax, w8\n        mov w9, eax\n        do_add sub, w9, w8, \\s\n        setf_zsp \\s, val=w9\n        .ifnb \\s\n            cmp w9, 0\n            and w9, eax, (-1 << \\size)\n            orr w8, w8, w9\n        .endif\n        csel eax, w8, eax, ne\n        csel w8, _tmp, w8, eq\n        cset w9, eq\n        str\\s w8, [_xaddr]\n        write_done \\size, cmpxchg\\size\\()_mem\n        gret 1\n        write_bullshit \\size, cmpxchg\\size\\()_mem\n\n    .gadget atomic_cmpxchg\\size\\()_mem\n        write_prep \\size, atomic_cmpxchg\\size\\()_mem\n        mov w12, eax\n        ldr w11, [_xaddr]\n    1:\n        mov w8, w11\n        setf_a eax, w8\n        mov w9, eax\n        do_add sub, w9, w8, \\s\n        setf_zsp \\s, val=w9\n        .ifnb \\s\n            cmp w9, 0\n            and w9, eax, (-1 << \\size)\n            orr w8, w8, w9\n        .endif\n        csel eax, w8, eax, ne\n        csel w8, _tmp, w8, eq\n        cset w9, eq\n\n        # all that setf stuff writes to memory which means instead of just using\n        # ldaxr and stlxr we now have to do *another* compare-and-exchange\n    2:\n        ldaxr\\s w10, [_xaddr]\n        cmp w10, w11\n        b.ne 3f\n        stlxr\\s w10, w8, [_xaddr]\n        cbnz w10, 2b\n\n        write_done \\size, atomic_cmpxchg\\size\\()_mem\n        gret 1\n        write_bullshit \\size, atomic_cmpxchg\\size\\()_mem\n    3:\n        dmb ish\n        mov w11, w10\n        mov eax, w12\n        b 1b\n.endm\n\n.irp size, SIZE_LIST\n    ss \\size, do_cmpxchg\n.endr\n.gadget_array cmpxchg\n.gadget_array atomic_cmpxchg\n\n.extern segfault_write\n\n.gadget atomic_cmpxchg8b\n    # Test for alignment.\n    tst _addr, 0x7\n    b.ne 3f\n\n    # cmpxchg8b via aligned exclusive 8b load\n    write_prep 64, atomic_cmpxchg8b\n\n    # load parameters: x10 = edx:eax (old value), x11 = ecx:ebx (new value)\n    mov w10, eax\n    bfi x10, xdx, 32, 32\n    mov w11, ebx\n    bfi x11, xcx, 32, 32\n\n    # run operation: load to x9, compare with x10, store x11. short circuit if comparison fails.\n1:\n    ldaxr x9, [_xaddr]\n    cmp x10, x9\n    b.ne 1f\n    stlxr w12, x11, [_xaddr]\n    cbnz w12, 1b\n1:\n    cset w12, eq\n\n    # edx:eax should always get set to the value last seen in memory (x9)\n    write_done 64, atomic_cmpxchg8b\n    ubfx xax, x9, 0, 32\n    ubfx xdx, x9, 32, 32\n\n    # set flags (but only zf)\n    ldr w8, [_cpu, CPU_flags_res]\n    ldr w9, [_cpu, CPU_eflags]\n    and w8, w8, ~ZF_RES\n    bfi w9, w12, 6, 1\n    str w8, [_cpu, CPU_flags_res]\n    str w9, [_cpu, CPU_eflags]\n    gret 1\n    write_bullshit 64, atomic_cmpxchg8b\n\n3:  # All unaligned paths\n    b segfault_write\n\n\n.gadget cmpxchg8b\n    write_prep 64, cmpxchg8b\n    mov w9, eax\n    bfi x9, xdx, 32, 32\n    mov w10, ebx\n    bfi x10, xcx, 32, 32\n\n    ldr x8, [_xaddr]\n    cmp x9, x8\n    csel x9, x8, x9, ne\n    csel x8, x10, x8, eq\n    cset w11, eq\n    str x8, [_xaddr]\n    write_done 64, cmpxchg8b\n    ubfx xax, x9, 0, 32\n    ubfx xdx, x9, 32, 32\n\n    ldr w8, [_cpu, CPU_flags_res]\n    ldr w9, [_cpu, CPU_eflags]\n    and w8, w8, ~ZF_RES\n    bfi w9, w11, 6, 1\n    str w8, [_cpu, CPU_flags_res]\n    str w9, [_cpu, CPU_eflags]\n    gret 1\n    write_bullshit 64, cmpxchg8b\n\n.macro do_helper type, size=\n    .gadget helper_\\type\\size\n        .ifin(\\type, read,write)\n            \\type\\()_prep (\\size), helper_\\type\\size\n        .endifin\n        save_regs\n        save_c\n        mov x0, _cpu\n        .ifc \\type,1\n            ldr x1, [_ip, 8]\n        .endif\n        .ifc \\type,2\n            ldr x1, [_ip, 8]\n            ldr x2, [_ip, 16]\n        .endif\n        .ifin(\\type, read,write)\n            mov x1, _xaddr\n            ldr x8, [_ip, 8]\n        .endifin\n        .ifin(\\type, 0,1,2)\n            ldr x8, [_ip]\n        .endifin\n        blr x8\n        restore_c\n        load_regs\n        .ifc \\type,write\n            write_done (\\size), helper_\\type\\size\n        .endif\n        .ifc \\type,0\n            gret 1\n        .else N .ifc \\type,2\n            gret 3\n        .else\n            gret 2\n        .endif N .endif\n        .ifc \\type,read\n            read_bullshit (\\size), helper_\\type\\size\n        .else N .ifc \\type,write\n            write_bullshit (\\size), helper_\\type\\size\n        .endif N .endif\n.endm\ndo_helper 0\ndo_helper 1\ndo_helper 2\n.irp size, SIZE_LIST,64,80\n    do_helper read, \\size\n    do_helper write, \\size\n.endr\n\n.macro do_vec_helper rm, _imm, size=\n    .gadget vec_helper_\\rm\\size\\_imm\n        .ifin(\\rm, read,write)\n            \\rm\\()_prep (\\size), vec_helper_\\rm\\size\\_imm\n        .endifin\n        save_regs\n        save_c\n        mov x0, _cpu\n\n        # the argument order should be a consistent src, dst\n        .ifc \\rm,reg\n            # src\n            ldrh w1, [_ip, 8]\n            add x1, x0, x1\n            # dst\n            ldrh w2, [_ip, 10]\n            add x2, x0, x2\n        .endif\n        .ifc \\rm,read\n            # src\n            mov x1, _xaddr\n            # dst\n            ldrh w2, [_ip, 16]\n            add x2, x0, x2\n        .endif\n        .ifc \\rm,write\n            # src\n            ldrh w1, [_ip, 16]\n            add x1, x0, x1\n            # dst\n            mov x2, _xaddr\n        .endif\n        .ifc \\rm,imm\n            # src\n            ldrh w1, [_ip, 8]\n            # dst\n            ldrh w2, [_ip, 10]\n            add x2, x0, x2\n        .endif\n\n        .ifc _imm,_imm\n            # imm for third argument\n            .ifin(\\rm, reg)\n                ldr w3, [_ip, 12]\n            .endifin\n            .ifin(\\rm, read,write)\n                ldr w3, [_ip, 20]\n            .endifin\n        .endif\n\n        .ifin(\\rm, read,write)\n            ldr x8, [_ip, 8]\n        .endifin\n        .ifin(\\rm, reg,imm)\n            ldr x8, [_ip]\n        .endifin\n        blr x8\n\n        restore_c\n        load_regs\n        .ifc \\rm,write\n            write_done (\\size), vec_helper_\\rm\\size\\_imm\n        .endif\n        .ifin(\\rm, reg,imm)\n            gret 2\n        .endifin\n        .ifin(\\rm, read,write)\n            gret 3\n        .endifin\n        .ifc \\rm,read\n            read_bullshit (\\size), vec_helper_\\rm\\size\\_imm\n        .else N .ifc \\rm,write\n            write_bullshit (\\size), vec_helper_\\rm\\size\\_imm\n        .endif N .endif\n.endm\n\n.irp _imm, ,_imm\n    .irp rm, reg,imm\n        do_vec_helper \\rm, \\_imm\n    .endr\n    .irp size, SIZE_LIST,64,128\n        do_vec_helper read, \\_imm, \\size\n        do_vec_helper write, \\_imm, \\size\n    .endr\n.endr\n\n.gadget fstsw_ax\n    ldrh w10, [_cpu, CPU_fsw]\n    movs eax, w10, h\n    gret"
  },
  {
    "path": "asbestos/gadgets-aarch64/string.S",
    "content": "#include \"gadgets.h\"\n#include \"math.h\"\n\n#define REP_LIST once,rep,repnz\n\n.gadget cld\n    ldr w8, [_cpu, CPU_eflags]\n    bic w8, w8, DF_FLAG\n    str w8, [_cpu, CPU_eflags]\n    gret\n\n.gadget std\n    ldr w8, [_cpu, CPU_eflags]\n    orr w8, w8, DF_FLAG\n    str w8, [_cpu, CPU_eflags]\n    gret\n\n# FIXME non 32 bit\n.macro do_strop op, size, rep, s=\n    # repnz is only a thing for cmps and scas\n    .ifc \\rep,repnz\n        .ifnc \\op,cmps N .ifnc \\op,scas\n            .exitm\n        .endif N .endif\n    .endif\n\n    .gadget \\op\\size\\()_\\rep\n        .ifnc \\rep,once\n            cbz ecx, 2f\n        .endif\n        # df_offset = w12\n        mov w12, (\\size/8)\n        ldr w8, [_cpu, CPU_eflags]\n        tst w8, DF_FLAG\n        cneg w12, w12, ne\n    1:\n\n        .ifc \\op,lods\n            mov _addr, esi\n            read_prep \\size, \\op\\size\\()_\\rep\n            ldrs eax, [_xaddr], \\s\n\n        .else N .ifc \\op,stos\n            mov _addr, edi\n            write_prep \\size, \\op\\size\\()_\\rep\n            str\\s eax, [_xaddr]\n            write_done \\size, \\op\\size\\()_\\rep\n\n        .else N .ifc \\op,movs\n            mov _addr, esi\n            read_prep \\size, \\op\\size\\()_\\rep\n            ldr\\s _tmp, [_xaddr]\n            mov _addr, edi\n            write_prep \\size, \\op\\size\\()_\\rep\\()2\n            str\\s _tmp, [_xaddr]\n            write_done \\size, \\op\\size\\()_\\rep\\()2\n\n        .else N .ifc \\op, scas\n            mov _addr, edi\n            read_prep \\size, \\op\\size\\()_\\rep\n            ldr\\s w8, [_xaddr]\n            mov _tmp, eax\n            setf_a src=w8, dst=_tmp\n            do_add sub, _tmp, w8, \\s\n            setf_zsp \\s\n\n        .else N .ifc \\op, cmps\n            mov _addr, esi\n            read_prep \\size, \\op\\size\\()_\\rep\n            ldr\\s _tmp, [_xaddr]\n            mov _addr, edi\n            read_prep \\size, \\op\\size\\()_\\rep\\()2\n            ldr\\s w8, [_xaddr]\n            setf_a src=w8, dst=_tmp\n            do_add sub, _tmp, w8, \\s\n            setf_zsp \\s\n        .endif N .endif N .endif N .endif N .endif\n\n        .ifin(\\op, lods,movs,cmps)\n            add esi, esi, w12\n        .endifin\n        .ifin(\\op, movs,stos,cmps,scas)\n            add edi, edi, w12\n        .endifin\n\n        .ifnc \\rep,once\n            subs ecx, ecx, 1\n            .ifin(\\op, scas,cmps)\n                .ifc \\rep,rep\n                    cbnz _tmp, 2f\n                .else N .ifc \\rep,repnz\n                    cbz _tmp, 2f\n                .endif N .endif\n            .endifin\n            cbnz ecx, 1b\n    2:\n        .endif\n        gret 1\n        .ifin(\\op, lods,movs,scas,cmps)\n            read_bullshit \\size, \\op\\size\\()_\\rep\n        .endifin\n        .ifc \\op,stos\n            write_bullshit \\size, \\op\\size\\()_\\rep\n        .endif\n        .ifc \\op,movs\n            write_bullshit \\size, \\op\\size\\()_\\rep\\()2\n        .endif\n        .ifc \\op,cmps\n            read_bullshit \\size, \\op\\size\\()_\\rep\\()2\n        .endif\n.endm\n\n.irp op, lods,stos,movs,scas,cmps\n    .irp size, 8,16,32\n        .irp rep, REP_LIST\n            .if \\size == 8\n                do_strop \\op, \\size, \\rep, b\n            .elseif \\size == 16\n                do_strop \\op, \\size, \\rep, h\n            .elseif \\size == 32\n                do_strop \\op, \\size, \\rep\n            .endif\n        .endr\n    .endr\n    .gadget_list_size \\op, REP_LIST\n.endr\n# temporary\n"
  },
  {
    "path": "asbestos/gadgets-generic.h",
    "content": "#include \"cpu-offsets.h\"\n\n#define ifin(thing, ...) _ifin(thing, __COUNTER__, __VA_ARGS__)\n#define _ifin(thing, line, ...) __ifin(thing, line, __VA_ARGS__)\n#define __ifin(thing, line, ...) irp da_op##line, __VA_ARGS__ N .ifc thing,\\da_op##line\n#define endifin endif N .endr\n\n# sync with enum reg\n#define REG_LIST reg_a,reg_c,reg_d,reg_b,reg_sp,reg_bp,reg_si,reg_di\n# sync with enum arg\n#define GADGET_LIST REG_LIST,imm,mem,addr,gs\n# sync with enum size\n#define SIZE_LIST 8,16,32\n\n# darwin/linux compatibility\n.macro .pushsection_rodata\n#if __APPLE__\n    .pushsection __DATA,__const\n#else\n    .pushsection .data.rel.ro\n#endif\n.endm\n.macro .pushsection_bullshit\n#if __APPLE__\n    .pushsection __TEXT,__text_bullshit,regular,pure_instructions\n#else\n    .pushsection .text.bullshit\n#endif\n.endm\n\n#if __APPLE__\n#define NAME(x) _##x\n#else\n#define NAME(x) x\n#endif\n.macro .global.name name\n    .global NAME(\\name)\n    NAME(\\name)\\():\n.endm\n\n.macro .type_compat type:vararg\n#if !__APPLE__\n    .type \\type\n#endif\n.endm\n\n# an array of gadgets\n.macro _gadget_array_start name\n    .pushsection_rodata\n    .type_compat \\name\\()_gadgets,@object\n    .global.name \\name\\()_gadgets\n.endm\n\n.macro gadgets type, list:vararg\n    .irp arg, \\list\n        .ifndef NAME(gadget_\\type\\()_\\arg)\n            .set NAME(gadget_\\type\\()_\\arg), 0\n        .endif\n        .quad NAME(gadget_\\type\\()_\\arg)\n    .endr\n.endm\n\n.macro .gadget_list type, list:vararg\n    _gadget_array_start \\type\n        gadgets \\type, \\list\n    .popsection\n.endm\n\n.macro .gadget_list_size type, list:vararg\n    _gadget_array_start \\type\n        # sync with enum size\n        gadgets \\type\\()8, \\list\n        gadgets \\type\\()16, \\list\n        gadgets \\type\\()32, \\list\n        gadgets \\type\\()64, \\list\n        gadgets \\type\\()80, \\list\n    .popsection\n.endm\n\n.macro .gadget_array type\n    .gadget_list_size \\type, GADGET_LIST\n.endm\n\n# jfc\n# https://github.com/llvm-mirror/llvm/blob/release_80/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp#L41\n# https://bugs.llvm.org/show_bug.cgi?id=39010#c4\n#if defined(__APPLE__) && defined(__arm64__)\n#define N %%\n#else\n#define N ;\n#endif\n\n# vim: ft=gas\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/bits.S",
    "content": "#include \"gadgets.h\"\n\n.macro do_shift type, size, s, ss\n    .irp arg, reg_c,imm\n        .gadget \\type\\size\\()_\\arg\n            .ifc \\arg,imm\n                movl %ecx, %r14d\n                movb (%_ip), %cl\n            .endif\n            testb $31, %cl\n            jz 1f\n            .ifin(\\type, rcl,rcr)\n                btw $0, CPU_cf(%_cpu)\n            .endifin\n            \\type\\()\\ss %cl, %tmp\\s\n            setf_oc\n            .ifin(\\type, shl,shr,sar)\n                setf_zsp %tmp\\s, \\ss\n                clearf_a\n            .endifin\n        1:\n            .ifc \\arg,imm\n                movl %r14d, %ecx\n            .endif\n            .ifc \\arg,imm\n                gret 1\n            .else\n                gret\n            .endif\n    .endr\n.endm\n\n.irp type, shl,shr,sar,rol,ror,rcl,rcr\n    .irp size, 8,16,32\n        ss \\size, do_shift, \\type\n    .endr\n    .gadget_array \\type\n.endr\n\n.macro do_shiftd op, arg\n    .macro x name, reg\n        .gadget \\op\\()_\\arg\\()32_\\name\n            .ifc \\arg,imm\n                .ifnc \\name,reg_c\n                    pushq %rcx\n                .else\n                    pushq %rdx\n                    xchg %ecx, %edx\n                .endif\n                movb (%_ip), %cl\n            .endif\n            testb $(32 - 1), %cl\n            jz 1f\n            .ifc \\name,reg_c\n                \\op %cl, %edx, %tmpd\n            .else\n                \\op %cl, %\\reg, %tmpd\n            .endif\n            setf_oc\n            setf_zsp %tmpd, l\n        1:\n            .ifc \\arg,imm\n                .ifnc \\name,reg_c\n                    popq %rcx\n                .else\n                    xchg %ecx, %edx\n                    popq %rdx\n                .endif\n                gret 1\n            .else\n                gret\n            .endif\n    .endm\n    .each_reg x\n    .purgem x\n    .gadget_array \\op\\()_\\arg\n.endm\n.irp op, shrd,shld\n    .irp arg, imm,cl\n        do_shiftd \\op, \\arg\n    .endr\n.endr\n\n.macro do_bt_op op, arg, size, s, ss\n    and\\ss $(\\size-1), %tmp\\s\n    \\op\\ss %tmp\\s, \\arg\n    setf_c\n.endm\n\n.macro do_bt op, size, s, ss\n    .gadget \\op\\size\\()_mem\n        mov\\ss %tmp\\s, %r14\\s\n        andl $~(\\size-1), %r14d\n        shrl $3, %r14d\n        addl %r14d, %_addr\n        read_prep \\size, \\op\\size\\()_mem\n        do_bt_op \\op, (%_addrq), \\size, \\s, \\ss\n        gret 1\n\n    .macro x name reg\n        .gadget \\op\\size\\()_\\name\n            do_bt_op \\op, %\\reg, \\size, \\s, \\ss\n            gret\n    .endm\n    .each_reg_size \\size, x\n    .purgem x\n.endm\n\n.irp op, bt,btc,bts,btr\n    .irp size, 16,32\n        ss \\size, do_bt, \\op\n    .endr\n    .gadget_array \\op\n.endr\n\n# atomic versions of the above\n\n.macro do_bt_atomic op, size, s, ss\n    .gadget atomic_\\op\\size\\()_mem\n        mov\\ss %tmp\\s, %r14\\s\n        andl $~(\\size-1), %r14d\n        shrl $3, %r14d\n        addl %r14d, %_addr\n        read_prep \\size, atomic_\\op\\size\\()_mem\n        and\\ss $(\\size-1), %tmp\\s\n        lock \\op\\ss %tmp\\s, (%_addrq)\n        setf_c\n        gret 1\n.endm\n\n.irp op, btc,bts,btr\n    .irp size, 16,32\n        ss \\size, do_bt_atomic, \\op\n    .endr\n    .gadget_array atomic_\\op\n.endr\n\n.macro x name reg\n    .gadget bswap_\\name\n        bswap %\\reg\n        gret\n.endm\n.each_reg x\n.purgem x\n.gadget_list bswap, REG_LIST\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/control.S",
    "content": "#include \"gadgets.h\"\n\n.gadget call\n    // save return address\n    leal -4(%_esp), %_addr\n    write_prep 32, call\n    movl 16(%_ip), %r14d\n    movl %r14d, (%_addrq)\n    // push stack pointer\n    subl $4, %_esp\n    // save ip-to-arguments to return cache\n    shrw $4, %r14w\n    movzwl %r14w, %r14d\n    movq %_ip, LOCAL_ret_cache(%_cpu, %r14, 8)\n    write_done 32, call // clobbers r14\n    // jump to target\n    movq 32(%_ip), %_ip\n    jmp fiber_ret_chain\n\n.gadget call_indir\n    // save return address\n    leal -4(%_esp), %_addr\n    write_prep 32, call_indir\n    movl 16(%_ip), %r14d\n    movl %r14d, (%_addrq)\n    // push stack pointer\n    subl $4, %_esp\n    // save ip-to-arguments to return cache\n    shrw $4, %r14w\n    movzwl %r14w, %r14d\n    movq %_ip, LOCAL_ret_cache(%_cpu, %r14, 8)\n    write_done 32, call_indir // clobbers r14\n    // jump to target\n    movl %_tmp, %_eip\n    jmp fiber_ret\n\n.gadget ret\n    movl %_esp, %_addr\n    // load return address and save to _tmp\n    read_prep 32, ret\n    movl (%_addrq), %tmpd\n    movl %tmpd, %r14d\n    // pop stack pointer\n    addl 8(%_ip), %_esp\n    // load saved ip in return cache\n    shrw $4, %r14w\n    movzwq %r14w, %r14\n    movq LOCAL_ret_cache(%_cpu, %r14, 8), %_ip\n    // found?\n    cmpq $0, %_ip\n    jz 2f\n    // check if we jumped to the correct CALL instruction\n    movl 16(%_ip), %r14d\n    movq 8(%_ip), %r15\n    cmpl %r14d, %tmpd\n    jnz 1f\n    // good, now do return chaining, the logic is similar to `fiber_ret_chain`\n    movq 24(%_ip), %_ip\n    btq $63, %_ip\n    jc 1f\n    leaq -FIBER_BLOCK_code(%_ip), %r15\n    movq %r15, LOCAL_last_block(%_cpu)\n    gret\n1:\n    movq %r15, LOCAL_last_block(%_cpu)\n    // fallthrough\n2:\n    movl %tmpd, %_eip\n    jmp fiber_ret\n\n.gadget jmp_indir\n    movl %_tmp, %_eip\n    jmp fiber_ret\n.gadget jmp\n    movq (%_ip), %_ip\n    jmp fiber_ret_chain\n.gadget jcxz\n    cmpl $0, %ecx\n    jne 1f\n    movq (%_ip), %_ip\n    jmp fiber_ret_chain\n1:\n    movq 8(%_ip), %_ip\n    jmp fiber_ret_chain\n\n#define COND_LIST o,c,z,cz,s,p,sxo,sxoz\n\n.macro check_res\n    cmpl DOLLAR(0), CPU_res(%_cpu)\n.endm\n.macro check_cf\n    cmpb DOLLAR(0), CPU_cf(%_cpu)\n.endm\n\n.macro res_or_flag type, resflag, flag, target\n    testl $\\resflag, CPU_flags_res(%_cpu)\n    jz 2f\n    check_res\n    j\\type \\target\n    jmp 3f\n    2:\n    testl $\\flag, CPU_eflags(%_cpu)\n    jnz \\target\n    3:\n.endm\n\n.macro do_jump cond, target\n    # please tell me if you know a better way\n    .ifc \\cond,o\n        cmpb $0, CPU_of(%_cpu)\n        jnz \\target\n    .else; .ifc \\cond,c\n        check_cf\n        jnz \\target\n    .else; .ifc \\cond,z\n        res_or_flag z, ZF_RES, ZF_FLAG, \\target\n    .else; .ifc \\cond,cz\n        check_cf\n        jnz \\target\n        res_or_flag z, ZF_RES, ZF_FLAG, \\target\n    .else; .ifc \\cond,s\n        res_or_flag s, SF_RES, SF_FLAG, \\target\n    .else; .ifc \\cond,p\n        res_or_flag p, PF_RES, PF_FLAG, \\target\n    .else; .ifc \\cond,sxo\n        check_res\n        sets %r14b\n        xorb CPU_of(%_cpu), %r14b\n        jnz \\target\n    .else; .ifc \\cond,sxoz\n        check_res\n        jz \\target\n        sets %r14b\n        xorb CPU_of(%_cpu), %r14b\n        jnz \\target\n    .endif; .endif; .endif; .endif; .endif; .endif; .endif; .endif\n.endm\n\n.irp cond, COND_LIST\n    .gadget jmp_\\cond\n        do_jump \\cond, 1f\n        movq 8(%_ip), %_ip\n        jmp fiber_ret_chain\n    1:\n        movq (%_ip), %_ip\n        jmp fiber_ret_chain\n\n    .gadget set_\\cond\n        do_jump \\cond, 1f\n        movl $0, %_tmp\n        gret\n    1:\n        movl $1, %_tmp\n        gret\n    .gadget setn_\\cond\n        do_jump \\cond, 1f\n        movl $1, %_tmp\n        gret\n    1:\n        movl $0, %_tmp\n        gret\n\n    .gadget skip_\\cond\n        do_jump \\cond, 1f\n        gret 1\n    1:\n        addq (%_ip), %_ip\n        gret 1\n    .gadget skipn_\\cond\n        do_jump \\cond, 1f\n        addq (%_ip), %_ip\n    1:\n        gret 1\n.endr\n.gadget_list jmp, COND_LIST\n.gadget_list set, COND_LIST\n.gadget_list setn, COND_LIST\n.gadget_list skip, COND_LIST\n.gadget_list skipn, COND_LIST\n\n.gadget pushf\n    save_c\n    movq %_cpu, %rdi\n    call NAME(helper_collapse_flags)\n    restore_c\n\n    sub $4, %_esp\n    movl %_esp, %_addr\n    write_prep 32, pushf\n    movl CPU_eflags(%_cpu), %tmpd\n    movl %tmpd, (%_addrq)\n    write_done 32, pushf\n    gret\n\n.gadget popf\n    movl %_esp, %_addr\n    read_prep 32, popf\n    movl (%_addrq), %tmpd\n    movl %tmpd, CPU_eflags(%_cpu)\n    add $4, %_esp\n\n    save_c\n    movq %_cpu, %rdi\n    call NAME(helper_expand_flags)\n    restore_c\n    gret\n\n.gadget sahf\n    xchgb %ah, %al\n    movb %al, CPU_eflags(%_cpu)\n    xchgb %al, %ah\n    save_c\n    movq %_cpu, %rdi\n    call NAME(helper_expand_flags)\n    restore_c\n    gret\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/entry.S",
    "content": "#include \"emu/interrupt.h\"\n#include \"gadgets.h\"\n\n.global NAME(fiber_enter)\n.type_compat NAME(fiber_enter),function\nNAME(fiber_enter):\n    push %rbp\n    push %rbx\n    push %r12\n    push %r13\n    push %r14\n    push %r15\n    # make stack 16 byte aligned\n    # this is preserved elsewhere by only doing even numbers of pushes\n    sub $0x8, %rsp\n    leaq FIBER_BLOCK_code(%rdi), %_ip\n    movq %rsi, %_cpu\n    movq %rsp, LOCAL_bp(%_cpu)\n    leaq TLB_entries(%rdx), %_tlb\n    load_regs\n    gret\n\n.global fiber_ret_chain\nfiber_ret_chain:\n    btq $63, %_ip\n    jc fiber_ret\n    mov CPU_poked_ptr(%_cpu), %r10\n    cmpb $0, (%r10)\n    jnz poke\n    leaq -FIBER_BLOCK_code(%_ip), %r10\n    mov %r10, LOCAL_last_block(%_cpu)\n    gret\n\npoke:\n    mov -FIBER_BLOCK_code+FIBER_BLOCK_addr(%_ip), %_eip\n    # fallthrough\n\n.global fiber_ret\nfiber_ret:\n    movl $-1, %_tmp\n    # fallthrough\n\n.global fiber_exit\nfiber_exit:\n    save_regs\n    movl %_eip, CPU_eip(%_cpu)\n    movq LOCAL_bp(%_cpu), %rsp\n    add $0x8, %rsp # keep stack 16 byte aligned\n    pop %r15\n    pop %r14\n    pop %r13\n    pop %r12\n    pop %rbx\n    pop %rbp\n    mov %_tmp, %eax\n    ret\n\n.gadget interrupt\n    movl (%_ip), %_tmp\n    movl 16(%_ip), %r14d\n    movl %r14d, CPU_segfault_addr(%_cpu)\n    movl 8(%_ip), %_eip\n    movb $0, CPU_segfault_was_write(%_cpu)\n    jmp fiber_exit\n\n.gadget exit\n    movl (%_ip), %_eip\n    jmp fiber_ret\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/gadgets.h",
    "content": "#include \"../gadgets-generic.h\"\n\n# register assignments\n#define _esp r8d\n#define _sp r8w\n#define _ip r9\n#define _eip r9d\n#define _tmp r10d\n#define tmp r10\n#define tmpd r10d\n#define tmpw r10w\n#define tmpb r10b\n#define _cpu r11\n#define _tlb r12\n#define _addr r13d\n#define _addrq r13\n\n.extern fiber_exit\n\n.macro .gadget name\n    .global.name gadget_\\()\\name\n.endm\n.macro gret pop=0\n    addq $((\\pop+1)*8), %_ip\n    jmp *-8(%_ip)\n.endm\n\n# memory reading and writing\n.irp type, read,write\n\n.macro \\type\\()_prep size, id\n    movl %_addr, %r14d\n    shrl $12, %r14d\n    andl $0x3ff, %r14d\n    movl %_addr, %r15d\n    shrl $22, %r15d\n    xor %r15d, %r14d\n    shll $4, %r14d\n    movl %_addr, %r15d\n    andl $0xfff, %r15d\n    cmpl $(0x1000-(\\size/8)), %r15d\n    ja crosspage_load_\\id\n    movl %_addr, %r15d\n    andl $0xfffff000, %r15d\n    .ifc \\type,read\n        cmpl TLB_ENTRY_page(%_tlb,%r14), %r15d\n    .else\n        cmpl TLB_ENTRY_page_if_writable(%_tlb,%r14), %r15d\n    .endif\n    movl %r15d, -TLB_entries+TLB_dirty_page(%_tlb)\n    jne handle_miss_\\id\n    addq TLB_ENTRY_data_minus_addr(%_tlb,%r14), %_addrq\nback_\\id :\n\n.pushsection_bullshit\nhandle_miss_\\id :\n    call handle_\\type\\()_miss\n    jmp back_\\id\ncrosspage_load_\\id :\n    movq $(\\size/8), %r14\n    call crosspage_load\n    jmp back_\\id\n.popsection\n.endm\n\n.endr\n.macro write_done size, id\n    leaq LOCAL_value(%_cpu), %r14\n    cmpq %_addrq, %r14\n    je crosspage_store_\\id\nback_write_done_\\id :\n.pushsection_bullshit\ncrosspage_store_\\id :\n    movq $(\\size/8), %r14\n    call crosspage_store\n    jmp back_write_done_\\id\n.popsection\n.endm\n\n.macro _invoke size, reg, post, macro:vararg\n    .if \\size == 32\n        \\macro reg_\\reg, e\\reg\\post\n    .else\n        \\macro reg_\\reg, \\reg\\post\n    .endif\n.endm\n.macro .each_reg_size size, macro:vararg\n    .irp reg, a,b,c,d\n        _invoke \\size, \\reg, x, \\macro\n    .endr\n    .irp reg, si,di,bp\n        _invoke \\size, \\reg, , \\macro\n    .endr\n    .if \\size == 32\n        \\macro reg_sp, _esp\n    .else\n        \\macro reg_sp, _sp\n    .endif\n.endm\n.macro .each_reg macro:vararg\n    .each_reg_size 32, \\macro\n.endm\n\n.macro ss size, macro, args:vararg\n    .ifnb \\args\n        .if \\size == 8\n            \\macro \\args, \\size, b, b\n        .elseif \\size == 16\n            \\macro \\args, \\size, w, w\n        .elseif \\size == 32\n            \\macro \\args, \\size, d, l\n        .else\n            .error \"bad size\"\n        .endif\n    .else\n        .if \\size == 8\n            \\macro \\size, b, b\n        .elseif \\size == 16\n            \\macro \\size, w, w\n        .elseif \\size == 32\n            \\macro \\size, d, l\n        .else\n            .error \"bad size\"\n        .endif\n    .endif\n.endm\n\n.macro setf_c\n    setc CPU_cf(%_cpu)\n.endm\n.macro setf_oc\n    seto CPU_of(%_cpu)\n    setf_c\n.endm\n.macro setf_a src, dst, ss\n    mov\\ss \\src, CPU_op1(%_cpu)\n    mov\\ss \\dst, CPU_op2(%_cpu)\n    orl $AF_OPS, CPU_flags_res(%_cpu)\n.endm\n.macro clearf_a\n    andl $~AF_FLAG, CPU_eflags(%_cpu)\n    andl $~AF_OPS, CPU_flags_res(%_cpu)\n.endm\n#if __APPLE__\n#define DOLLAR(x) $$x\n#else\n#define DOLLAR(x) $x\n#endif\n.macro clearf_oc\n    movl DOLLAR(0), CPU_of(%_cpu)\n    movl DOLLAR(0), CPU_cf(%_cpu)\n.endm\n.macro setf_zsp res, ss\n    .ifnc \\ss,l\n        movs\\ss\\()l \\res, %_tmp\n    .endif\n    movl %_tmp, CPU_res(%_cpu)\n    orl $(ZF_RES|SF_RES|PF_RES), CPU_flags_res(%_cpu)\n.endm\n\n.macro save_c odd\n    push %rax\n    push %rcx\n    push %rdx\n    push %rsi\n    push %rdi\n    push %r8\n    push %r9\n    push %r10\n    push %r11\n    .ifnc \\odd,odd\n        sub $8, %rsp # 16 byte alignment is so annoying\n    .endif\n.endm\n.macro restore_c odd\n    .ifnc \\odd,odd\n        add $8, %rsp\n    .endif\n    pop %r11\n    pop %r10\n    pop %r9\n    pop %r8\n    pop %rdi\n    pop %rsi\n    pop %rdx\n    pop %rcx\n    pop %rax\n.endm\n\n.macro load_regs\n    movl CPU_eax(%_cpu), %eax\n    movl CPU_ebx(%_cpu), %ebx\n    movl CPU_ecx(%_cpu), %ecx\n    movl CPU_edx(%_cpu), %edx\n    movl CPU_esi(%_cpu), %esi\n    movl CPU_edi(%_cpu), %edi\n    movl CPU_ebp(%_cpu), %ebp\n    movl CPU_esp(%_cpu), %_esp\n.endm\n\n.macro save_regs\n    movl %eax, CPU_eax(%_cpu)\n    movl %ebx, CPU_ebx(%_cpu)\n    movl %ecx, CPU_ecx(%_cpu)\n    movl %edx, CPU_edx(%_cpu)\n    movl %esi, CPU_esi(%_cpu)\n    movl %edi, CPU_edi(%_cpu)\n    movl %ebp, CPU_ebp(%_cpu)\n    movl %_esp, CPU_esp(%_cpu)\n.endm\n\n# vim: ft=gas\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/math.S",
    "content": "#include \"gadgets.h\"\n\n.gadget load32_addr\n    movl %_addr, %_tmp\n    gret\n\n.gadget load16_gs\n    movw CPU_gs(%_cpu), %r10w\n    gret\n\n.gadget store16_gs\n    movw %r10w, CPU_gs(%_cpu)\n    gret\n\n# this would have been just a few nice compact nested loops, but gas said \"nuh uh\"\n\n.macro _do_op op, arg, size, s, ss\n    .ifc \\op,load\n        mov\\ss \\arg, %tmp\\s\n        .exitm\n    .else; .ifc \\op,store\n        mov\\ss %tmp\\s, \\arg\n        .exitm\n    .endif; .endif\n\n    .ifin(\\op, add,sub,adc,sbb)\n        mov\\ss \\arg, %r14\\s\n        setf_a src=%r14\\s, dst=%tmp\\s, ss=\\ss\n    .endifin\n    .ifin(\\op, and,or,xor)\n        clearf_a\n        clearf_oc\n    .endifin\n    .ifin(\\op, adc,sbb)\n        btw $0, CPU_cf(%_cpu)\n    .endifin\n\n    \\op\\ss \\arg, %tmp\\s\n\n    .ifin(\\op, add,sub,adc,sbb,imul)\n        setf_oc\n    .endifin\n    .ifin(\\op, add,sub,adc,sbb,and,or,xor)\n        setf_zsp %tmp\\s, \\ss\n    .endifin\n    .ifin(\\op, bsf,bsr)\n        # I sure hope this isn't too hot\n        setzb %r14b\n        andb $~(1<<6), CPU_eflags(%_cpu)\n        shlb $6, %r14b\n        orb %r14b, CPU_eflags(%_cpu)\n        andl $~ZF_RES, CPU_flags_res(%_cpu)\n    .endifin\n.endm\n.macro do_op op, size, arg\n    ss \\size, _do_op, \\op, \\arg\n.endm\n\n.macro do_reg_op op, size, reg\n    .gadget \\op\\size\\()_reg_\\reg\n        .if \\size == 32\n            do_op \\op, \\size, %e\\reg\\()x\n        .elseif \\size == 16\n            do_op \\op, \\size, %\\reg\\()x\n        .elseif \\size == 8\n            do_op \\op, \\size, %\\reg\\()l\n        .endif\n        gret\n.endm\n\n.macro do_hi_op op, size, reg\n    xchg %\\reg\\()h, %\\reg\\()l\n    do_op \\op, \\size, %\\reg\\()l\n    xchg %\\reg\\()h, %\\reg\\()l\n.endm\n\n.macro do_op_size op, size\n    .ifnc \\op,store\n        .gadget \\op\\size\\()_imm\n            do_op \\op, \\size, (%_ip)\n            gret 1\n    .endif\n\n    .gadget \\op\\size\\()_mem\n        .ifc \\op,store\n            write_prep \\size, \\op\\size\\()_mem\n        .else; .ifc \\op,xchg\n            write_prep \\size, \\op\\size\\()_mem\n        .else\n            read_prep \\size, \\op\\size\\()_mem\n        .endif; .endif\n        do_op \\op, \\size, (%_addrq)\n        .ifc \\op,store\n            write_done \\size, \\op\\size\\()_mem\n        .else; .ifc \\op,xchg\n            write_done \\size, \\op\\size\\()_mem\n        .endif; .endif\n        gret 1\n\n    .irp reg, a,b,c,d\n        do_reg_op \\op, \\size, \\reg\n    .endr\n\n    .irp reg, si,di,sp,bp\n        .gadget \\op\\size\\()_reg_\\reg\n            .if \\size == 32\n                .ifnc \\reg,sp\n                    do_op \\op, \\size, %e\\reg\n                .else\n                    do_op \\op, \\size, %_esp\n                .endif\n            .elseif \\size == 16\n                .ifnc \\reg,sp\n                    do_op \\op, \\size, %\\reg\n                .else\n                    do_op \\op, \\size, %_sp\n                .endif\n            .elseif \\size == 8\n                .ifc \\reg,sp; do_hi_op \\op, \\size, a; .else\n                .ifc \\reg,bp; do_hi_op \\op, \\size, c; .else\n                .ifc \\reg,si; do_hi_op \\op, \\size, d; .else\n                .ifc \\reg,di; do_hi_op \\op, \\size, b\n                .endif; .endif; .endif; .endif\n            .endif\n            gret\n    .endr\n.endm\n\n.irp op, load,store,xchg,add,sub,adc,sbb,and,or,xor\n    .irp size, SIZE_LIST\n        do_op_size \\op, \\size\n    .endr\n    .gadget_array \\op\n.endr\n.irp op, imul,bsf,bsr\n    .irp size, 16,32\n        do_op_size \\op, \\size\n    .endr\n    .gadget_array \\op\n.endr\n\n# same as above, but only atomics\n.macro _do_op_atomic op, arg, size, s, ss\n    .ifin(\\op, and,or,xor)\n        clearf_a\n        clearf_oc\n    .endifin\n    .ifin(\\op, adc,sbb)\n        btw $0, CPU_cf(%_cpu)\n    .endifin\n\n    mov\\ss \\arg, %r15\\s\n    xchg %r15, %rax # cmpxchg uses rax as implicit operand\n    .ifc \\op,xadd\n        push %tmp\n    .endif\n1:\n    mov %rax, %r14\n    .ifc \\op,xadd\n        mov (%rsp), %tmp\n        xchg %tmp, %r14\n    .endif\n    .ifin(\\op, add,sub,adc,sbb,xadd)\n        setf_a src=%tmp\\s, dst=%r14\\s, ss=\\ss\n    .endifin\n    .ifin(\\op, inc,dec)\n        setf_a src=$1, dst=%r14\\s, ss=\\ss\n    .endifin\n\n    .ifin(\\op, add,sub,adc,sbb,and,or,xor)\n        \\op\\ss %tmp\\s, %r14\\s\n    .endifin\n    .ifc \\op,xadd\n        add\\ss %tmp\\s, %r14\\s\n    .endif\n    .ifin(\\op, inc,dec)\n        \\op\\ss %r14\\s\n    .endifin\n    lock cmpxchg\\ss %r14\\s, \\arg\n    jnz 1b\n    mov %r15, %rax\n    .ifc \\op,xadd\n        add $8, %rsp\n    .endif\n\n    .ifin(\\op, add,sub,adc,sbb,xadd)\n        setf_oc\n    .endifin\n    .ifin(\\op, inc,dec)\n        seto CPU_of(%_cpu)\n    .endifin\n    # setf_zsp is apparently broken in such a way that it can only use _tmp\n    xchg %r14, %r10\n    setf_zsp %tmp\\s, \\ss\n    xchg %r14, %r10\n.endm\n.macro do_op_atomic op, size, arg\n    ss \\size, _do_op_atomic, \\op, \\arg\n.endm\n\n.macro do_op_size_atomic op, size\n    .gadget atomic_\\op\\size\\()_mem\n        write_prep \\size, atomic_\\op\\size\\()_mem\n        do_op_atomic \\op, \\size, (%_addrq)\n        write_done \\size, atomic_\\op\\size\\()_mem\n        gret 1\n.endm\n\n.irp op, add,sub,adc,sbb,and,or,xor,inc,dec,xadd\n    .irp size, SIZE_LIST\n        do_op_size_atomic \\op, \\size\n    .endr\n    .gadget_array atomic_\\op\n.endr\n\n# unary operations (well, only one explicit operand)\n\n.irp op, inc,dec\n    .macro do_\\op size, s, ss\n        setf_a src=$1, dst=%tmp\\s, ss=\\ss\n        \\op\\()\\ss %tmp\\s\n        seto CPU_of(%_cpu)\n        setf_zsp %tmp\\s, \\ss\n    .endm\n.endr\n.macro do_sign_extend size, s, ss\n    .if \\size != 32\n        movs\\ss\\()l %tmp\\s, %tmpd\n    .endif\n.endm\n.macro do_zero_extend size, s, ss\n    .if \\size != 32\n        movz\\ss\\()l %tmp\\s, %tmpd\n    .endif\n.endm\n.macro do_div size, s, ss\n    div\\ss %tmp\\s\n.endm\n.macro do_idiv size, s, ss\n    idiv\\ss %tmp\\s\n.endm\n.macro do_mul size, s, ss\n    mul\\ss %tmp\\s\n    setf_oc\n.endm\n.macro do_imul1 size, s, ss\n    imul\\ss %tmp\\s\n    setf_oc\n.endm\n.macro do_not size, s, ss\n    not\\ss %tmp\\s\n.endm\n\n.irp op, inc,dec,sign_extend,zero_extend,div,idiv,mul,imul1,not\n    .irp size, SIZE_LIST\n        .gadget \\op\\()_\\size\n            ss \\size, do_\\op\n            gret\n    .endr\n    .gadget_list \\op, SIZE_LIST\n.endr\n\n.gadget cvt_16\n    cwd\n    gret\n.gadget cvt_32\n    cdq\n    gret\n.gadget_list cvt, SIZE_LIST\n\n.gadget cvte_16\n    cbw\n    gret\n.gadget cvte_32\n    cwde\n    gret\n.gadget_list cvte, SIZE_LIST\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/memory.S",
    "content": "#include \"gadgets.h\"\n#include \"emu/interrupt.h\"\n\n.gadget push\n    leal -4(%_esp), %_addr\n    write_prep 32, push\n    movl %_tmp, (%_addrq)\n    write_done 32, push\n    sub $4, %_esp\n    gret 1\n.gadget pop\n    movl %_esp, %_addr\n    read_prep 32, pop\n    movl (%_addrq), %_tmp\n    add $4, %_esp\n    gret 1\n\n.macro x name, reg\n    .gadget addr_\\name\n        movl %\\reg, %_addr\n        addl (%_ip), %_addr\n        gret 1\n.endm\n.each_reg x\n.purgem x\n.gadget addr_none\n    movl (%_ip), %_addr\n    gret 1\n.gadget_list addr, REG_LIST\n\n.macro x name, reg\n    .irp times, 1,2,4,8\n        .gadget si_\\name\\()_\\times\n            .ifnc \\reg,esp\n                leal (%_addr,%\\reg,\\times), %_addr\n            .else\n                leal (%_addr,%_esp,\\times), %_addr\n            .endif\n            gret\n    .endr\n.endm\n.each_reg x\n.purgem x\n\n.pushsection_rodata\n.global.name si_gadgets\n.irp reg, REG_LIST\n    .irp times, 1,2,4,8\n        .quad NAME(gadget_si_\\reg\\()_\\times)\n    .endr\n.endr\n.popsection\n\n.gadget seg_gs\n    addl CPU_tls_ptr(%_cpu), %_addr\n    gret\n\n.irp type, read,write\n\n.global handle_\\type\\()_miss\nhandle_\\type\\()_miss:\n    subq $8, %rsp\n    save_c\n    # %tlb actually points to tlb->entries\n    leaq -TLB_entries(%_tlb), %rdi\n    movl %_addr, %esi\n    .ifc \\type,read\n        movq $0, %rdx\n    .else\n        movq $1, %rdx\n    .endif\n    call NAME(tlb_handle_miss)\n    movq %rax, %r15\n    restore_c\n    testq %r15, %r15\n    jz segfault_\\type\n    movq %r15, %_addrq\n    addq $8, %rsp\n    ret\n\nsegfault_\\type:\n    movl -TLB_entries+TLB_segfault_addr(%_tlb), %_addr\n    movl %_addr, CPU_segfault_addr(%_cpu)\n    .ifc \\type,read\n        movb $0, CPU_segfault_was_write(%_cpu)\n    .else\n        movb $1, CPU_segfault_was_write(%_cpu)\n    .endif\n    movq (%_ip), %_ip\n    leal -4(%_esp), %r14d\n    btq $62, %_ip\n    cmovcl %r14d, %_esp\n    movl %_eip, %_eip\n    movl $INT_GPF, %_tmp\n    jmp fiber_exit\n\n.endr\n\n.global crosspage_load\ncrosspage_load:\n    save_c odd\n    leaq -TLB_entries(%_tlb), %rdi\n    movl %_addr, %esi\n    leaq LOCAL_value(%_cpu), %rdx\n    movq %r14, %rcx\n    call NAME(__tlb_read_cross_page)\n    movq %rax, %r14\n    restore_c odd\n    testq %r14, %r14\n    jz segfault_read\n    movl %_addr, LOCAL_value_addr(%_cpu)\n    leaq LOCAL_value(%_cpu), %_addrq\n    ret\n\n.global crosspage_store\ncrosspage_store:\n    save_c odd\n    leaq -TLB_entries(%_tlb), %rdi\n    movl LOCAL_value_addr(%_cpu), %esi\n    leaq LOCAL_value(%_cpu), %rdx\n    movq %r14, %rcx\n    call NAME(__tlb_write_cross_page)\n    movq %rax, %r14\n    restore_c odd\n    testq %r14, %r14\n    jz segfault_write\n    ret\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/misc.S",
    "content": "#include \"gadgets.h\"\n\n.gadget cpuid\n    # regrettable\n    push %rsi\n    push %rdi\n    push %r8\n    push %r9\n    push %r10\n    push %r11\n    subq $0x10, %rsp\n    movl %eax, 0xc(%rsp)\n    movl %ebx, 0x8(%rsp)\n    movl %ecx, 0x4(%rsp)\n    movl %edx, 0x0(%rsp)\n    leaq 0xc(%rsp), %rdi\n    leaq 0x8(%rsp), %rsi\n    leaq 0x4(%rsp), %rdx\n    leaq 0x0(%rsp), %rcx\n    call NAME(helper_cpuid)\n    movl 0xc(%rsp), %eax\n    movl 0x8(%rsp), %ebx\n    movl 0x4(%rsp), %ecx\n    movl 0x0(%rsp), %edx\n    addq $0x10, %rsp\n    pop %r11\n    pop %r10\n    pop %r9\n    pop %r8\n    pop %rdi\n    pop %rsi\n    gret\n\n.macro cmpxchg_set_flags\n    setf_oc\n    # god help us\n    setp %r10b\n    seta %r13b\n    setz %r14b\n    sets %r15b\n    shlb DOLLAR(2), %r10b\n    shlb DOLLAR(4), %r13b\n    shlb DOLLAR(6), %r14b\n    shlb DOLLAR(7), %r15b\n    orb %r10b, %r15b\n    orb %r13b, %r15b\n    orb %r14b, %r15b\n    andl $~(PF_RES|ZF_RES|SF_RES|AF_OPS), CPU_flags_res(%_cpu)\n    movb %r15b, CPU_eflags(%_cpu)\n.endm\n\n.macro do_cmpxchg size, s, ss\n    .gadget cmpxchg\\size\\()_mem\n        write_prep \\size, cmpxchg\\size\\()_mem\n        cmpxchg\\ss %tmp\\s, (%_addrq)\n        pushf\n        write_done \\size, cmpxchg\\size\\()_mem\n        popf\n        cmpxchg_set_flags\n        gret 1\n\n    .gadget atomic_cmpxchg\\size\\()_mem\n        write_prep \\size, atomic_cmpxchg\\size\\()_mem\n        lock cmpxchg\\ss %tmp\\s, (%_addrq)\n        pushf\n        write_done \\size, atomic_cmpxchg\\size\\()_mem\n        popf\n        cmpxchg_set_flags\n        gret 1\n.endm\n\n.irp size, SIZE_LIST\n    ss \\size, do_cmpxchg\n.endr\n.gadget_array cmpxchg\n.gadget_array atomic_cmpxchg\n\n.gadget atomic_cmpxchg8b\n    write_prep 64, atomic_cmpxchg8b\n    lock cmpxchg8b (%_addrq)\n    setz %r15b\n    write_done 64, atomic_cmpxchg8b\n    andl $~ZF_RES, CPU_flags_res(%_cpu)\n    andl $~ZF_FLAG, CPU_eflags(%_cpu)\n    shlb $6, %r15b\n    orb %r15b, CPU_eflags(%_cpu)\n    gret 1\n\n.gadget cmpxchg8b\n    write_prep 64, cmpxchg8b\n    cmpxchg8b (%_addrq)\n    setz %r15b\n    write_done 64, cmpxchg8b\n    andl $~ZF_RES, CPU_flags_res(%_cpu)\n    andl $~ZF_FLAG, CPU_eflags(%_cpu)\n    shlb $6, %r15b\n    orb %r15b, CPU_eflags(%_cpu)\n    gret 1\n\n.macro do_helper type, size=\n    .gadget helper_\\type\\size\n        .ifin(\\type, read,write)\n            \\type\\()_prep (\\size), helper_\\type\\size\n        .endifin\n        save_regs\n        save_c\n        movq %_cpu, %rdi\n        .ifc \\type,1\n            movq 8(%_ip), %rsi\n        .endif\n        .ifc \\type,2\n            movq 8(%_ip), %rsi\n            movq 16(%_ip), %rdx\n        .endif\n        .ifin(\\type, read,write)\n            movq %_addrq, %rsi\n            callq *8(%_ip)\n        .endifin\n        .ifin(\\type, 0,1,2)\n            callq *(%_ip)\n        .endifin\n        restore_c\n        load_regs\n        .ifc \\type,write\n            write_done (\\size), helper_\\type\\size\n        .endif\n        .ifc \\type,0\n            gret 1\n        .else; .ifc \\type,2\n            gret 3\n        .else\n            gret 2\n        .endif; .endif\n.endm\ndo_helper 0\ndo_helper 1\ndo_helper 2\n.irp size, SIZE_LIST,64,80\n    do_helper read, \\size\n    do_helper write, \\size\n.endr\n\n.macro do_vec_helper rm, _imm, size=\n    .gadget vec_helper_\\rm\\size\\_imm\n        .ifin(\\rm, read,write)\n            \\rm\\()_prep (\\size), vec_helper_\\rm\\size\\_imm\n        .endifin\n        save_regs\n        save_c\n        movq %_cpu, %rdi\n        xorq %r14, %r14\n\n        # the argument order should be a consistent src, dst\n        .ifc \\rm,reg\n            # src\n            movw 8(%_ip), %r14w\n            leaq (%_cpu,%r14), %rsi\n            # dst\n            movw 10(%_ip), %r14w\n            leaq (%_cpu,%r14), %rdx\n        .endif\n        .ifc \\rm,read\n            # src\n            movq %_addrq, %rsi\n            # dst\n            movw 16(%_ip), %r14w\n            leaq (%_cpu,%r14), %rdx\n        .endif\n        .ifc \\rm,write\n            # src\n            movw 16(%_ip), %r14w\n            leaq (%_cpu,%r14), %rsi\n            # dst\n            movq %_addrq, %rdx\n        .endif\n        .ifc \\rm,imm\n            # src\n            movw 8(%_ip), %si\n            # dst\n            movw 10(%_ip), %r14w\n            leaq (%_cpu,%r14), %rdx\n        .endif\n\n        .ifc _imm,_imm\n            # imm for third argument\n            .ifin(\\rm, reg)\n                movl 12(%_ip), %ecx\n            .endifin\n            .ifin(\\rm, read,write)\n                movl 20(%_ip), %ecx\n            .endifin\n        .endif\n\n        .ifin(\\rm, read,write)\n            callq *8(%_ip)\n        .endifin\n        .ifin(\\rm, reg,imm)\n            callq *(%_ip)\n        .endifin\n\n        restore_c\n        load_regs\n        .ifc \\rm,write\n            write_done (\\size), vec_helper_\\rm\\size\\_imm\n        .endif\n        .ifin(\\rm, reg,imm)\n            gret 2\n        .endifin\n        .ifin(\\rm, read,write)\n            gret 3\n        .endifin\n.endm\n\n.irp _imm, ,_imm\n    .irp rm, reg,imm\n        do_vec_helper \\rm, \\_imm\n    .endr\n    .irp size, SIZE_LIST,64,128\n        do_vec_helper read, \\_imm, \\size\n        do_vec_helper write, \\_imm, \\size\n    .endr\n.endr\n\n.gadget fstsw_ax\n    movw CPU_fsw(%_cpu), %ax\n    gret\n"
  },
  {
    "path": "asbestos/gadgets-x86_64/string.S",
    "content": "#include \"gadgets.h\"\n\n#define REP_LIST once,rep,repnz\n\n.gadget cld\n    andl $~DF_FLAG, CPU_eflags(%_cpu)\n    gret\n\n.gadget std\n    orl $DF_FLAG, CPU_eflags(%_cpu)\n    gret\n\n.macro do_strop op, size, rep, s, ss, a\n    # repnz is only a thing for cmps and scas\n    .ifc \\rep,repnz\n        .ifnc \\op,cmps; .ifnc \\op,scas\n            .exitm\n        .endif; .endif\n    .endif\n\n    .gadget \\op\\size\\()_\\rep\n        .ifnc \\rep,once\n            testl %ecx, %ecx\n            jz 2f\n    1:\n        .endif\n        movl $-(\\size/8), CPU_df_offset(%_cpu)\n        testl $DF_FLAG, CPU_eflags(%_cpu)\n        jnz 3f\n        negl CPU_df_offset(%_cpu)\n    3:\n\n        .ifc \\op,lods\n            movl %esi, %_addr\n            read_prep \\size, \\op\\size\\()_\\rep\n            mov\\ss (%_addrq), %\\a\n\n        .else; .ifc \\op,stos\n            movl %edi, %_addr\n            write_prep \\size, \\op\\size\\()_\\rep\n            mov\\ss %\\a, (%_addrq)\n            write_done \\size, \\op\\size\\()_\\rep\n\n        .else; .ifc \\op,movs\n            movl %esi, %_addr\n            read_prep \\size, \\op\\size\\()_\\rep\n            mov\\ss (%_addrq), %tmp\\s\n            movl %edi, %_addr\n            write_prep \\size, \\op\\size\\()_\\rep\\()2\n            mov\\ss %tmp\\s, (%_addrq)\n            write_done \\size, \\op\\size\\()_\\rep\n\n        .else; .ifc \\op, scas\n            mov %edi, %_addr\n            read_prep \\size, \\op\\size\\()_\\rep\n            mov\\ss (%_addrq), %r14\\s\n            setf_a src=%\\a, dst=%r14\\s, ss=\\ss\n            mov\\ss %\\a, %tmp\\s\n            sub\\ss %r14\\s, %tmp\\s\n            setf_oc\n            setf_zsp %tmp\\s, \\ss\n\n        .else; .ifc \\op, cmps\n            movl %esi, %_addr\n            read_prep \\size, \\op\\size\\()_\\rep\n            mov\\ss (%_addrq), %tmp\\s\n            mov %edi, %_addr\n            read_prep \\size, \\op\\size\\()_\\rep\\()2\n            mov\\ss (%_addrq), %r14\\s\n            setf_a src=%r14\\s, dst=%tmp\\s, ss=\\ss\n            sub\\ss %r14\\s, %tmp\\s\n            setf_oc\n            setf_zsp %tmp\\s, \\ss\n        .endif; .endif; .endif; .endif; .endif\n\n        .ifin(\\op, lods,movs,cmps)\n            addl CPU_df_offset(%_cpu), %esi\n        .endifin\n        .ifin(\\op, movs,stos,cmps,scas)\n            addl CPU_df_offset(%_cpu), %edi\n        .endifin\n\n        .ifnc \\rep,once\n            decl %ecx\n            .ifin(\\op, scas,cmps)\n                .ifc \\rep,rep\n                    test\\ss %tmp\\s, %tmp\\s\n                    jnz 2f\n                .else; .ifc \\rep,repnz\n                    test\\ss %tmp\\s, %tmp\\s\n                    jz 2f\n                .endif; .endif\n            .endifin\n            testl %ecx, %ecx\n            jnz 1b\n    2:\n        .endif\n        gret 1\n.endm\n\n.irp op, lods,stos,movs,scas,cmps\n    .irp size, 8,16,32\n        .irp rep, REP_LIST\n            .if \\size == 8\n                do_strop \\op, \\size, \\rep, b, b, al\n            .elseif \\size == 16\n                do_strop \\op, \\size, \\rep, w, w, ax\n            .elseif \\size == 32\n                do_strop \\op, \\size, \\rep, d, l, eax\n            .endif\n        .endr\n    .endr\n    .gadget_list_size \\op, REP_LIST\n.endr\n# temporary\n"
  },
  {
    "path": "asbestos/gen.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include \"asbestos/gen.h\"\n#include \"emu/modrm.h\"\n#include \"emu/cpuid.h\"\n#include \"emu/fpu.h\"\n#include \"emu/vec.h\"\n#include \"emu/interrupt.h\"\n\nstatic int gen_step32(struct gen_state *state, struct tlb *tlb);\nstatic int gen_step16(struct gen_state *state, struct tlb *tlb);\n\nint gen_step(struct gen_state *state, struct tlb *tlb) {\n    state->orig_ip = state->ip;\n    state->orig_ip_extra = 0;\n    return gen_step32(state, tlb);\n}\n\nstatic void gen(struct gen_state *state, unsigned long thing) {\n    assert(state->size <= state->capacity);\n    if (state->size >= state->capacity) {\n        state->capacity *= 2;\n        struct fiber_block *bigger_block = realloc(state->block,\n                sizeof(struct fiber_block) + state->capacity * sizeof(unsigned long));\n        if (bigger_block == NULL) {\n            die(\"out of memory while carcinizing\");\n        }\n        state->block = bigger_block;\n    }\n    assert(state->size < state->capacity);\n    state->block->code[state->size++] = thing;\n}\n\nvoid gen_start(addr_t addr, struct gen_state *state) {\n    state->capacity = FIBER_BLOCK_INITIAL_CAPACITY;\n    state->size = 0;\n    state->ip = addr;\n    for (int i = 0; i <= 1; i++) {\n        state->jump_ip[i] = 0;\n    }\n    state->block_patch_ip = 0;\n\n    struct fiber_block *block = malloc(sizeof(struct fiber_block) + state->capacity * sizeof(unsigned long));\n    state->block = block;\n    block->addr = addr;\n}\n\nvoid gen_end(struct gen_state *state) {\n    struct fiber_block *block = state->block;\n    for (int i = 0; i <= 1; i++) {\n        if (state->jump_ip[i] != 0) {\n            block->jump_ip[i] = &block->code[state->jump_ip[i]];\n            block->old_jump_ip[i] = *block->jump_ip[i];\n        } else {\n            block->jump_ip[i] = NULL;\n        }\n\n        list_init(&block->jumps_from[i]);\n        list_init(&block->jumps_from_links[i]);\n    }\n    if (state->block_patch_ip != 0) {\n        block->code[state->block_patch_ip] = (unsigned long) block;\n    }\n    if (block->addr != state->ip)\n        block->end_addr = state->ip - 1;\n    else\n        block->end_addr = block->addr;\n    list_init(&block->chain);\n    block->is_jetsam = false;\n    for (int i = 0; i <= 1; i++) {\n        list_init(&block->page[i]);\n    }\n}\n\nvoid gen_exit(struct gen_state *state) {\n    extern void gadget_exit(void);\n    // in case the last instruction didn't end the block\n    gen(state, (unsigned long) gadget_exit);\n    gen(state, state->ip);\n}\n\n#define DECLARE_LOCALS \\\n    dword_t addr_offset = 0; \\\n    bool end_block = false; \\\n    bool seg_gs = false\n\n#define FINISH \\\n    return !end_block\n\n#define RESTORE_IP state->ip = state->orig_ip\n#define _READIMM(name, size) do {\\\n    state->ip += size/8; \\\n    if (!tlb_read(tlb, state->ip - size/8, &name, size/8)) SEGFAULT; \\\n} while (0)\n\n#define READMODRM if (!modrm_decode32(&state->ip, tlb, &modrm)) SEGFAULT\n#define READADDR _READIMM(addr_offset, 32)\n#define SEG_GS() seg_gs = true\n\n// This should stay in sync with the definition of .gadget_array in gadgets.h\nenum arg {\n    arg_reg_a, arg_reg_c, arg_reg_d, arg_reg_b, arg_reg_sp, arg_reg_bp, arg_reg_si, arg_reg_di,\n    arg_imm, arg_mem, arg_addr, arg_gs,\n    arg_count, arg_invalid,\n    // the following should not be synced with the list mentioned above (no gadgets implement them)\n    arg_modrm_val, arg_modrm_reg,\n    arg_xmm_modrm_val, arg_xmm_modrm_reg,\n    arg_mm_modrm_val, arg_mm_modrm_reg,\n    arg_mem_addr, arg_1,\n};\n\nenum size {\n    size_8, size_16, size_32,\n    size_count,\n    size_64, size_80, size_128, // bonus sizes\n};\n\n// sync with COND_LIST in control.S\nenum cond {\n    cond_O, cond_B, cond_E, cond_BE, cond_S, cond_P, cond_L, cond_LE,\n    cond_count,\n};\n\nenum repeat {\n    rep_once, rep_repz, rep_repnz,\n    rep_count,\n    rep_rep = rep_repz,\n};\n\ntypedef void (*gadget_t)(void);\n\n#define GEN(thing) gen(state, (unsigned long) (thing))\n#define g(g) do { extern void gadget_##g(void); GEN(gadget_##g); } while (0)\n#define gg(_g, a) do { g(_g); GEN(a); } while (0)\n#define ggg(_g, a, b) do { g(_g); GEN(a); GEN(b); } while (0)\n#define gggg(_g, a, b, c) do { g(_g); GEN(a); GEN(b); GEN(c); } while (0)\n#define ggggg(_g, a, b, c, d) do { g(_g); GEN(a); GEN(b); GEN(c); GEN(d); } while (0)\n#define gggggg(_g, a, b, c, d, e) do { g(_g); GEN(a); GEN(b); GEN(c); GEN(d); GEN(e); } while (0)\n#define ga(g, i) do { extern gadget_t g##_gadgets[]; if (g##_gadgets[i] == NULL) UNDEFINED; GEN(g##_gadgets[i]); } while (0)\n#define gag(g, i, a) do { ga(g, i); GEN(a); } while (0)\n#define gagg(g, i, a, b) do { ga(g, i); GEN(a); GEN(b); } while (0)\n#define gz(g, z) ga(g, sz(z))\n#define h(h) gg(helper_0, h)\n#define hh(h, a) ggg(helper_1, h, a)\n#define hhh(h, a, b) gggg(helper_2, h, a, b)\n#define h_read(h, z) do { g_addr(); ggg(helper_read##z, state->orig_ip, h##z); } while (0)\n#define h_write(h, z) do { g_addr(); ggg(helper_write##z, state->orig_ip, h##z); } while (0)\n#define UNDEFINED do { gggg(interrupt, INT_UNDEFINED, state->orig_ip, state->orig_ip); return false; } while (0)\n#define SEGFAULT do { gggg(interrupt, INT_GPF, state->orig_ip, tlb->segfault_addr); return false; } while (0)\n\nstatic inline int sz(int size) {\n    switch (size) {\n        case 8: return size_8;\n        case 16: return size_16;\n        case 32: return size_32;\n        default: return -1;\n    }\n}\n\nbool gen_addr(struct gen_state *state, struct modrm *modrm, bool seg_gs) {\n    if (modrm->base == reg_none)\n        gg(addr_none, modrm->offset);\n    else\n        gag(addr, modrm->base, modrm->offset);\n    if (modrm->type == modrm_mem_si)\n        ga(si, modrm->index * 4 + modrm->shift);\n    if (seg_gs)\n        g(seg_gs);\n    return true;\n}\n#define g_addr() gen_addr(state, &modrm, seg_gs)\n\n// this really wants to use all the locals of the decoder, which we can do\n// really nicely in gcc using nested functions, but that won't work in clang,\n// so we explicitly pass 500 arguments. sorry for the mess\nstatic inline bool gen_op(struct gen_state *state, gadget_t *gadgets, enum arg arg, struct modrm *modrm, uint64_t *imm, int size, bool seg_gs, dword_t addr_offset) {\n    size = sz(size);\n    gadgets = gadgets + size * arg_count;\n\n    switch (arg) {\n        case arg_modrm_reg:\n            // TODO find some way to assert that this won't overflow?\n            arg = modrm->reg + arg_reg_a; break;\n        case arg_modrm_val:\n            if (modrm->type == modrm_reg)\n                arg = modrm->base + arg_reg_a;\n            else\n                arg = arg_mem;\n            break;\n        case arg_mem_addr:\n            arg = arg_mem;\n            modrm->type = modrm_mem;\n            modrm->base = reg_none;\n            modrm->offset = addr_offset;\n            break;\n        case arg_1:\n            arg = arg_imm;\n            *imm = 1;\n            break;\n    }\n    if (arg >= arg_count || gadgets[arg] == NULL) {\n        UNDEFINED;\n    }\n    if (arg == arg_mem || arg == arg_addr) {\n        if (!gen_addr(state, modrm, seg_gs))\n            return false;\n    }\n    GEN(gadgets[arg]);\n    if (arg == arg_imm)\n        GEN(*imm);\n    else if (arg == arg_mem)\n        GEN(state->orig_ip | state->orig_ip_extra);\n    return true;\n}\n#define op(type, thing, z) do { \\\n    extern gadget_t type##_gadgets[]; \\\n    if (!gen_op(state, type##_gadgets, arg_##thing, &modrm, &imm, z, seg_gs, addr_offset)) return false; \\\n} while (0)\n\n#define load(thing, z) op(load, thing, z)\n#define store(thing, z) op(store, thing, z)\n// load-op-store\n#define los(o, src, dst, z) load(dst, z); op(o, src, z); store(dst, z)\n#define lo(o, src, dst, z) load(dst, z); op(o, src, z)\n\n#define MOV(src, dst,z) load(src, z); store(dst, z)\n#define MOVZX(src, dst,zs,zd) load(src, zs); gz(zero_extend, zs); store(dst, zd)\n#define MOVSX(src, dst,zs,zd) load(src, zs); gz(sign_extend, zs); store(dst, zd)\n// xchg must generate in this order to be atomic\n#define XCHG(src, dst,z) load(src, z); op(xchg, dst, z); store(src, z)\n\n#define ADD(src, dst,z) los(add, src, dst, z)\n#define OR(src, dst,z) los(or, src, dst, z)\n#define ADC(src, dst,z) los(adc, src, dst, z)\n#define SBB(src, dst,z) los(sbb, src, dst, z)\n#define AND(src, dst,z) los(and, src, dst, z)\n#define SUB(src, dst,z) los(sub, src, dst, z)\n#define XOR(src, dst,z) los(xor, src, dst, z)\n#define CMP(src, dst,z) lo(sub, src, dst, z)\n#define TEST(src, dst,z) lo(and, src, dst, z)\n#define NOT(val,z) load(val,z); gz(not, z); store(val,z)\n#define NEG(val,z) imm = 0; load(imm,z); op(sub, val,z); store(val,z)\n\n#define POP(thing,z) \\\n    gg(pop, state->orig_ip); \\\n    state->orig_ip_extra = 1ul << 62; /* marks that on segfault the stack pointer should be adjusted */\\\n    store(thing, z)\n#define PUSH(thing,z) load(thing, z); gg(push, state->orig_ip)\n\n#define INC(val,z) load(val, z); gz(inc, z); store(val, z)\n#define DEC(val,z) load(val, z); gz(dec, z); store(val, z)\n\n#define fake_ip (state->ip | (1ul << 63))\n\n#define jump_ips(off1, off2) \\\n    state->jump_ip[0] = state->size + off1; \\\n    if (off2 != 0) \\\n        state->jump_ip[1] = state->size + off2\n#define JMP(loc) load(loc, OP_SIZE); g(jmp_indir); end_block = true\n#define JMP_REL(off) gg(jmp, fake_ip + off); jump_ips(-1, 0); end_block = true\n#define JCXZ_REL(off) ggg(jcxz, fake_ip + off, fake_ip); jump_ips(-2, -1); end_block = true\n#define jcc(cc, to, else) gagg(jmp, cond_##cc, to, else); jump_ips(-2, -1); end_block = true\n#define J_REL(cc, off)  jcc(cc, fake_ip + off, fake_ip)\n#define JN_REL(cc, off) jcc(cc, fake_ip, fake_ip + off)\n\n// state->orig_ip: for use with page fault handler;\n// -1: will be patched to block address in gen_end();\n// fake_ip: the first one is the return address, used for saving to stack and verifying the cached ip in return cache is correct;\n// fake_ip: the second one is the return target, patchable by return chaining.\n#define CALL(loc) do { \\\n    load(loc, OP_SIZE); \\\n    ggggg(call_indir, state->orig_ip, -1, fake_ip, fake_ip); \\\n    state->block_patch_ip = state->size - 3; \\\n    jump_ips(-1, 0); \\\n    end_block = true; \\\n} while (0)\n// the first four arguments are the same with CALL,\n// the last one is the call target, patchable by return chaining.\n#define CALL_REL(off) do { \\\n    gggggg(call, state->orig_ip, -1, fake_ip, fake_ip, fake_ip + off); \\\n    state->block_patch_ip = state->size - 4; \\\n    jump_ips(-2, -1); \\\n    end_block = true; \\\n} while (0)\n#define RET_NEAR(imm) ggg(ret, state->orig_ip, 4 + imm); end_block = true\n#define INT(code) gggg(interrupt, (uint8_t) code, state->ip, 0); end_block = true\n\n#define SET(cc, dst) ga(set, cond_##cc); store(dst, 8)\n#define SETN(cc, dst) ga(setn, cond_##cc); store(dst, 8)\n// wins the prize for the most annoying instruction to generate\n#define CMOV(cc, src, dst,z) do { \\\n    gag(skipn, cond_##cc, 0); \\\n    int start = state->size; \\\n    load(src, z); store(dst, z); \\\n    state->block->code[start - 1] = (state->size - start) * sizeof(long); \\\n} while (0)\n#define CMOVN(cc, src, dst,z) do { \\\n    gag(skip, cond_##cc, 0); \\\n    int start = state->size; \\\n    load(src, z); store(dst, z); \\\n    state->block->code[start - 1] = (state->size - start) * sizeof(long); \\\n} while (0)\n\n#define PUSHF() g(pushf)\n#define POPF() g(popf)\n#define SAHF g(sahf)\n#define CLD g(cld)\n#define STD g(std)\n\n#define MUL18(val,z) MUL1(val,z)\n#define MUL1(val,z) load(val, z); gz(mul, z)\n#define IMUL1(val,z) load(val, z); gz(imul1, z)\n#define DIV(val, z) load(val, z); gz(div, z)\n#define IDIV(val, z) load(val, z); gz(idiv, z)\n#define IMUL3(times, src, dst,z) load(src, z); op(imul, times, z); store(dst, z)\n#define IMUL2(val, reg,z) IMUL3(val, reg, reg, z)\n\n#define CVT ga(cvt, sz(oz))\n#define CVTE ga(cvte, sz(oz))\n\n#define ROL(count, val,z) los(rol, count, val, z)\n#define ROR(count, val,z) los(ror, count, val, z)\n#define RCL(count, val,z) los(rcl, count, val, z)\n#define RCR(count, val,z) los(rcr, count, val, z)\n#define SHL(count, val,z) los(shl, count, val, z)\n#define SHR(count, val,z) los(shr, count, val, z)\n#define SAR(count, val,z) los(sar, count, val, z)\n\n#define SHLD(count, extra, dst,z) \\\n    load(dst,z); \\\n    if (arg_##count == arg_reg_c) op(shld_cl, extra,z); \\\n    else { op(shld_imm, extra,z); GEN(imm); } \\\n    store(dst,z)\n#define SHRD(count, extra, dst,z) \\\n    load(dst,z); \\\n    if (arg_##count == arg_reg_c) op(shrd_cl, extra,z); \\\n    else { op(shrd_imm, extra,z); GEN(imm); } \\\n    store(dst,z)\n\n#define BT(bit, val,z) lo(bt, val, bit, z)\n#define BTC(bit, val,z) lo(btc, val, bit, z)\n#define BTS(bit, val,z) lo(bts, val, bit, z)\n#define BTR(bit, val,z) lo(btr, val, bit, z)\n#define BSF(src, dst,z) los(bsf, src, dst, z)\n#define BSR(src, dst,z) los(bsr, src, dst, z)\n\n#define BSWAP(dst) ga(bswap, arg_##dst)\n\n#define strop(op, rep, z) gag(op, sz(z) * size_count + rep_##rep, state->orig_ip)\n#define STR(op, z) strop(op, once, z)\n#define REP(op, z) strop(op, rep, z)\n#define REPZ(op, z) strop(op, repz, z)\n#define REPNZ(op, z) strop(op, repnz, z)\n\n#define CMPXCHG(src, dst,z) load(src, z); op(cmpxchg, dst, z)\n#define CMPXCHG8B(dst,z) g_addr(); gg(cmpxchg8b, state->orig_ip)\n#define XADD(src, dst,z) XCHG(src, dst,z); ADD(src, dst,z)\n\nvoid helper_rdtsc(struct cpu_state *cpu);\n#define RDTSC h(helper_rdtsc)\n#define CPUID() g(cpuid)\n\n// atomic\n#define atomic_op(type, src, dst,z) load(src, z); op(atomic_##type, dst, z)\n#define ATOMIC_ADD(src, dst,z) atomic_op(add, src, dst, z)\n#define ATOMIC_OR(src, dst,z) atomic_op(or, src, dst, z)\n#define ATOMIC_ADC(src, dst,z) atomic_op(adc, src, dst, z)\n#define ATOMIC_SBB(src, dst,z) atomic_op(sbb, src, dst, z)\n#define ATOMIC_AND(src, dst,z) atomic_op(and, src, dst, z)\n#define ATOMIC_SUB(src, dst,z) atomic_op(sub, src, dst, z)\n#define ATOMIC_XOR(src, dst,z) atomic_op(xor, src, dst, z)\n#define ATOMIC_INC(val,z) op(atomic_inc, val, z)\n#define ATOMIC_DEC(val,z) op(atomic_dec, val, z)\n#define ATOMIC_CMPXCHG(src, dst,z) atomic_op(cmpxchg, src, dst, z)\n#define ATOMIC_XADD(src, dst,z) load(src, z); op(atomic_xadd, dst, z); store(src, z)\n#define ATOMIC_BTC(bit, val,z) lo(atomic_btc, val, bit, z)\n#define ATOMIC_BTS(bit, val,z) lo(atomic_bts, val, bit, z)\n#define ATOMIC_BTR(bit, val,z) lo(atomic_btr, val, bit, z)\n#define ATOMIC_CMPXCHG8B(dst,z) g_addr(); gg(atomic_cmpxchg8b, state->orig_ip)\n\n// fpu\n#define st_0 0\n#define st_i modrm.rm_opcode\n#define FLD() hh(fpu_ld, st_i);\n#define FILD(val,z) h_read(fpu_ild, z)\n#define FLDM(val,z) h_read(fpu_ldm, z)\n#define FSTM(dst,z) h_write(fpu_stm, z)\n#define FIST(dst,z) h_write(fpu_ist, z)\n#define FXCH() hh(fpu_xch, st_i)\n#define FCOM() hh(fpu_com, st_i)\n#define FCOMM(val,z) h_read(fpu_comm, z)\n#define FICOM(val,z) h_read(fpu_icom, z)\n#define FUCOM() hh(fpu_ucom, st_i)\n#define FUCOMI() hh(fpu_ucomi, st_i)\n#define FCOMI() hh(fpu_comi, st_i)\n#define FTST() h(fpu_tst)\n#define FXAM() h(fpu_xam)\n#define FST() hh(fpu_st, st_i)\n#define FCHS() h(fpu_chs)\n#define FABS() h(fpu_abs)\n#define FLDC(what) hh(fpu_ldc, fconst_##what)\n#define FPREM() h(fpu_prem)\n#define FRNDINT() h(fpu_rndint)\n#define FSCALE() h(fpu_scale)\n#define FSQRT() h(fpu_sqrt)\n#define FYL2X() h(fpu_yl2x)\n#define F2XM1() h(fpu_2xm1)\n#define FSTSW(dst) if (arg_##dst == arg_reg_a) g(fstsw_ax); else UNDEFINED\n#define FSTCW(dst) if (arg_##dst == arg_reg_a) UNDEFINED; else h_write(fpu_stcw, 16)\n#define FLDCW(dst) if (arg_##dst == arg_reg_a) UNDEFINED; else h_read(fpu_ldcw, 16)\n#define FSTENV(val,z) h_write(fpu_stenv, z)\n#define FLDENV(val,z) h_write(fpu_ldenv, z)\n#define FSAVE(val,z) h_write(fpu_save, z)\n#define FRESTORE(val,z) h_write(fpu_restore, z)\n#define FCLEX() h(fpu_clex)\n#define FPOP h(fpu_pop)\n#define FINCSTP() h(fpu_incstp)\n#define FADD(src, dst) hhh(fpu_add, src, dst)\n#define FIADD(val,z) h_read(fpu_iadd, z)\n#define FADDM(val,z) h_read(fpu_addm, z)\n#define FSUB(src, dst) hhh(fpu_sub, src, dst)\n#define FSUBM(val,z) h_read(fpu_subm, z)\n#define FISUB(val,z) h_read(fpu_isub, z)\n#define FISUBR(val,z) h_read(fpu_isubr, z)\n#define FSUBR(src, dst) hhh(fpu_subr, src, dst)\n#define FSUBRM(val,z) h_read(fpu_subrm, z)\n#define FMUL(src, dst) hhh(fpu_mul, src, dst)\n#define FIMUL(val,z) h_read(fpu_imul, z)\n#define FMULM(val,z) h_read(fpu_mulm, z)\n#define FDIV(src, dst) hhh(fpu_div, src, dst)\n#define FIDIV(val,z) h_read(fpu_idiv, z)\n#define FDIVM(val,z) h_read(fpu_divm, z)\n#define FDIVR(src, dst) hhh(fpu_divr, src, dst)\n#define FIDIVR(val,z) h_read(fpu_idivr, z)\n#define FDIVRM(val,z) h_read(fpu_divrm, z)\n#define FPATAN() h(fpu_patan)\n#define FSIN() h(fpu_sin)\n#define FCOS() h(fpu_cos)\n#define FXTRACT() h(fpu_xtract)\n#define FCMOVB(src) hh(fpu_cmovb, src)\n#define FCMOVE(src) hh(fpu_cmove, src)\n#define FCMOVBE(src) hh(fpu_cmovbe, src)\n#define FCMOVU(src) hh(fpu_cmovu, src)\n#define FCMOVNB(src) hh(fpu_cmovnb, src)\n#define FCMOVNE(src) hh(fpu_cmovne, src)\n#define FCMOVNBE(src) hh(fpu_cmovnbe, src)\n#define FCMOVNU(src) hh(fpu_cmovnu, src)\n\n// vector\n\nstatic inline bool could_be_memory(enum arg arg) {\n    return arg == arg_modrm_val || arg == arg_mm_modrm_val || arg == arg_xmm_modrm_val;\n}\n\nstatic inline uint16_t cpu_reg_offset(enum arg arg, int index) {\n    if (arg == arg_xmm_modrm_reg || arg == arg_xmm_modrm_val)\n        return CPU_OFFSET(xmm[index]);\n    if (arg == arg_mm_modrm_reg || arg == arg_mm_modrm_val)\n        return CPU_OFFSET(mm[index]);\n    if (arg == arg_modrm_reg || arg == arg_modrm_val)\n        return CPU_OFFSET(regs[index]);\n    return 0;\n}\n\nstatic inline bool gen_vec(enum arg src, enum arg dst, void (*helper)(), gadget_t read_mem_gadget, gadget_t write_mem_gadget, struct gen_state *state, struct modrm *modrm, uint8_t imm, bool seg_gs, bool has_imm) {\n    bool rm_is_src = !could_be_memory(dst);\n    enum arg rm = rm_is_src ? src : dst;\n    enum arg reg = rm_is_src ? dst : src;\n\n    uint16_t reg_offset = cpu_reg_offset(reg, modrm->opcode);\n    uint16_t rm_reg_offset = cpu_reg_offset(rm, modrm->rm_opcode);\n    assert(reg_offset != 0);\n\n    if (could_be_memory(rm) && modrm->type != modrm_reg)\n        rm = arg_mem;\n\n    uint64_t imm_arg = 0;\n    if (has_imm)\n        imm_arg = (uint64_t) imm << 32;\n\n    switch (rm) {\n        case arg_xmm_modrm_val:\n        case arg_mm_modrm_val:\n        case arg_modrm_val:\n            assert(rm_reg_offset != 0);\n            if (!has_imm)\n                g(vec_helper_reg);\n            else\n                g(vec_helper_reg_imm);\n            GEN(helper);\n            // first byte is src, second byte is dst\n            uint64_t arg;\n            if (rm_is_src)\n                arg = rm_reg_offset | (reg_offset << 16);\n            else\n                arg = reg_offset | (rm_reg_offset << 16);\n            GEN(arg | imm_arg);\n            break;\n\n        case arg_mem:\n            gen_addr(state, modrm, seg_gs);\n            GEN(rm_is_src ? read_mem_gadget : write_mem_gadget);\n            GEN(state->orig_ip);\n            GEN(helper);\n            GEN(reg_offset | imm_arg);\n            break;\n\n        case arg_imm:\n            // TODO: support immediates and opcode\n            g(vec_helper_imm);\n            GEN(helper);\n            // This is rm_opcode instead of opcode because PSRLQ is weird like that\n            GEN(((uint16_t) imm) | (cpu_reg_offset(reg, modrm->rm_opcode) << 16));\n            break;\n\n        default: die(\"unimplemented vecarg\");\n    }\n    return true;\n}\n\n#define has_imm_ false\n#define has_imm__imm true\n#define _v(src, dst, helper, _imm, z) do { \\\n    extern void gadget_vec_helper_read##z##_imm(void); \\\n    extern void gadget_vec_helper_write##z##_imm(void); \\\n    if (!gen_vec(src, dst, (void (*)()) helper, gadget_vec_helper_read##z##_imm, gadget_vec_helper_write##z##_imm, state, &modrm, imm, seg_gs, has_imm_##_imm)) return false; \\\n} while (0)\n#define v_(op, src, dst, _imm,z) _v(arg_##src, arg_##dst, vec_##op##z, _imm,z)\n#define v(op, src, dst,z) v_(op, src, dst,,z)\n#define v_imm(op, src, dst,z) v_(op, src, dst, _imm,z)\n\n#define vec_dst_size_modrm_val 32\n#define vec_dst_size_mm_modrm_val 64\n#define vec_dst_size_mm_modrm_reg 64\n#define vec_dst_size_xmm_modrm_val 128\n#define vec_dst_size_xmm_modrm_reg 128\n// you always want to merge when storing to memory\n// default is to never merge otherwise\n#define VMOV(src, dst, z) \\\n    if (could_be_memory(arg_##dst) && modrm.type != modrm_reg) { \\\n        v(merge, src, dst,z); \\\n    } else { \\\n        v(glue3(zero, vec_dst_size_##dst, _copy), src, dst,z); \\\n    }\n// this will additionally merge if both src and dst are registers, e.g. movss\n#define VMOV_MERGE_REG(src, dst, z) \\\n    if (modrm.type == modrm_reg || could_be_memory(arg_##dst)) { \\\n        v(merge, src, dst,z); \\\n    } else { \\\n        v(glue3(zero, vec_dst_size_##dst, _copy), src, dst,z); \\\n    }\n\n#define VCOMPARE(src, dst,z) v(compare, src, dst,z)\n#define V_OP(op, src, dst, z) v(op, src, dst, z)\n#define V_OP_IMM(op, src, dst, z) v_imm(op, src, dst, z)\n\n#define DECODER_RET static int\n#define DECODER_NAME gen_step\n#define DECODER_ARGS struct gen_state *state, struct tlb *tlb\n#define DECODER_PASS_ARGS state, tlb\n\n#define OP_SIZE 32\n#include \"emu/decode.h\"\n#undef OP_SIZE\n#define OP_SIZE 16\n#include \"emu/decode.h\"\n#undef OP_SIZE\n"
  },
  {
    "path": "asbestos/gen.h",
    "content": "#ifndef EMU_GEN_H\n#define EMU_GEN_H\n\n#include \"asbestos/asbestos.h\"\n#include \"emu/tlb.h\"\n\nstruct gen_state {\n    addr_t ip;\n    addr_t orig_ip;\n    unsigned long orig_ip_extra;\n    struct fiber_block *block;\n    unsigned size;\n    unsigned capacity;\n    unsigned jump_ip[2];\n    unsigned block_patch_ip; // for call/call_indir gadgets\n};\n\nvoid gen_start(addr_t addr, struct gen_state *state);\nvoid gen_exit(struct gen_state *state);\nvoid gen_end(struct gen_state *state);\n\nint gen_step(struct gen_state *state, struct tlb *tlb);\n\n#endif\n"
  },
  {
    "path": "asbestos/helpers.c",
    "content": "#include <time.h>\n#include \"emu/cpu.h\"\n#include \"emu/cpuid.h\"\n\nvoid helper_cpuid(dword_t *a, dword_t *b, dword_t *c, dword_t *d) {\n    do_cpuid(a, b, c, d);\n}\n\nvoid helper_rdtsc(struct cpu_state *cpu) {\n    struct timespec now;\n    clock_gettime(CLOCK_MONOTONIC, &now);\n    uint64_t tsc = now.tv_sec * 1000000000l + now.tv_nsec;\n    cpu->eax = tsc & 0xffffffff;\n    cpu->edx = tsc >> 32;\n}\n\nvoid helper_expand_flags(struct cpu_state *cpu) {\n    expand_flags(cpu);\n}\n\nvoid helper_collapse_flags(struct cpu_state *cpu) {\n    collapse_flags(cpu);\n}\n"
  },
  {
    "path": "asbestos/offsets.c",
    "content": "#include \"asbestos/asbestos.h\"\n#include \"asbestos/frame.h\"\n#include \"emu/cpu.h\"\n#include \"emu/tlb.h\"\n\nvoid cpu() {\n    OFFSET(CPU, cpu_state, eax);\n    OFFSET(CPU, cpu_state, ebx);\n    OFFSET(CPU, cpu_state, ecx);\n    OFFSET(CPU, cpu_state, edx);\n    OFFSET(CPU, cpu_state, esi);\n    OFFSET(CPU, cpu_state, edi);\n    OFFSET(CPU, cpu_state, ebp);\n    OFFSET(CPU, cpu_state, esp);\n    OFFSET(CPU, cpu_state, ax);\n    OFFSET(CPU, cpu_state, bx);\n    OFFSET(CPU, cpu_state, cx);\n    OFFSET(CPU, cpu_state, dx);\n    OFFSET(CPU, cpu_state, si);\n    OFFSET(CPU, cpu_state, di);\n    OFFSET(CPU, cpu_state, bp);\n    OFFSET(CPU, cpu_state, sp);\n    OFFSET(CPU, cpu_state, eip);\n    OFFSET(CPU, cpu_state, gs);\n    OFFSET(CPU, cpu_state, tls_ptr);\n\n    OFFSET(CPU, cpu_state, eflags);\n    OFFSET(CPU, cpu_state, of);\n    OFFSET(CPU, cpu_state, cf);\n    OFFSET(CPU, cpu_state, res);\n    OFFSET(CPU, cpu_state, op1);\n    OFFSET(CPU, cpu_state, op2);\n    OFFSET(CPU, cpu_state, flags_res);\n    OFFSET(CPU, cpu_state, df_offset);\n    OFFSET(CPU, cpu_state, fsw);\n    OFFSET(CPU, cpu_state, xmm);\n    MACRO(PF_RES);\n    MACRO(ZF_RES);\n    MACRO(SF_RES);\n    MACRO(AF_OPS);\n    MACRO(PF_FLAG);\n    MACRO(AF_FLAG);\n    MACRO(ZF_FLAG);\n    MACRO(SF_FLAG);\n    MACRO(DF_FLAG);\n\n    OFFSET(LOCAL, fiber_frame, bp);\n    OFFSET(LOCAL, fiber_frame, value);\n    OFFSET(LOCAL, fiber_frame, value_addr);\n    OFFSET(LOCAL, fiber_frame, last_block);\n    OFFSET(LOCAL, fiber_frame, ret_cache);\n    OFFSET(CPU, cpu_state, segfault_addr);\n    OFFSET(CPU, cpu_state, segfault_was_write);\n    OFFSET(CPU, cpu_state, poked_ptr);\n    MACRO(MEM_READ);\n    MACRO(MEM_WRITE);\n\n    OFFSET(FIBER_BLOCK, fiber_block, addr);\n    OFFSET(FIBER_BLOCK, fiber_block, code);\n\n    OFFSET(TLB, tlb, entries);\n    OFFSET(TLB, tlb, dirty_page);\n    OFFSET(TLB, tlb, segfault_addr);\n    OFFSET(TLB_ENTRY, tlb_entry, page);\n    OFFSET(TLB_ENTRY, tlb_entry, page_if_writable);\n    OFFSET(TLB_ENTRY, tlb_entry, data_minus_addr);\n}\n"
  },
  {
    "path": "debug.h",
    "content": "#ifndef UTIL_DEBUG_H\n#define UTIL_DEBUG_H\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid ish_printk(const char *msg, ...);\nvoid ish_vprintk(const char *msg, va_list args);\n#undef printk\n#define printk ish_printk\n\n// debug output utilities\n// save me\n\n#ifndef DEBUG_all\n#define DEBUG_all 0\n#endif\n#ifndef DEBUG_verbose\n#define DEBUG_verbose DEBUG_all\n#endif\n#ifndef DEBUG_instr\n#define DEBUG_instr DEBUG_all\n#endif\n#ifndef DEBUG_debug\n#define DEBUG_debug DEBUG_all\n#endif\n#ifndef DEBUG_strace\n#define DEBUG_strace DEBUG_all\n#endif\n#ifndef DEBUG_memory\n#define DEBUG_memory DEBUG_all\n#endif\n\n#if DEBUG_verbose\n#define TRACE_verbose TRACE__\n#else\n#define TRACE_verbose TRACE__NOP\n#endif\n#if DEBUG_instr\n#define TRACE_instr TRACE__\n#else\n#define TRACE_instr TRACE__NOP\n#endif\n#if DEBUG_debug\n#define TRACE_debug TRACE__\n#else\n#define TRACE_debug TRACE__NOP\n#endif\n#if DEBUG_strace\n#define TRACE_strace TRACE__\n#else\n#define TRACE_strace TRACE__NOP\n#endif\n#if DEBUG_memory\n#define TRACE_memory TRACE__\n#else\n#define TRACE_memory TRACE__NOP\n#endif\n\n#ifdef LOG_OVERRIDE\nextern int log_override;\n#define TRACE__NOP(msg, ...) if (log_override) { TRACE__(msg, ##__VA_ARGS__); }\n#else\n#define TRACE__NOP(msg, ...) use(__VA_ARGS__)\n#endif\n#define TRACE__(msg, ...) printk(msg, ##__VA_ARGS__)\n\n#define TRACE_(chan, msg, ...) glue(TRACE_, chan)(msg, ##__VA_ARGS__)\n#define TRACE(msg, ...) TRACE_(DEFAULT_CHANNEL, msg, ##__VA_ARGS__)\n#ifndef DEFAULT_CHANNEL\n#define DEFAULT_CHANNEL verbose\n#endif\n\n#define TODO(msg, ...) die(\"TODO: \" msg, ##__VA_ARGS__)\n#define FIXME(msg, ...) printk(\"FIXME \" msg \"\\n\", ##__VA_ARGS__)\n#define ERRNO_DIE(msg) { perror(msg); abort(); }\nextern void (*die_handler)(const char *msg);\n_Noreturn void die(const char *msg, ...);\n\n#define STRACE(msg, ...) TRACE_(strace, msg, ##__VA_ARGS__)\n\n#if defined(__i386__) || defined(__x86_64__)\n#define debugger __asm__(\"int3\")\n#else\n#include <signal.h>\n#define debugger raise(SIGTRAP)\n#endif\n\n#endif"
  },
  {
    "path": "deps/aports/sync-archive.sh",
    "content": "#!/bin/bash -ex\n# This script only works on my machine\narchive_remote=\"wasabici:alpine-archive\"\n\nfunction sync_repo() {\n    version=\"$1\"\n    path=\"$2\"\n    index_name_file=\"$3\"\n    remote_path=\"$archive_remote/$version/$path\"\n\n    rclone copy -v --transfers=32 \"alpine:$version/$path\" \"$remote_path\"\n    date=$(date +%F)\n    new_index_name=\"APKINDEX-$version-$date.tar.gz\"\n    rclone moveto \"$remote_path/APKINDEX.tar.gz\" \"$remote_path/$new_index_name\"\n    echo \"$new_index_name\" > \"$index_name_file\"\n}\n\nfunction update_repo() {\n    version=\"$1\"\n    path=\"$2\"\n    index_name_file=\"$3/$version/$path/index.txt\"\n    remote_path=\"$archive_remote/$version/$path\"\n\n    mkdir -p \"$(dirname \"$index_name_file\")\"\n    touch \"$index_name_file\"\n\n    old_index_name=\"$(cat \"$index_name_file\")\"\n    sync_repo \"$version\" \"$path\" \"$index_name_file\"\n    new_index_name=\"$(cat \"$index_name_file\")\"\n    rclone cat \"$remote_path/$new_index_name\" | tar -xzOf - -O APKINDEX | format_index > /tmp/APKINDEX.new\n    rclone cat \"$remote_path/$old_index_name\" | tar -xzOf - -O APKINDEX | format_index > /tmp/APKINDEX.old\n    if diff -u /tmp/APKINDEX.new /tmp/APKINDEX.old; then\n        echo \"nothing new\"\n        echo \"$old_index_name\" > \"$index_name_file\"\n    fi\n}\n\nfunction format_index() {\n    awk '/^P:/ {name=substr($0,3)} /^V:/ {version=substr($0,3)} /^$/ {print name, version}' | sort\n}\n\nif [[ \"$#\" -lt 2 || \"$#\" -gt 3 ]]; then\n    echo \"usage: $0 version path [index.txt]\" >&2\n    exit 1\nfi\nif [[ -n \"$3\" ]]; then\n    update_repo \"$1\" \"$2\" \"$3\"\nelse\n    sync_repo \"$1\" \"$2\" /dev/null\nfi"
  },
  {
    "path": "deps/aports/v3.14/community/x86/index.txt",
    "content": "APKINDEX-v3.14-2023-05-19.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.14/main/x86/index.txt",
    "content": "APKINDEX-v3.14-2023-11-25.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.17/community/x86/index.txt",
    "content": "APKINDEX-v3.17-2024-11-30.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.17/main/x86/index.txt",
    "content": "APKINDEX-v3.17-2026-02-14.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.18/community/x86/index.txt",
    "content": "APKINDEX-v3.18-2025-05-24.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.18/main/x86/index.txt",
    "content": "APKINDEX-v3.18-2026-02-07.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.19/community/x86/index.txt",
    "content": "APKINDEX-v3.19-2026-02-07.tar.gz\n"
  },
  {
    "path": "deps/aports/v3.19/main/x86/index.txt",
    "content": "APKINDEX-v3.19-2026-02-07.tar.gz\n"
  },
  {
    "path": "deps/clone-linux.sh",
    "content": "#!/bin/sh -e\nrepo_root=\"$(dirname $0)/..\"\nsub_path=deps/linux\nsub_repo=\"$repo_root/$sub_path\"\ngit submodule init \"$sub_path\"\ngit clone \"$(git config submodule.\"$sub_path\".url)\" --depth=1 --sparse \"$sub_repo\"\ngit -C \"$sub_repo\" sparse-checkout set --no-cone --stdin < \"$sub_repo/../linux-sparse.txt\"\ngit config --local submodule.\"$sub_path\".update checkout\ngit submodule update --depth=1 \"$sub_path\"\n"
  },
  {
    "path": "deps/config.h",
    "content": "/* config.h.  Generated from config.h.in by configure.  */\n/* config.h.in.  Generated from configure.ac by autoheader.  */\n\n/* Darwin ACL support */\n#define ARCHIVE_ACL_DARWIN 1\n\n/* FreeBSD ACL support */\n/* #undef ARCHIVE_ACL_FREEBSD */\n\n/* FreeBSD NFSv4 ACL support */\n/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */\n\n/* Linux POSIX.1e ACL support via libacl */\n/* #undef ARCHIVE_ACL_LIBACL */\n\n/* Linux NFSv4 ACL support via librichacl */\n/* #undef ARCHIVE_ACL_LIBRICHACL */\n\n/* Solaris ACL support */\n/* #undef ARCHIVE_ACL_SUNOS */\n\n/* Solaris NFSv4 ACL support */\n/* #undef ARCHIVE_ACL_SUNOS_NFS4 */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_LIBC */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_LIBMD */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */\n#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */\n\n/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */\n/* #undef ARCHIVE_CRYPTO_MD5_WIN */\n\n/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */\n\n/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */\n/* #undef ARCHIVE_CRYPTO_RMD160_LIBMD */\n\n/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */\n\n/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */\n\n/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_LIBMD */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */\n#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */\n\n/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */\n/* #undef ARCHIVE_CRYPTO_SHA1_WIN */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_LIBMD */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */\n#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */\n\n/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */\n/* #undef ARCHIVE_CRYPTO_SHA256_WIN */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */\n#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */\n\n/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */\n/* #undef ARCHIVE_CRYPTO_SHA384_WIN */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_LIBMD */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */\n#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */\n\n/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */\n/* #undef ARCHIVE_CRYPTO_SHA512_WIN */\n\n/* AIX xattr support */\n/* #undef ARCHIVE_XATTR_AIX */\n\n/* Darwin xattr support */\n#define ARCHIVE_XATTR_DARWIN 1\n\n/* FreeBSD xattr support */\n/* #undef ARCHIVE_XATTR_FREEBSD */\n\n/* Linux xattr support */\n/* #undef ARCHIVE_XATTR_LINUX */\n\n/* Version number of bsdcat */\n#define BSDCAT_VERSION_STRING \"3.4.3\"\n\n/* Version number of bsdcpio */\n#define BSDCPIO_VERSION_STRING \"3.4.3\"\n\n/* Version number of bsdtar */\n#define BSDTAR_VERSION_STRING \"3.4.3\"\n\n/* Define to 1 if the system has the type `ace_t'. */\n/* #undef HAVE_ACE_T */\n\n/* Define to 1 if you have the `acl' function. */\n/* #undef HAVE_ACL */\n\n/* Define to 1 if the system has the type `aclent_t'. */\n/* #undef HAVE_ACLENT_T */\n\n/* Define to 1 if you have the `acl_add_flag_np' function. */\n#define HAVE_ACL_ADD_FLAG_NP 1\n\n/* Define to 1 if you have the `acl_add_perm' function. */\n#define HAVE_ACL_ADD_PERM 1\n\n/* Define to 1 if you have the `acl_clear_flags_np' function. */\n#define HAVE_ACL_CLEAR_FLAGS_NP 1\n\n/* Define to 1 if you have the `acl_clear_perms' function. */\n#define HAVE_ACL_CLEAR_PERMS 1\n\n/* Define to 1 if you have the `acl_create_entry' function. */\n#define HAVE_ACL_CREATE_ENTRY 1\n\n/* Define to 1 if you have the `acl_delete_def_file' function. */\n#define HAVE_ACL_DELETE_DEF_FILE 1\n\n/* Define to 1 if the system has the type `acl_entry_t'. */\n#define HAVE_ACL_ENTRY_T 1\n\n/* Define to 1 if you have the `acl_free' function. */\n#define HAVE_ACL_FREE 1\n\n/* Define to 1 if you have the `acl_get_brand_np' function. */\n/* #undef HAVE_ACL_GET_BRAND_NP */\n\n/* Define to 1 if you have the `acl_get_entry' function. */\n#define HAVE_ACL_GET_ENTRY 1\n\n/* Define to 1 if you have the `acl_get_entry_type_np' function. */\n/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */\n\n/* Define to 1 if you have the `acl_get_fd' function. */\n#define HAVE_ACL_GET_FD 1\n\n/* Define to 1 if you have the `acl_get_fd_np' function. */\n#define HAVE_ACL_GET_FD_NP 1\n\n/* Define to 1 if you have the `acl_get_file' function. */\n#define HAVE_ACL_GET_FILE 1\n\n/* Define to 1 if you have the `acl_get_flagset_np' function. */\n#define HAVE_ACL_GET_FLAGSET_NP 1\n\n/* Define to 1 if you have the `acl_get_flag_np' function. */\n#define HAVE_ACL_GET_FLAG_NP 1\n\n/* Define to 1 if you have the `acl_get_link_np' function. */\n#define HAVE_ACL_GET_LINK_NP 1\n\n/* Define to 1 if you have the `acl_get_perm' function. */\n/* #undef HAVE_ACL_GET_PERM */\n\n/* Define to 1 if you have the `acl_get_permset' function. */\n#define HAVE_ACL_GET_PERMSET 1\n\n/* Define to 1 if you have the `acl_get_perm_np' function. */\n#define HAVE_ACL_GET_PERM_NP 1\n\n/* Define to 1 if you have the `acl_get_qualifier' function. */\n#define HAVE_ACL_GET_QUALIFIER 1\n\n/* Define to 1 if you have the `acl_get_tag_type' function. */\n#define HAVE_ACL_GET_TAG_TYPE 1\n\n/* Define to 1 if you have the `acl_init' function. */\n#define HAVE_ACL_INIT 1\n\n/* Define to 1 if you have the `acl_is_trivial_np' function. */\n/* #undef HAVE_ACL_IS_TRIVIAL_NP */\n\n/* Define to 1 if you have the <acl/libacl.h> header file. */\n/* #undef HAVE_ACL_LIBACL_H */\n\n/* Define to 1 if the system has the type `acl_permset_t'. */\n#define HAVE_ACL_PERMSET_T 1\n\n/* Define to 1 if you have the `acl_set_entry_type_np' function. */\n/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */\n\n/* Define to 1 if you have the `acl_set_fd' function. */\n#define HAVE_ACL_SET_FD 1\n\n/* Define to 1 if you have the `acl_set_fd_np' function. */\n#define HAVE_ACL_SET_FD_NP 1\n\n/* Define to 1 if you have the `acl_set_file' function. */\n#define HAVE_ACL_SET_FILE 1\n\n/* Define to 1 if you have the `acl_set_link_np' function. */\n#define HAVE_ACL_SET_LINK_NP 1\n\n/* Define to 1 if you have the `acl_set_qualifier' function. */\n#define HAVE_ACL_SET_QUALIFIER 1\n\n/* Define to 1 if you have the `acl_set_tag_type' function. */\n#define HAVE_ACL_SET_TAG_TYPE 1\n\n/* Define to 1 if the system has the type `acl_t'. */\n#define HAVE_ACL_T 1\n\n/* Define to 1 if the system has the type `acl_tag_t'. */\n#define HAVE_ACL_TAG_T 1\n\n/* Define to 1 if you have the `arc4random_buf' function. */\n#define HAVE_ARC4RANDOM_BUF 1\n\n/* Define to 1 if you have the <attr/xattr.h> header file. */\n/* #undef HAVE_ATTR_XATTR_H */\n\n/* Define to 1 if you have the <bcrypt.h> header file. */\n/* #undef HAVE_BCRYPT_H */\n\n/* Define to 1 if you have the <blake2.h> header file. */\n/* #undef HAVE_BLAKE2_H */\n\n/* Define to 1 if you have the <bzlib.h> header file. */\n#define HAVE_BZLIB_H 1\n\n/* Define to 1 if you have the `chflags' function. */\n#define HAVE_CHFLAGS 1\n\n/* Define to 1 if you have the `chown' function. */\n#define HAVE_CHOWN 1\n\n/* Define to 1 if you have the `chroot' function. */\n#define HAVE_CHROOT 1\n\n/* Define to 1 if you have the <copyfile.h> header file. */\n#define HAVE_COPYFILE_H 1\n\n/* Define to 1 if you have the `ctime_r' function. */\n#define HAVE_CTIME_R 1\n\n/* Define to 1 if you have the <ctype.h> header file. */\n#define HAVE_CTYPE_H 1\n\n/* Define to 1 if you have the `cygwin_conv_path' function. */\n/* #undef HAVE_CYGWIN_CONV_PATH */\n\n/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you\n   don't. */\n/* #undef HAVE_DECL_ACE_GETACL */\n\n/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you\n   don't. */\n/* #undef HAVE_DECL_ACE_GETACLCNT */\n\n/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you\n   don't. */\n/* #undef HAVE_DECL_ACE_SETACL */\n\n/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if\n   you don't. */\n#define HAVE_DECL_ACL_SYNCHRONIZE 1\n\n/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if\n   you don't. */\n#define HAVE_DECL_ACL_TYPE_EXTENDED 1\n\n/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you\n   don't. */\n#define HAVE_DECL_ACL_TYPE_NFS4 0\n\n/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you\n   don't. */\n#define HAVE_DECL_ACL_USER 0\n\n/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to\n   0 if you don't. */\n#define HAVE_DECL_EXTATTR_NAMESPACE_USER 0\n\n/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.\n   */\n/* #undef HAVE_DECL_GETACL */\n\n/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you\n   don't. */\n/* #undef HAVE_DECL_GETACLCNT */\n\n/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_INT32_MAX 1\n\n/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you\n   don't. */\n#define HAVE_DECL_INT32_MIN 1\n\n/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_INT64_MAX 1\n\n/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you\n   don't. */\n#define HAVE_DECL_INT64_MIN 1\n\n/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_INTMAX_MAX 1\n\n/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you\n   don't. */\n#define HAVE_DECL_INTMAX_MIN 1\n\n/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.\n   */\n/* #undef HAVE_DECL_SETACL */\n\n/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_SIZE_MAX 1\n\n/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_SSIZE_MAX 1\n\n/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRERROR_R 1\n\n/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_UINT32_MAX 1\n\n/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_UINT64_MAX 1\n\n/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you\n   don't. */\n#define HAVE_DECL_UINTMAX_MAX 1\n\n/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if\n   you don't. */\n#define HAVE_DECL_XATTR_NOFOLLOW 1\n\n/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.\n   */\n#define HAVE_DIRENT_H 1\n\n/* Define to 1 if you have a dirfd function or macro */\n#define HAVE_DIRFD 1\n\n/* Define to 1 if you have the <dlfcn.h> header file. */\n#define HAVE_DLFCN_H 1\n\n/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */\n/* #undef HAVE_DOPRNT */\n\n/* Define to 1 if nl_langinfo supports D_MD_ORDER */\n#define HAVE_D_MD_ORDER 1\n\n/* A possible errno value for invalid file format errors */\n#define HAVE_EFTYPE 1\n\n/* A possible errno value for invalid file format errors */\n#define HAVE_EILSEQ 1\n\n/* Define to 1 if you have the <errno.h> header file. */\n#define HAVE_ERRNO_H 1\n\n/* Define to 1 if you have the <expat.h> header file. */\n/* #undef HAVE_EXPAT_H */\n\n/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */\n/* #undef HAVE_EXT2FS_EXT2_FS_H */\n\n/* Define to 1 if you have the `extattr_get_fd' function. */\n/* #undef HAVE_EXTATTR_GET_FD */\n\n/* Define to 1 if you have the `extattr_get_file' function. */\n/* #undef HAVE_EXTATTR_GET_FILE */\n\n/* Define to 1 if you have the `extattr_get_link' function. */\n/* #undef HAVE_EXTATTR_GET_LINK */\n\n/* Define to 1 if you have the `extattr_list_fd' function. */\n/* #undef HAVE_EXTATTR_LIST_FD */\n\n/* Define to 1 if you have the `extattr_list_file' function. */\n/* #undef HAVE_EXTATTR_LIST_FILE */\n\n/* Define to 1 if you have the `extattr_list_link' function. */\n/* #undef HAVE_EXTATTR_LIST_LINK */\n\n/* Define to 1 if you have the `extattr_set_fd' function. */\n/* #undef HAVE_EXTATTR_SET_FD */\n\n/* Define to 1 if you have the `extattr_set_link' function. */\n/* #undef HAVE_EXTATTR_SET_LINK */\n\n/* Define to 1 if you have the `facl' function. */\n/* #undef HAVE_FACL */\n\n/* Define to 1 if you have the `fchdir' function. */\n#define HAVE_FCHDIR 1\n\n/* Define to 1 if you have the `fchflags' function. */\n#define HAVE_FCHFLAGS 1\n\n/* Define to 1 if you have the `fchmod' function. */\n#define HAVE_FCHMOD 1\n\n/* Define to 1 if you have the `fchown' function. */\n#define HAVE_FCHOWN 1\n\n/* Define to 1 if you have the `fcntl' function. */\n#define HAVE_FCNTL 1\n\n/* Define to 1 if you have the <fcntl.h> header file. */\n#define HAVE_FCNTL_H 1\n\n/* Define to 1 if you have the `fdopendir' function. */\n#define HAVE_FDOPENDIR 1\n\n/* Define to 1 if you have the `fgetea' function. */\n/* #undef HAVE_FGETEA */\n\n/* Define to 1 if you have the `fgetxattr' function. */\n#define HAVE_FGETXATTR 1\n\n/* Define to 1 if you have the `flistea' function. */\n/* #undef HAVE_FLISTEA */\n\n/* Define to 1 if you have the `flistxattr' function. */\n#define HAVE_FLISTXATTR 1\n\n/* Define to 1 if you have the `fork' function. */\n#define HAVE_FORK 1\n\n/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */\n#define HAVE_FSEEKO 1\n\n/* Define to 1 if you have the `fsetea' function. */\n/* #undef HAVE_FSETEA */\n\n/* Define to 1 if you have the `fsetxattr' function. */\n#define HAVE_FSETXATTR 1\n\n/* Define to 1 if you have the `fstat' function. */\n#define HAVE_FSTAT 1\n\n/* Define to 1 if you have the `fstatat' function. */\n#define HAVE_FSTATAT 1\n\n/* Define to 1 if you have the `fstatfs' function. */\n#define HAVE_FSTATFS 1\n\n/* Define to 1 if you have the `fstatvfs' function. */\n#define HAVE_FSTATVFS 1\n\n/* Define to 1 if you have the `ftruncate' function. */\n#define HAVE_FTRUNCATE 1\n\n/* Define to 1 if you have the `futimens' function. */\n#define HAVE_FUTIMENS 1\n\n/* Define to 1 if you have the `futimes' function. */\n#define HAVE_FUTIMES 1\n\n/* Define to 1 if you have the `futimesat' function. */\n/* #undef HAVE_FUTIMESAT */\n\n/* Define to 1 if you have the `getea' function. */\n/* #undef HAVE_GETEA */\n\n/* Define to 1 if you have the `geteuid' function. */\n#define HAVE_GETEUID 1\n\n/* Define to 1 if you have the `getgrgid_r' function. */\n#define HAVE_GETGRGID_R 1\n\n/* Define to 1 if you have the `getgrnam_r' function. */\n#define HAVE_GETGRNAM_R 1\n\n/* Define to 1 if you have the `getpid' function. */\n#define HAVE_GETPID 1\n\n/* Define to 1 if you have the `getpwnam_r' function. */\n#define HAVE_GETPWNAM_R 1\n\n/* Define to 1 if you have the `getpwuid_r' function. */\n#define HAVE_GETPWUID_R 1\n\n/* Define to 1 if you have the `getvfsbyname' function. */\n#define HAVE_GETVFSBYNAME 1\n\n/* Define to 1 if you have the `getxattr' function. */\n#define HAVE_GETXATTR 1\n\n/* Define to 1 if you have the `gmtime_r' function. */\n#define HAVE_GMTIME_R 1\n\n/* Define to 1 if you have the <grp.h> header file. */\n#define HAVE_GRP_H 1\n\n/* Define if you have the iconv() function and it works. */\n#define HAVE_ICONV 1\n\n/* Define to 1 if you have the <iconv.h> header file. */\n#define HAVE_ICONV_H 1\n\n/* Define to 1 if the system has the type `intmax_t'. */\n#define HAVE_INTMAX_T 1\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#define HAVE_INTTYPES_H 1\n\n/* Define to 1 if you have the <io.h> header file. */\n/* #undef HAVE_IO_H */\n\n/* Define to 1 if you have the <langinfo.h> header file. */\n#define HAVE_LANGINFO_H 1\n\n/* Define to 1 if you have the `lchflags' function. */\n#define HAVE_LCHFLAGS 1\n\n/* Define to 1 if you have the `lchmod' function. */\n#define HAVE_LCHMOD 1\n\n/* Define to 1 if you have the `lchown' function. */\n#define HAVE_LCHOWN 1\n\n/* Define to 1 if you have the `lgetea' function. */\n/* #undef HAVE_LGETEA */\n\n/* Define to 1 if you have the `lgetxattr' function. */\n/* #undef HAVE_LGETXATTR */\n\n/* Define to 1 if you have the `acl' library (-lacl). */\n/* #undef HAVE_LIBACL */\n\n/* Define to 1 if you have the `b2' library (-lb2). */\n/* #undef HAVE_LIBB2 */\n\n/* Define to 1 if you have the `bz2' library (-lbz2). */\n#define HAVE_LIBBZ2 1\n\n/* Define to 1 if you have the `charset' library (-lcharset). */\n/* #undef HAVE_LIBCHARSET */\n\n/* Define to 1 if you have the `crypto' library (-lcrypto). */\n/* #undef HAVE_LIBCRYPTO */\n\n/* Define to 1 if you have the `eay32' library (-leay32). */\n/* #undef HAVE_LIBEAY32 */\n\n/* Define to 1 if you have the `eay64' library (-leay64). */\n/* #undef HAVE_LIBEAY64 */\n\n/* Define to 1 if you have the `expat' library (-lexpat). */\n/* #undef HAVE_LIBEXPAT */\n\n/* Define to 1 if you have the `lz4' library (-llz4). */\n/* #undef HAVE_LIBLZ4 */\n\n/* Define to 1 if you have the `lzma' library (-llzma). */\n#define HAVE_LIBLZMA 1\n\n/* Define to 1 if you have the `lzo2' library (-llzo2). */\n/* #undef HAVE_LIBLZO2 */\n\n/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */\n/* #undef HAVE_LIBMBEDCRYPTO */\n\n/* Define to 1 if you have the `md' library (-lmd). */\n/* #undef HAVE_LIBMD */\n\n/* Define to 1 if you have the `nettle' library (-lnettle). */\n/* #undef HAVE_LIBNETTLE */\n\n/* Define to 1 if you have the `pcre' library (-lpcre). */\n/* #undef HAVE_LIBPCRE */\n\n/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */\n/* #undef HAVE_LIBPCREPOSIX */\n\n/* Define to 1 if you have the `regex' library (-lregex). */\n/* #undef HAVE_LIBREGEX */\n\n/* Define to 1 if you have the `richacl' library (-lrichacl). */\n/* #undef HAVE_LIBRICHACL */\n\n/* Define to 1 if you have the `xml2' library (-lxml2). */\n/* #undef HAVE_LIBXML2 */\n\n/* Define to 1 if you have the <libxml/xmlreader.h> header file. */\n#define HAVE_LIBXML_XMLREADER_H 1\n\n/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */\n#define HAVE_LIBXML_XMLWRITER_H 1\n\n/* Define to 1 if you have the `z' library (-lz). */\n#define HAVE_LIBZ 1\n\n/* Define to 1 if you have the `zstd' library (-lzstd). */\n/* #undef HAVE_LIBZSTD */\n\n/* Define to 1 if you have the <limits.h> header file. */\n#define HAVE_LIMITS_H 1\n\n/* Define to 1 if you have the `link' function. */\n#define HAVE_LINK 1\n\n/* Define to 1 if you have the <linux/fiemap.h> header file. */\n/* #undef HAVE_LINUX_FIEMAP_H */\n\n/* Define to 1 if you have the <linux/fs.h> header file. */\n/* #undef HAVE_LINUX_FS_H */\n\n/* Define to 1 if you have the <linux/magic.h> header file. */\n/* #undef HAVE_LINUX_MAGIC_H */\n\n/* Define to 1 if you have the <linux/types.h> header file. */\n/* #undef HAVE_LINUX_TYPES_H */\n\n/* Define to 1 if you have the `listea' function. */\n/* #undef HAVE_LISTEA */\n\n/* Define to 1 if you have the `listxattr' function. */\n#define HAVE_LISTXATTR 1\n\n/* Define to 1 if you have the `llistea' function. */\n/* #undef HAVE_LLISTEA */\n\n/* Define to 1 if you have the `llistxattr' function. */\n/* #undef HAVE_LLISTXATTR */\n\n/* Define to 1 if you have the <localcharset.h> header file. */\n/* #undef HAVE_LOCALCHARSET_H */\n\n/* Define to 1 if you have the `locale_charset' function. */\n#define HAVE_LOCALE_CHARSET 1\n\n/* Define to 1 if you have the <locale.h> header file. */\n#define HAVE_LOCALE_H 1\n\n/* Define to 1 if you have the `localtime_r' function. */\n#define HAVE_LOCALTIME_R 1\n\n/* Define to 1 if the system has the type `long long int'. */\n#define HAVE_LONG_LONG_INT 1\n\n/* Define to 1 if you have the `lsetea' function. */\n/* #undef HAVE_LSETEA */\n\n/* Define to 1 if you have the `lsetxattr' function. */\n/* #undef HAVE_LSETXATTR */\n\n/* Define to 1 if you have the `lstat' function. */\n#define HAVE_LSTAT 1\n\n/* Define to 1 if `lstat' has the bug that it succeeds when given the\n   zero-length file name argument. */\n#define HAVE_LSTAT_EMPTY_STRING_BUG 1\n\n/* Define to 1 if you have the `lutimes' function. */\n#define HAVE_LUTIMES 1\n\n/* Define to 1 if you have the <lz4hc.h> header file. */\n/* #undef HAVE_LZ4HC_H */\n\n/* Define to 1 if you have the <lz4.h> header file. */\n/* #undef HAVE_LZ4_H */\n\n/* Define to 1 if you have the <lzma.h> header file. */\n/* #undef HAVE_LZMA_H */\n\n/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */\n/* #undef HAVE_LZMA_STREAM_ENCODER_MT */\n\n/* Define to 1 if you have the <lzo/lzo1x.h> header file. */\n/* #undef HAVE_LZO_LZO1X_H */\n\n/* Define to 1 if you have the <lzo/lzoconf.h> header file. */\n/* #undef HAVE_LZO_LZOCONF_H */\n\n/* Define to 1 if you have the <mbedtls/aes.h> header file. */\n/* #undef HAVE_MBEDTLS_AES_H */\n\n/* Define to 1 if you have the <mbedtls/md.h> header file. */\n/* #undef HAVE_MBEDTLS_MD_H */\n\n/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */\n/* #undef HAVE_MBEDTLS_PKCS5_H */\n\n/* Define to 1 if you have the `mbrtowc' function. */\n#define HAVE_MBRTOWC 1\n\n/* Define to 1 if you have the `mbr_gid_to_uuid' function. */\n#define HAVE_MBR_GID_TO_UUID 1\n\n/* Define to 1 if you have the `mbr_uid_to_uuid' function. */\n#define HAVE_MBR_UID_TO_UUID 1\n\n/* Define to 1 if you have the `mbr_uuid_to_id' function. */\n#define HAVE_MBR_UUID_TO_ID 1\n\n/* Define to 1 if you have the <md5.h> header file. */\n/* #undef HAVE_MD5_H */\n\n/* Define to 1 if you have the <membership.h> header file. */\n#define HAVE_MEMBERSHIP_H 1\n\n/* Define to 1 if you have the `memmove' function. */\n#define HAVE_MEMMOVE 1\n\n/* Define to 1 if you have the <memory.h> header file. */\n#define HAVE_MEMORY_H 1\n\n/* Define to 1 if you have the `memset' function. */\n#define HAVE_MEMSET 1\n\n/* Define to 1 if you have the `mkdir' function. */\n#define HAVE_MKDIR 1\n\n/* Define to 1 if you have the `mkfifo' function. */\n#define HAVE_MKFIFO 1\n\n/* Define to 1 if you have the `mknod' function. */\n#define HAVE_MKNOD 1\n\n/* Define to 1 if you have the `mkstemp' function. */\n#define HAVE_MKSTEMP 1\n\n/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */\n/* #undef HAVE_NDIR_H */\n\n/* Define to 1 if you have the <nettle/aes.h> header file. */\n/* #undef HAVE_NETTLE_AES_H */\n\n/* Define to 1 if you have the <nettle/hmac.h> header file. */\n/* #undef HAVE_NETTLE_HMAC_H */\n\n/* Define to 1 if you have the <nettle/md5.h> header file. */\n/* #undef HAVE_NETTLE_MD5_H */\n\n/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */\n/* #undef HAVE_NETTLE_PBKDF2_H */\n\n/* Define to 1 if you have the <nettle/ripemd160.h> header file. */\n/* #undef HAVE_NETTLE_RIPEMD160_H */\n\n/* Define to 1 if you have the <nettle/sha.h> header file. */\n/* #undef HAVE_NETTLE_SHA_H */\n\n/* Define to 1 if you have the `nl_langinfo' function. */\n#define HAVE_NL_LANGINFO 1\n\n/* Define to 1 if you have the `openat' function. */\n#define HAVE_OPENAT 1\n\n/* Define to 1 if you have the <openssl/evp.h> header file. */\n/* #undef HAVE_OPENSSL_EVP_H */\n\n/* Define to 1 if you have the <paths.h> header file. */\n#define HAVE_PATHS_H 1\n\n/* Define to 1 if you have the <pcreposix.h> header file. */\n/* #undef HAVE_PCREPOSIX_H */\n\n/* Define to 1 if you have the `pipe' function. */\n#define HAVE_PIPE 1\n\n/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */\n/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */\n\n/* Define to 1 if you have the `poll' function. */\n#define HAVE_POLL 1\n\n/* Define to 1 if you have the <poll.h> header file. */\n#define HAVE_POLL_H 1\n\n/* Define to 1 if you have the `posix_spawnp' function. */\n#define HAVE_POSIX_SPAWNP 1\n\n/* Define to 1 if you have the <pthread.h> header file. */\n#define HAVE_PTHREAD_H 1\n\n/* Define to 1 if you have the <pwd.h> header file. */\n#define HAVE_PWD_H 1\n\n/* Define to 1 if you have a POSIX compatible readdir_r */\n#define HAVE_READDIR_R 1\n\n/* Define to 1 if you have the `readlink' function. */\n#define HAVE_READLINK 1\n\n/* Define to 1 if you have the `readlinkat' function. */\n#define HAVE_READLINKAT 1\n\n/* Define to 1 if you have the `readpassphrase' function. */\n#define HAVE_READPASSPHRASE 1\n\n/* Define to 1 if you have the <readpassphrase.h> header file. */\n#define HAVE_READPASSPHRASE_H 1\n\n/* Define to 1 if you have the <regex.h> header file. */\n#define HAVE_REGEX_H 1\n\n/* Define to 1 if you have the `richacl_alloc' function. */\n/* #undef HAVE_RICHACL_ALLOC */\n\n/* Define to 1 if you have the `richacl_equiv_mode' function. */\n/* #undef HAVE_RICHACL_EQUIV_MODE */\n\n/* Define to 1 if you have the `richacl_free' function. */\n/* #undef HAVE_RICHACL_FREE */\n\n/* Define to 1 if you have the `richacl_get_fd' function. */\n/* #undef HAVE_RICHACL_GET_FD */\n\n/* Define to 1 if you have the `richacl_get_file' function. */\n/* #undef HAVE_RICHACL_GET_FILE */\n\n/* Define to 1 if you have the `richacl_set_fd' function. */\n/* #undef HAVE_RICHACL_SET_FD */\n\n/* Define to 1 if you have the `richacl_set_file' function. */\n/* #undef HAVE_RICHACL_SET_FILE */\n\n/* Define to 1 if you have the <ripemd.h> header file. */\n/* #undef HAVE_RIPEMD_H */\n\n/* Define to 1 if you have the `select' function. */\n#define HAVE_SELECT 1\n\n/* Define to 1 if you have the `setenv' function. */\n#define HAVE_SETENV 1\n\n/* Define to 1 if you have the `setlocale' function. */\n#define HAVE_SETLOCALE 1\n\n/* Define to 1 if you have the `setxattr' function. */\n#define HAVE_SETXATTR 1\n\n/* Define to 1 if you have the <sha256.h> header file. */\n/* #undef HAVE_SHA256_H */\n\n/* Define to 1 if you have the <sha512.h> header file. */\n/* #undef HAVE_SHA512_H */\n\n/* Define to 1 if you have the <sha.h> header file. */\n/* #undef HAVE_SHA_H */\n\n/* Define to 1 if you have the `sigaction' function. */\n#define HAVE_SIGACTION 1\n\n/* Define to 1 if you have the <signal.h> header file. */\n#define HAVE_SIGNAL_H 1\n\n/* Define to 1 if you have the <spawn.h> header file. */\n#define HAVE_SPAWN_H 1\n\n/* Define to 1 if you have the `statfs' function. */\n#define HAVE_STATFS 1\n\n/* Define to 1 if you have the `statvfs' function. */\n#define HAVE_STATVFS 1\n\n/* Define to 1 if `stat' has the bug that it succeeds when given the\n   zero-length file name argument. */\n#define HAVE_STAT_EMPTY_STRING_BUG 1\n\n/* Define to 1 if you have the <stdarg.h> header file. */\n#define HAVE_STDARG_H 1\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#define HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#define HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the `strchr' function. */\n#define HAVE_STRCHR 1\n\n/* Define to 1 if you have the `strdup' function. */\n#define HAVE_STRDUP 1\n\n/* Define to 1 if you have the `strerror' function. */\n#define HAVE_STRERROR 1\n\n/* Define to 1 if you have the `strerror_r' function. */\n#define HAVE_STRERROR_R 1\n\n/* Define to 1 if you have the `strftime' function. */\n#define HAVE_STRFTIME 1\n\n/* Define to 1 if you have the <strings.h> header file. */\n#define HAVE_STRINGS_H 1\n\n/* Define to 1 if you have the <string.h> header file. */\n#define HAVE_STRING_H 1\n\n/* Define to 1 if you have the `strncpy_s' function. */\n/* #undef HAVE_STRNCPY_S */\n\n/* Define to 1 if you have the `strrchr' function. */\n#define HAVE_STRRCHR 1\n\n/* Define to 1 if the system has the type `struct richace'. */\n/* #undef HAVE_STRUCT_RICHACE */\n\n/* Define to 1 if the system has the type `struct richacl'. */\n/* #undef HAVE_STRUCT_RICHACL */\n\n/* Define to 1 if `f_namemax' is a member of `struct statfs'. */\n/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */\n\n/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */\n/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */\n\n/* Define to 1 if `st_birthtime' is a member of `struct stat'. */\n#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1\n\n/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */\n#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1\n\n/* Define to 1 if `st_blksize' is a member of `struct stat'. */\n#define HAVE_STRUCT_STAT_ST_BLKSIZE 1\n\n/* Define to 1 if `st_flags' is a member of `struct stat'. */\n#define HAVE_STRUCT_STAT_ST_FLAGS 1\n\n/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */\n#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1\n\n/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */\n/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */\n\n/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */\n/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */\n\n/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */\n/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */\n\n/* Define to 1 if `st_umtime' is a member of `struct stat'. */\n/* #undef HAVE_STRUCT_STAT_ST_UMTIME */\n\n/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */\n#define HAVE_STRUCT_TM_TM_GMTOFF 1\n\n/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */\n/* #undef HAVE_STRUCT_TM___TM_GMTOFF */\n\n/* Define to 1 if the system has the type `struct vfsconf'. */\n#define HAVE_STRUCT_VFSCONF 1\n\n/* Define to 1 if the system has the type `struct xvfsconf'. */\n/* #undef HAVE_STRUCT_XVFSCONF */\n\n/* Define to 1 if you have the `symlink' function. */\n#define HAVE_SYMLINK 1\n\n/* Define to 1 if you have the <sys/acl.h> header file. */\n#define HAVE_SYS_ACL_H 1\n\n/* Define to 1 if you have the <sys/cdefs.h> header file. */\n#define HAVE_SYS_CDEFS_H 1\n\n/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.\n   */\n/* #undef HAVE_SYS_DIR_H */\n\n/* Define to 1 if you have the <sys/ea.h> header file. */\n/* #undef HAVE_SYS_EA_H */\n\n/* Define to 1 if you have the <sys/extattr.h> header file. */\n/* #undef HAVE_SYS_EXTATTR_H */\n\n/* Define to 1 if you have the <sys/ioctl.h> header file. */\n#define HAVE_SYS_IOCTL_H 1\n\n/* Define to 1 if you have the <sys/mkdev.h> header file. */\n/* #undef HAVE_SYS_MKDEV_H */\n\n/* Define to 1 if you have the <sys/mount.h> header file. */\n#define HAVE_SYS_MOUNT_H 1\n\n/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.\n   */\n/* #undef HAVE_SYS_NDIR_H */\n\n/* Define to 1 if you have the <sys/param.h> header file. */\n#define HAVE_SYS_PARAM_H 1\n\n/* Define to 1 if you have the <sys/poll.h> header file. */\n#define HAVE_SYS_POLL_H 1\n\n/* Define to 1 if you have the <sys/richacl.h> header file. */\n/* #undef HAVE_SYS_RICHACL_H */\n\n/* Define to 1 if you have the <sys/select.h> header file. */\n#define HAVE_SYS_SELECT_H 1\n\n/* Define to 1 if you have the <sys/statfs.h> header file. */\n/* #undef HAVE_SYS_STATFS_H */\n\n/* Define to 1 if you have the <sys/statvfs.h> header file. */\n#define HAVE_SYS_STATVFS_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#define HAVE_SYS_STAT_H 1\n\n/* Define to 1 if you have the <sys/sysmacros.h> header file. */\n/* #undef HAVE_SYS_SYSMACROS_H */\n\n/* Define to 1 if you have the <sys/time.h> header file. */\n#define HAVE_SYS_TIME_H 1\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#define HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if you have the <sys/utime.h> header file. */\n/* #undef HAVE_SYS_UTIME_H */\n\n/* Define to 1 if you have the <sys/utsname.h> header file. */\n#define HAVE_SYS_UTSNAME_H 1\n\n/* Define to 1 if you have the <sys/vfs.h> header file. */\n/* #undef HAVE_SYS_VFS_H */\n\n/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */\n#define HAVE_SYS_WAIT_H 1\n\n/* Define to 1 if you have the <sys/xattr.h> header file. */\n#define HAVE_SYS_XATTR_H 1\n\n/* Define to 1 if you have the `timegm' function. */\n#define HAVE_TIMEGM 1\n\n/* Define to 1 if you have the <time.h> header file. */\n#define HAVE_TIME_H 1\n\n/* Define to 1 if you have the `tzset' function. */\n#define HAVE_TZSET 1\n\n/* Define to 1 if the system has the type `uintmax_t'. */\n#define HAVE_UINTMAX_T 1\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#define HAVE_UNISTD_H 1\n\n/* Define to 1 if you have the `unlinkat' function. */\n#define HAVE_UNLINKAT 1\n\n/* Define to 1 if you have the `unsetenv' function. */\n#define HAVE_UNSETENV 1\n\n/* Define to 1 if the system has the type `unsigned long long'. */\n#define HAVE_UNSIGNED_LONG_LONG 1\n\n/* Define to 1 if the system has the type `unsigned long long int'. */\n#define HAVE_UNSIGNED_LONG_LONG_INT 1\n\n/* Define to 1 if you have the `utime' function. */\n#define HAVE_UTIME 1\n\n/* Define to 1 if you have the `utimensat' function. */\n#define HAVE_UTIMENSAT 1\n\n/* Define to 1 if you have the `utimes' function. */\n#define HAVE_UTIMES 1\n\n/* Define to 1 if you have the <utime.h> header file. */\n#define HAVE_UTIME_H 1\n\n/* Define to 1 if you have the `vfork' function. */\n#define HAVE_VFORK 1\n\n/* Define to 1 if you have the `vprintf' function. */\n#define HAVE_VPRINTF 1\n\n/* Define to 1 if you have the <wchar.h> header file. */\n#define HAVE_WCHAR_H 1\n\n/* Define to 1 if the system has the type `wchar_t'. */\n#define HAVE_WCHAR_T 1\n\n/* Define to 1 if you have the `wcrtomb' function. */\n#define HAVE_WCRTOMB 1\n\n/* Define to 1 if you have the `wcscmp' function. */\n#define HAVE_WCSCMP 1\n\n/* Define to 1 if you have the `wcscpy' function. */\n#define HAVE_WCSCPY 1\n\n/* Define to 1 if you have the `wcslen' function. */\n#define HAVE_WCSLEN 1\n\n/* Define to 1 if you have the `wctomb' function. */\n#define HAVE_WCTOMB 1\n\n/* Define to 1 if you have the <wctype.h> header file. */\n#define HAVE_WCTYPE_H 1\n\n/* Define to 1 if you have the <wincrypt.h> header file. */\n/* #undef HAVE_WINCRYPT_H */\n\n/* Define to 1 if you have the <windows.h> header file. */\n/* #undef HAVE_WINDOWS_H */\n\n/* Define to 1 if you have the <winioctl.h> header file. */\n/* #undef HAVE_WINIOCTL_H */\n\n/* Define to 1 if you have the `wmemcmp' function. */\n#define HAVE_WMEMCMP 1\n\n/* Define to 1 if you have the `wmemcpy' function. */\n#define HAVE_WMEMCPY 1\n\n/* Define to 1 if you have the `wmemmove' function. */\n#define HAVE_WMEMMOVE 1\n\n/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */\n/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */\n\n/* Define to 1 if you have a working FS_IOC_GETFLAGS */\n/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */\n\n/* Define to 1 if you have the <zlib.h> header file. */\n#define HAVE_ZLIB_H 1\n\n/* Define to 1 if you have the <zstd.h> header file. */\n/* #undef HAVE_ZSTD_H */\n\n/* Define to 1 if you have the `_ctime64_s' function. */\n/* #undef HAVE__CTIME64_S */\n\n/* Define to 1 if you have the `_fseeki64' function. */\n/* #undef HAVE__FSEEKI64 */\n\n/* Define to 1 if you have the `_get_timezone' function. */\n/* #undef HAVE__GET_TIMEZONE */\n\n/* Define to 1 if you have the `_gmtime64_s' function. */\n/* #undef HAVE__GMTIME64_S */\n\n/* Define to 1 if you have the `_localtime64_s' function. */\n/* #undef HAVE__LOCALTIME64_S */\n\n/* Define to 1 if you have the `_mkgmtime64' function. */\n/* #undef HAVE__MKGMTIME64 */\n\n/* Define as const if the declaration of iconv() needs const. */\n#define ICONV_CONST \n\n/* Version number of libarchive as a single integer */\n#define LIBARCHIVE_VERSION_NUMBER \"3004003\"\n\n/* Version number of libarchive */\n#define LIBARCHIVE_VERSION_STRING \"3.4.3\"\n\n/* Define to 1 if `lstat' dereferences a symlink specified with a trailing\n   slash. */\n/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */\n\n/* Define to the sub-directory where libtool stores uninstalled libraries. */\n#define LT_OBJDIR \".libs/\"\n\n/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.\n   */\n/* #undef MAJOR_IN_MKDEV */\n\n/* Define to 1 if `major', `minor', and `makedev' are declared in\n   <sysmacros.h>. */\n/* #undef MAJOR_IN_SYSMACROS */\n\n/* Define to '0x05020000' for Windows Server 2003 APIs. */\n/* #undef NTDDI_VERSION */\n\n/* Name of package */\n#define PACKAGE \"libarchive\"\n\n/* Define to the address where bug reports for this package should be sent. */\n#define PACKAGE_BUGREPORT \"libarchive-discuss@googlegroups.com\"\n\n/* Define to the full name of this package. */\n#define PACKAGE_NAME \"libarchive\"\n\n/* Define to the full name and version of this package. */\n#define PACKAGE_STRING \"libarchive 3.4.3\"\n\n/* Define to the one symbol short name of this package. */\n#define PACKAGE_TARNAME \"libarchive\"\n\n/* Define to the home page for this package. */\n#define PACKAGE_URL \"\"\n\n/* Define to the version of this package. */\n#define PACKAGE_VERSION \"3.4.3\"\n\n/* Define to 1 if PCRE_STATIC needs to be defined. */\n/* #undef PCRE_STATIC */\n\n/* The size of `wchar_t', as computed by sizeof. */\n#define SIZEOF_WCHAR_T 4\n\n/* Define to 1 if you have the ANSI C header files. */\n#define STDC_HEADERS 1\n\n/* Define to 1 if strerror_r returns char *. */\n/* #undef STRERROR_R_CHAR_P */\n\n/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */\n#define TIME_WITH_SYS_TIME 1\n\n/* Enable extensions on AIX 3, Interix.  */\n#ifndef _ALL_SOURCE\n# define _ALL_SOURCE 1\n#endif\n/* Enable GNU extensions on systems that have them.  */\n#ifndef _GNU_SOURCE\n# define _GNU_SOURCE 1\n#endif\n/* Enable threading extensions on Solaris.  */\n#ifndef _POSIX_PTHREAD_SEMANTICS\n# define _POSIX_PTHREAD_SEMANTICS 1\n#endif\n/* Enable extensions on HP NonStop.  */\n#ifndef _TANDEM_SOURCE\n# define _TANDEM_SOURCE 1\n#endif\n/* Enable general extensions on Solaris.  */\n#ifndef __EXTENSIONS__\n# define __EXTENSIONS__ 1\n#endif\n\n\n/* Version number of package */\n#define VERSION \"3.4.3\"\n\n/* Define to '0x0502' for Windows Server 2003 APIs. */\n/* #undef WINVER */\n\n/* Enable large inode numbers on Mac OS X 10.5.  */\n#ifndef _DARWIN_USE_64_BIT_INODE\n# define _DARWIN_USE_64_BIT_INODE 1\n#endif\n\n/* Number of bits in a file offset, on hosts where this is settable. */\n/* #undef _FILE_OFFSET_BITS */\n\n/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */\n/* #undef _LARGEFILE_SOURCE */\n\n/* Define for large files, on AIX-style hosts. */\n/* #undef _LARGE_FILES */\n\n/* Define to 1 if on MINIX. */\n/* #undef _MINIX */\n\n/* Define to 2 if the system does not provide POSIX.1 features except with\n   this defined. */\n/* #undef _POSIX_1_SOURCE */\n\n/* Define to 1 if you need to in order for `stat' and other things to work. */\n/* #undef _POSIX_SOURCE */\n\n/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,\n   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the\n   #define below would cause a syntax error. */\n/* #undef _UINT32_T */\n\n/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,\n   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the\n   #define below would cause a syntax error. */\n/* #undef _UINT64_T */\n\n/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,\n   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the\n   #define below would cause a syntax error. */\n/* #undef _UINT8_T */\n\n/* Define to '0x0502' for Windows Server 2003 APIs. */\n/* #undef _WIN32_WINNT */\n\n/* Define to empty if `const' does not conform to ANSI C. */\n/* #undef const */\n\n/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't\n   define. */\n/* #undef gid_t */\n\n/* Define to `unsigned long' if <sys/types.h> does not define. */\n/* #undef id_t */\n\n/* Define to the type of a signed integer type of width exactly 16 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef int16_t */\n\n/* Define to the type of a signed integer type of width exactly 32 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef int32_t */\n\n/* Define to the type of a signed integer type of width exactly 64 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef int64_t */\n\n/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do\n   not define. */\n/* #undef intmax_t */\n\n/* Define to `int' if <sys/types.h> does not define. */\n/* #undef mode_t */\n\n/* Define to `long long' if <sys/types.h> does not define. */\n/* #undef off_t */\n\n/* Define to `unsigned int' if <sys/types.h> does not define. */\n/* #undef size_t */\n\n/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't\n   define. */\n/* #undef uid_t */\n\n/* Define to the type of an unsigned integer type of width exactly 16 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef uint16_t */\n\n/* Define to the type of an unsigned integer type of width exactly 32 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef uint32_t */\n\n/* Define to the type of an unsigned integer type of width exactly 64 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef uint64_t */\n\n/* Define to the type of an unsigned integer type of width exactly 8 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef uint8_t */\n\n/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>\n   do not define. */\n/* #undef uintmax_t */\n\n/* Define to `unsigned int' if <sys/types.h> does not define. */\n/* #undef uintptr_t */\n"
  },
  {
    "path": "deps/kconfig-fragment.sh",
    "content": "#!/bin/sh\noutput=\"$1\"\nshift\n: > \"$output\"\nfor cfg in $@; do\n    echo \"$cfg\" >> \"$output\"\ndone\n"
  },
  {
    "path": "deps/libarchive.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tBB10E403248DA6F1009C7A74 /* archive_read_support_format_rar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0E4248DA6EA009C7A74 /* archive_read_support_format_rar.c */; };\n\t\tBB10E404248DA6F1009C7A74 /* archive_read_support_format_empty.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0E6248DA6EA009C7A74 /* archive_read_support_format_empty.c */; };\n\t\tBB10E405248DA6F1009C7A74 /* archive_blake2s_ref.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0E7248DA6EA009C7A74 /* archive_blake2s_ref.c */; };\n\t\tBB10E406248DA6F1009C7A74 /* archive_read_support_filter_zstd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0E8248DA6EA009C7A74 /* archive_read_support_filter_zstd.c */; };\n\t\tBB10E408248DA6F1009C7A74 /* archive_write_set_format_7zip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0EB248DA6EA009C7A74 /* archive_write_set_format_7zip.c */; };\n\t\tBB10E40A248DA6F1009C7A74 /* archive_write_disk_set_standard_lookup.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0EE248DA6EA009C7A74 /* archive_write_disk_set_standard_lookup.c */; };\n\t\tBB10E40B248DA6F1009C7A74 /* archive_virtual.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0EF248DA6EA009C7A74 /* archive_virtual.c */; };\n\t\tBB10E40C248DA6F1009C7A74 /* archive_read_support_format_ar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F0248DA6EA009C7A74 /* archive_read_support_format_ar.c */; };\n\t\tBB10E40D248DA6F1009C7A74 /* archive_read_support_filter_lrzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F1248DA6EA009C7A74 /* archive_read_support_filter_lrzip.c */; };\n\t\tBB10E40E248DA6F1009C7A74 /* archive_entry_copy_bhfi.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F2248DA6EA009C7A74 /* archive_entry_copy_bhfi.c */; };\n\t\tBB10E40F248DA6F1009C7A74 /* archive_write_set_passphrase.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F3248DA6EA009C7A74 /* archive_write_set_passphrase.c */; };\n\t\tBB10E410248DA6F1009C7A74 /* archive_read_support_filter_compress.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F5248DA6EA009C7A74 /* archive_read_support_filter_compress.c */; };\n\t\tBB10E411248DA6F1009C7A74 /* archive_util.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F6248DA6EA009C7A74 /* archive_util.c */; };\n\t\tBB10E412248DA6F1009C7A74 /* archive_read_extract.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F7248DA6EA009C7A74 /* archive_read_extract.c */; };\n\t\tBB10E414248DA6F1009C7A74 /* archive_write_add_filter_lzop.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0F9248DA6EA009C7A74 /* archive_write_add_filter_lzop.c */; };\n\t\tBB10E416248DA6F1009C7A74 /* archive_write_add_filter_grzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0FC248DA6EA009C7A74 /* archive_write_add_filter_grzip.c */; };\n\t\tBB10E417248DA6F1009C7A74 /* archive_write_set_format_by_name.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E0FD248DA6EA009C7A74 /* archive_write_set_format_by_name.c */; };\n\t\tBB10E41A248DA6F1009C7A74 /* archive_cmdline.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E103248DA6EA009C7A74 /* archive_cmdline.c */; };\n\t\tBB10E41B248DA6F1009C7A74 /* archive_read_open_file.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E104248DA6EA009C7A74 /* archive_read_open_file.c */; };\n\t\tBB10E41C248DA6F1009C7A74 /* archive_write_add_filter_program.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E105248DA6EA009C7A74 /* archive_write_add_filter_program.c */; };\n\t\tBB10E41E248DA6F1009C7A74 /* archive_read_support_format_tar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E107248DA6EA009C7A74 /* archive_read_support_format_tar.c */; };\n\t\tBB10E41F248DA6F1009C7A74 /* archive_entry_stat.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E108248DA6EA009C7A74 /* archive_entry_stat.c */; };\n\t\tBB10E421248DA6F1009C7A74 /* archive_pack_dev.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E10A248DA6EA009C7A74 /* archive_pack_dev.c */; };\n\t\tBB10E422248DA6F1009C7A74 /* archive_write_set_format_warc.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E10B248DA6EA009C7A74 /* archive_write_set_format_warc.c */; };\n\t\tBB10E423248DA6F1009C7A74 /* archive_disk_acl_freebsd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E10C248DA6EA009C7A74 /* archive_disk_acl_freebsd.c */; };\n\t\tBB10E424248DA6F1009C7A74 /* archive_entry_strmode.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E10F248DA6EA009C7A74 /* archive_entry_strmode.c */; };\n\t\tBB10E425248DA6F1009C7A74 /* archive_read_disk_entry_from_file.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E110248DA6EA009C7A74 /* archive_read_disk_entry_from_file.c */; };\n\t\tBB10E426248DA6F1009C7A74 /* archive_ppmd7.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E111248DA6EA009C7A74 /* archive_ppmd7.c */; };\n\t\tBB10E427248DA6F1009C7A74 /* archive_options.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E112248DA6EA009C7A74 /* archive_options.c */; };\n\t\tBB10E53B248DA6F4009C7A74 /* archive_hmac.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E362248DA6EE009C7A74 /* archive_hmac.c */; };\n\t\tBB10E53E248DA6F4009C7A74 /* archive_read_support_format_all.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E366248DA6EE009C7A74 /* archive_read_support_format_all.c */; };\n\t\tBB10E53F248DA6F4009C7A74 /* archive_read_open_filename.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E367248DA6EE009C7A74 /* archive_read_open_filename.c */; };\n\t\tBB10E540248DA6F4009C7A74 /* archive_write_set_format_cpio_newc.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E368248DA6EE009C7A74 /* archive_write_set_format_cpio_newc.c */; };\n\t\tBB10E541248DA6F4009C7A74 /* archive_entry_link_resolver.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E369248DA6EE009C7A74 /* archive_entry_link_resolver.c */; };\n\t\tBB10E544248DA6F4009C7A74 /* archive_windows.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E36E248DA6EF009C7A74 /* archive_windows.c */; };\n\t\tBB10E545248DA6F4009C7A74 /* archive_write_set_format_gnutar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E36F248DA6EF009C7A74 /* archive_write_set_format_gnutar.c */; };\n\t\tBB10E546248DA6F4009C7A74 /* archive_write_set_format_filter_by_ext.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E370248DA6EF009C7A74 /* archive_write_set_format_filter_by_ext.c */; };\n\t\tBB10E547248DA6F4009C7A74 /* archive_read_support_filter_uu.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E371248DA6EF009C7A74 /* archive_read_support_filter_uu.c */; };\n\t\tBB10E548248DA6F4009C7A74 /* archive_write_set_format_ustar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E372248DA6EF009C7A74 /* archive_write_set_format_ustar.c */; };\n\t\tBB10E549248DA6F4009C7A74 /* archive_entry_sparse.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E373248DA6EF009C7A74 /* archive_entry_sparse.c */; };\n\t\tBB10E54A248DA6F4009C7A74 /* archive_disk_acl_sunos.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E374248DA6EF009C7A74 /* archive_disk_acl_sunos.c */; };\n\t\tBB10E54B248DA6F4009C7A74 /* archive_read.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E375248DA6EF009C7A74 /* archive_read.c */; };\n\t\tBB10E54C248DA6F4009C7A74 /* archive_read_open_fd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E376248DA6EF009C7A74 /* archive_read_open_fd.c */; };\n\t\tBB10E54D248DA6F4009C7A74 /* archive_entry_copy_stat.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E377248DA6EF009C7A74 /* archive_entry_copy_stat.c */; };\n\t\tBB10E54E248DA6F4009C7A74 /* archive_write_set_format_v7tar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E378248DA6EF009C7A74 /* archive_write_set_format_v7tar.c */; };\n\t\tBB10E550248DA6F4009C7A74 /* archive_write_set_format_cpio.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E37C248DA6EF009C7A74 /* archive_write_set_format_cpio.c */; };\n\t\tBB10E551248DA6F4009C7A74 /* archive_write_open_filename.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E37D248DA6EF009C7A74 /* archive_write_open_filename.c */; };\n\t\tBB10E555248DA6F4009C7A74 /* archive_write_add_filter_by_name.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E381248DA6EF009C7A74 /* archive_write_add_filter_by_name.c */; };\n\t\tBB10E557248DA6F4009C7A74 /* archive_read_support_filter_xz.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E383248DA6EF009C7A74 /* archive_read_support_filter_xz.c */; };\n\t\tBB10E558248DA6F4009C7A74 /* archive_read_support_filter_program.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E384248DA6EF009C7A74 /* archive_read_support_filter_program.c */; };\n\t\tBB10E559248DA6F4009C7A74 /* archive_write_set_format.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E386248DA6EF009C7A74 /* archive_write_set_format.c */; };\n\t\tBB10E55A248DA6F4009C7A74 /* archive_cryptor.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E387248DA6EF009C7A74 /* archive_cryptor.c */; };\n\t\tBB10E55B248DA6F4009C7A74 /* archive_write_set_format_pax.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E388248DA6EF009C7A74 /* archive_write_set_format_pax.c */; };\n\t\tBB10E55C248DA6F4009C7A74 /* archive_read_support_format_mtree.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E38A248DA6EF009C7A74 /* archive_read_support_format_mtree.c */; };\n\t\tBB10E55D248DA6F4009C7A74 /* archive_digest.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E38B248DA6EF009C7A74 /* archive_digest.c */; };\n\t\tBB10E55E248DA6F4009C7A74 /* archive_match.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E38C248DA6EF009C7A74 /* archive_match.c */; };\n\t\tBB10E560248DA6F4009C7A74 /* archive_read_support_filter_rpm.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E38E248DA6EF009C7A74 /* archive_read_support_filter_rpm.c */; };\n\t\tBB10E561248DA6F4009C7A74 /* archive_write_set_format_shar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E390248DA6EF009C7A74 /* archive_write_set_format_shar.c */; };\n\t\tBB10E562248DA6F4009C7A74 /* archive_write_add_filter.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E391248DA6EF009C7A74 /* archive_write_add_filter.c */; };\n\t\tBB10E563248DA6F4009C7A74 /* archive_version_details.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E393248DA6EF009C7A74 /* archive_version_details.c */; };\n\t\tBB10E564248DA6F4009C7A74 /* archive_read_support_filter_lzop.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E394248DA6EF009C7A74 /* archive_read_support_filter_lzop.c */; };\n\t\tBB10E565248DA6F4009C7A74 /* archive_write_open_fd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E395248DA6EF009C7A74 /* archive_write_open_fd.c */; };\n\t\tBB10E566248DA6F4009C7A74 /* archive_write_add_filter_lz4.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E396248DA6EF009C7A74 /* archive_write_add_filter_lz4.c */; };\n\t\tBB10E567248DA6F4009C7A74 /* archive_rb.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E397248DA6EF009C7A74 /* archive_rb.c */; };\n\t\tBB10E569248DA6F4009C7A74 /* archive_read_support_format_by_code.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E399248DA6EF009C7A74 /* archive_read_support_format_by_code.c */; };\n\t\tBB10E56B248DA6F4009C7A74 /* archive_read_set_format.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E39C248DA6EF009C7A74 /* archive_read_set_format.c */; };\n\t\tBB10E56D248DA6F4009C7A74 /* archive_read_support_filter_bzip2.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E39F248DA6EF009C7A74 /* archive_read_support_filter_bzip2.c */; };\n\t\tBB10E56E248DA6F4009C7A74 /* archive_entry_xattr.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3A0248DA6EF009C7A74 /* archive_entry_xattr.c */; };\n\t\tBB10E56F248DA6F4009C7A74 /* archive_write_add_filter_zstd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3A1248DA6EF009C7A74 /* archive_write_add_filter_zstd.c */; };\n\t\tBB10E572248DA6F4009C7A74 /* archive_pathmatch.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3A5248DA6EF009C7A74 /* archive_pathmatch.c */; };\n\t\tBB10E573248DA6F4009C7A74 /* archive_write_disk_windows.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3A8248DA6EF009C7A74 /* archive_write_disk_windows.c */; };\n\t\tBB10E575248DA6F4009C7A74 /* archive_write.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3AA248DA6EF009C7A74 /* archive_write.c */; };\n\t\tBB10E576248DA6F4009C7A74 /* archive_read_add_passphrase.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3AB248DA6EF009C7A74 /* archive_read_add_passphrase.c */; };\n\t\tBB10E577248DA6F4009C7A74 /* archive_write_add_filter_uuencode.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3AD248DA6EF009C7A74 /* archive_write_add_filter_uuencode.c */; };\n\t\tBB10E578248DA6F4009C7A74 /* archive_read_support_filter_all.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3AE248DA6EF009C7A74 /* archive_read_support_filter_all.c */; };\n\t\tBB10E579248DA6F4009C7A74 /* archive_read_support_format_cab.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3AF248DA6EF009C7A74 /* archive_read_support_format_cab.c */; };\n\t\tBB10E57A248DA6F4009C7A74 /* archive_read_support_filter_grzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B0248DA6EF009C7A74 /* archive_read_support_filter_grzip.c */; };\n\t\tBB10E57B248DA6F4009C7A74 /* archive_read_support_format_7zip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B2248DA6EF009C7A74 /* archive_read_support_format_7zip.c */; };\n\t\tBB10E57C248DA6F4009C7A74 /* archive_read_support_format_lha.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B3248DA6EF009C7A74 /* archive_read_support_format_lha.c */; };\n\t\tBB10E57D248DA6F4009C7A74 /* archive_write_add_filter_none.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B4248DA6EF009C7A74 /* archive_write_add_filter_none.c */; };\n\t\tBB10E57E248DA6F4009C7A74 /* archive_entry.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B5248DA6EF009C7A74 /* archive_entry.c */; };\n\t\tBB10E57F248DA6F4009C7A74 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B6248DA6EF009C7A74 /* xxhash.c */; };\n\t\tBB10E580248DA6F4009C7A74 /* archive_read_append_filter.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B8248DA6EF009C7A74 /* archive_read_append_filter.c */; };\n\t\tBB10E581248DA6F4009C7A74 /* archive_read_disk_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3B9248DA6EF009C7A74 /* archive_read_disk_posix.c */; };\n\t\tBB10E583248DA6F5009C7A74 /* filter_fork_windows.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3BB248DA6EF009C7A74 /* filter_fork_windows.c */; };\n\t\tBB10E584248DA6F5009C7A74 /* archive_read_support_filter_lz4.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3BC248DA6EF009C7A74 /* archive_read_support_filter_lz4.c */; };\n\t\tBB10E585248DA6F5009C7A74 /* archive_write_set_format_zip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3BD248DA6EF009C7A74 /* archive_write_set_format_zip.c */; };\n\t\tBB10E586248DA6F5009C7A74 /* archive_write_set_format_xar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3BE248DA6EF009C7A74 /* archive_write_set_format_xar.c */; };\n\t\tBB10E587248DA6F5009C7A74 /* archive_read_disk_set_standard_lookup.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3BF248DA6F0009C7A74 /* archive_read_disk_set_standard_lookup.c */; };\n\t\tBB10E588248DA6F5009C7A74 /* archive_write_add_filter_lrzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C0248DA6F0009C7A74 /* archive_write_add_filter_lrzip.c */; };\n\t\tBB10E589248DA6F5009C7A74 /* archive_write_add_filter_xz.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C1248DA6F0009C7A74 /* archive_write_add_filter_xz.c */; };\n\t\tBB10E58A248DA6F5009C7A74 /* archive_write_set_format_ar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C2248DA6F0009C7A74 /* archive_write_set_format_ar.c */; };\n\t\tBB10E58B248DA6F5009C7A74 /* archive_write_set_format_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C3248DA6F0009C7A74 /* archive_write_set_format_raw.c */; };\n\t\tBB10E58C248DA6F5009C7A74 /* archive_acl.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C4248DA6F0009C7A74 /* archive_acl.c */; };\n\t\tBB10E58F248DA6F5009C7A74 /* archive_read_support_format_rar5.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3C7248DA6F0009C7A74 /* archive_read_support_format_rar5.c */; };\n\t\tBB10E591248DA6F5009C7A74 /* archive_string_sprintf.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3CA248DA6F0009C7A74 /* archive_string_sprintf.c */; };\n\t\tBB10E592248DA6F5009C7A74 /* filter_fork_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3CB248DA6F0009C7A74 /* filter_fork_posix.c */; };\n\t\tBB10E593248DA6F5009C7A74 /* archive_write_set_format_mtree.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3CC248DA6F0009C7A74 /* archive_write_set_format_mtree.c */; };\n\t\tBB10E594248DA6F5009C7A74 /* archive_read_support_format_warc.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3CD248DA6F0009C7A74 /* archive_read_support_format_warc.c */; };\n\t\tBB10E595248DA6F5009C7A74 /* archive_write_open_file.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3CF248DA6F0009C7A74 /* archive_write_open_file.c */; };\n\t\tBB10E596248DA6F5009C7A74 /* archive_write_add_filter_gzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D1248DA6F0009C7A74 /* archive_write_add_filter_gzip.c */; };\n\t\tBB10E597248DA6F5009C7A74 /* archive_getdate.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D2248DA6F0009C7A74 /* archive_getdate.c */; };\n\t\tBB10E599248DA6F5009C7A74 /* archive_check_magic.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D4248DA6F0009C7A74 /* archive_check_magic.c */; };\n\t\tBB10E59B248DA6F5009C7A74 /* archive_write_disk_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D6248DA6F0009C7A74 /* archive_write_disk_posix.c */; };\n\t\tBB10E59C248DA6F5009C7A74 /* archive_write_add_filter_compress.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D7248DA6F0009C7A74 /* archive_write_add_filter_compress.c */; };\n\t\tBB10E59D248DA6F5009C7A74 /* archive_read_extract2.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3D9248DA6F0009C7A74 /* archive_read_extract2.c */; };\n\t\tBB10E59E248DA6F5009C7A74 /* archive_read_support_format_cpio.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3DA248DA6F0009C7A74 /* archive_read_support_format_cpio.c */; };\n\t\tBB10E5A0248DA6F5009C7A74 /* archive_read_support_format_iso9660.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3DD248DA6F0009C7A74 /* archive_read_support_format_iso9660.c */; };\n\t\tBB10E5A2248DA6F5009C7A74 /* archive_write_add_filter_b64encode.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3DF248DA6F0009C7A74 /* archive_write_add_filter_b64encode.c */; };\n\t\tBB10E5A3248DA6F5009C7A74 /* archive_blake2sp_ref.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3E1248DA6F0009C7A74 /* archive_blake2sp_ref.c */; };\n\t\tBB10E5A4248DA6F5009C7A74 /* archive_read_set_options.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3E4248DA6F0009C7A74 /* archive_read_set_options.c */; };\n\t\tBB10E5A5248DA6F5009C7A74 /* archive_read_support_format_xar.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3E6248DA6F0009C7A74 /* archive_read_support_format_xar.c */; };\n\t\tBB10E5A6248DA6F5009C7A74 /* archive_write_open_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3E7248DA6F0009C7A74 /* archive_write_open_memory.c */; };\n\t\tBB10E5A7248DA6F5009C7A74 /* archive_read_support_filter_gzip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3E9248DA6F0009C7A74 /* archive_read_support_filter_gzip.c */; };\n\t\tBB10E5A8248DA6F5009C7A74 /* archive_read_support_format_zip.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3EA248DA6F0009C7A74 /* archive_read_support_format_zip.c */; };\n\t\tBB10E5A9248DA6F5009C7A74 /* archive_read_support_format_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3EC248DA6F0009C7A74 /* archive_read_support_format_raw.c */; };\n\t\tBB10E5AA248DA6F5009C7A74 /* archive_write_set_format_iso9660.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3ED248DA6F0009C7A74 /* archive_write_set_format_iso9660.c */; };\n\t\tBB10E5AB248DA6F5009C7A74 /* archive_disk_acl_linux.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3EE248DA6F0009C7A74 /* archive_disk_acl_linux.c */; };\n\t\tBB10E5AC248DA6F5009C7A74 /* archive_read_open_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3EF248DA6F0009C7A74 /* archive_read_open_memory.c */; };\n\t\tBB10E5AD248DA6F5009C7A74 /* archive_write_set_options.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F0248DA6F0009C7A74 /* archive_write_set_options.c */; };\n\t\tBB10E5AF248DA6F5009C7A74 /* archive_string.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F3248DA6F0009C7A74 /* archive_string.c */; };\n\t\tBB10E5B1248DA6F5009C7A74 /* archive_read_data_into_fd.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F5248DA6F0009C7A74 /* archive_read_data_into_fd.c */; };\n\t\tBB10E5B2248DA6F5009C7A74 /* archive_write_add_filter_bzip2.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F6248DA6F0009C7A74 /* archive_write_add_filter_bzip2.c */; };\n\t\tBB10E5B4248DA6F5009C7A74 /* archive_disk_acl_darwin.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F8248DA6F0009C7A74 /* archive_disk_acl_darwin.c */; };\n\t\tBB10E5B5248DA6F5009C7A74 /* archive_random.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3F9248DA6F0009C7A74 /* archive_random.c */; };\n\t\tBB10E5B6248DA6F5009C7A74 /* archive_ppmd8.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3FA248DA6F0009C7A74 /* archive_ppmd8.c */; };\n\t\tBB10E5BA248DA6F5009C7A74 /* archive_read_support_filter_none.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E3FE248DA6F0009C7A74 /* archive_read_support_filter_none.c */; };\n\t\tBB10E5BB248DA6F5009C7A74 /* archive_read_disk_windows.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E400248DA6F0009C7A74 /* archive_read_disk_windows.c */; };\n\t\tBBCE66E8249A875400F45269 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBCE66E7249A875400F45269 /* libxml2.tbd */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tBB10E0D6248DA67B009C7A74 /* libarchive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libarchive.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB10E0E4248DA6EA009C7A74 /* archive_read_support_format_rar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_rar.c; sourceTree = \"<group>\"; };\n\t\tBB10E0E6248DA6EA009C7A74 /* archive_read_support_format_empty.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_empty.c; sourceTree = \"<group>\"; };\n\t\tBB10E0E7248DA6EA009C7A74 /* archive_blake2s_ref.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_blake2s_ref.c; sourceTree = \"<group>\"; };\n\t\tBB10E0E8248DA6EA009C7A74 /* archive_read_support_filter_zstd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_zstd.c; sourceTree = \"<group>\"; };\n\t\tBB10E0EA248DA6EA009C7A74 /* archive_random_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_random_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E0EB248DA6EA009C7A74 /* archive_write_set_format_7zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_7zip.c; sourceTree = \"<group>\"; };\n\t\tBB10E0ED248DA6EA009C7A74 /* config_freebsd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_freebsd.h; sourceTree = \"<group>\"; };\n\t\tBB10E0EE248DA6EA009C7A74 /* archive_write_disk_set_standard_lookup.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_disk_set_standard_lookup.c; sourceTree = \"<group>\"; };\n\t\tBB10E0EF248DA6EA009C7A74 /* archive_virtual.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_virtual.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F0248DA6EA009C7A74 /* archive_read_support_format_ar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_ar.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F1248DA6EA009C7A74 /* archive_read_support_filter_lrzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_lrzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F2248DA6EA009C7A74 /* archive_entry_copy_bhfi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_copy_bhfi.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F3248DA6EA009C7A74 /* archive_write_set_passphrase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_passphrase.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F5248DA6EA009C7A74 /* archive_read_support_filter_compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_compress.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F6248DA6EA009C7A74 /* archive_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_util.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F7248DA6EA009C7A74 /* archive_read_extract.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_extract.c; sourceTree = \"<group>\"; };\n\t\tBB10E0F8248DA6EA009C7A74 /* archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive.h; sourceTree = \"<group>\"; };\n\t\tBB10E0F9248DA6EA009C7A74 /* archive_write_add_filter_lzop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_lzop.c; sourceTree = \"<group>\"; };\n\t\tBB10E0FB248DA6EA009C7A74 /* archive_entry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_entry.h; sourceTree = \"<group>\"; };\n\t\tBB10E0FC248DA6EA009C7A74 /* archive_write_add_filter_grzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_grzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E0FD248DA6EA009C7A74 /* archive_write_set_format_by_name.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_by_name.c; sourceTree = \"<group>\"; };\n\t\tBB10E0FE248DA6EA009C7A74 /* archive_blake2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_blake2.h; sourceTree = \"<group>\"; };\n\t\tBB10E101248DA6EA009C7A74 /* archive_entry_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_entry_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E103248DA6EA009C7A74 /* archive_cmdline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_cmdline.c; sourceTree = \"<group>\"; };\n\t\tBB10E104248DA6EA009C7A74 /* archive_read_open_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_open_file.c; sourceTree = \"<group>\"; };\n\t\tBB10E105248DA6EA009C7A74 /* archive_write_add_filter_program.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_program.c; sourceTree = \"<group>\"; };\n\t\tBB10E106248DA6EA009C7A74 /* archive_blake2_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_blake2_impl.h; sourceTree = \"<group>\"; };\n\t\tBB10E107248DA6EA009C7A74 /* archive_read_support_format_tar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_tar.c; sourceTree = \"<group>\"; };\n\t\tBB10E108248DA6EA009C7A74 /* archive_entry_stat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_stat.c; sourceTree = \"<group>\"; };\n\t\tBB10E109248DA6EA009C7A74 /* archive_entry_locale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_entry_locale.h; sourceTree = \"<group>\"; };\n\t\tBB10E10A248DA6EA009C7A74 /* archive_pack_dev.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_pack_dev.c; sourceTree = \"<group>\"; };\n\t\tBB10E10B248DA6EA009C7A74 /* archive_write_set_format_warc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_warc.c; sourceTree = \"<group>\"; };\n\t\tBB10E10C248DA6EA009C7A74 /* archive_disk_acl_freebsd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_disk_acl_freebsd.c; sourceTree = \"<group>\"; };\n\t\tBB10E10F248DA6EA009C7A74 /* archive_entry_strmode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_strmode.c; sourceTree = \"<group>\"; };\n\t\tBB10E110248DA6EA009C7A74 /* archive_read_disk_entry_from_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_disk_entry_from_file.c; sourceTree = \"<group>\"; };\n\t\tBB10E111248DA6EA009C7A74 /* archive_ppmd7.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_ppmd7.c; sourceTree = \"<group>\"; };\n\t\tBB10E112248DA6EA009C7A74 /* archive_options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_options.c; sourceTree = \"<group>\"; };\n\t\tBB10E362248DA6EE009C7A74 /* archive_hmac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_hmac.c; sourceTree = \"<group>\"; };\n\t\tBB10E363248DA6EE009C7A74 /* archive_digest_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_digest_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E365248DA6EE009C7A74 /* archive_getdate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_getdate.h; sourceTree = \"<group>\"; };\n\t\tBB10E366248DA6EE009C7A74 /* archive_read_support_format_all.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_all.c; sourceTree = \"<group>\"; };\n\t\tBB10E367248DA6EE009C7A74 /* archive_read_open_filename.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_open_filename.c; sourceTree = \"<group>\"; };\n\t\tBB10E368248DA6EE009C7A74 /* archive_write_set_format_cpio_newc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_cpio_newc.c; sourceTree = \"<group>\"; };\n\t\tBB10E369248DA6EE009C7A74 /* archive_entry_link_resolver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_link_resolver.c; sourceTree = \"<group>\"; };\n\t\tBB10E36A248DA6EE009C7A74 /* archive_ppmd7_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_ppmd7_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E36B248DA6EF009C7A74 /* archive_options_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_options_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E36E248DA6EF009C7A74 /* archive_windows.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_windows.c; sourceTree = \"<group>\"; };\n\t\tBB10E36F248DA6EF009C7A74 /* archive_write_set_format_gnutar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_gnutar.c; sourceTree = \"<group>\"; };\n\t\tBB10E370248DA6EF009C7A74 /* archive_write_set_format_filter_by_ext.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_filter_by_ext.c; sourceTree = \"<group>\"; };\n\t\tBB10E371248DA6EF009C7A74 /* archive_read_support_filter_uu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_uu.c; sourceTree = \"<group>\"; };\n\t\tBB10E372248DA6EF009C7A74 /* archive_write_set_format_ustar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_ustar.c; sourceTree = \"<group>\"; };\n\t\tBB10E373248DA6EF009C7A74 /* archive_entry_sparse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_sparse.c; sourceTree = \"<group>\"; };\n\t\tBB10E374248DA6EF009C7A74 /* archive_disk_acl_sunos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_disk_acl_sunos.c; sourceTree = \"<group>\"; };\n\t\tBB10E375248DA6EF009C7A74 /* archive_read.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read.c; sourceTree = \"<group>\"; };\n\t\tBB10E376248DA6EF009C7A74 /* archive_read_open_fd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_open_fd.c; sourceTree = \"<group>\"; };\n\t\tBB10E377248DA6EF009C7A74 /* archive_entry_copy_stat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_copy_stat.c; sourceTree = \"<group>\"; };\n\t\tBB10E378248DA6EF009C7A74 /* archive_write_set_format_v7tar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_v7tar.c; sourceTree = \"<group>\"; };\n\t\tBB10E37B248DA6EF009C7A74 /* archive_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_platform.h; sourceTree = \"<group>\"; };\n\t\tBB10E37C248DA6EF009C7A74 /* archive_write_set_format_cpio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_cpio.c; sourceTree = \"<group>\"; };\n\t\tBB10E37D248DA6EF009C7A74 /* archive_write_open_filename.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_open_filename.c; sourceTree = \"<group>\"; };\n\t\tBB10E37E248DA6EF009C7A74 /* archive_write_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_write_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E37F248DA6EF009C7A74 /* archive_ppmd_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_ppmd_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E380248DA6EF009C7A74 /* archive_acl_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_acl_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E381248DA6EF009C7A74 /* archive_write_add_filter_by_name.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_by_name.c; sourceTree = \"<group>\"; };\n\t\tBB10E382248DA6EF009C7A74 /* archive_hmac_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_hmac_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E383248DA6EF009C7A74 /* archive_read_support_filter_xz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_xz.c; sourceTree = \"<group>\"; };\n\t\tBB10E384248DA6EF009C7A74 /* archive_read_support_filter_program.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_program.c; sourceTree = \"<group>\"; };\n\t\tBB10E386248DA6EF009C7A74 /* archive_write_set_format.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format.c; sourceTree = \"<group>\"; };\n\t\tBB10E387248DA6EF009C7A74 /* archive_cryptor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_cryptor.c; sourceTree = \"<group>\"; };\n\t\tBB10E388248DA6EF009C7A74 /* archive_write_set_format_pax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_pax.c; sourceTree = \"<group>\"; };\n\t\tBB10E38A248DA6EF009C7A74 /* archive_read_support_format_mtree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_mtree.c; sourceTree = \"<group>\"; };\n\t\tBB10E38B248DA6EF009C7A74 /* archive_digest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_digest.c; sourceTree = \"<group>\"; };\n\t\tBB10E38C248DA6EF009C7A74 /* archive_match.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_match.c; sourceTree = \"<group>\"; };\n\t\tBB10E38D248DA6EF009C7A74 /* archive_read_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_read_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E38E248DA6EF009C7A74 /* archive_read_support_filter_rpm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_rpm.c; sourceTree = \"<group>\"; };\n\t\tBB10E390248DA6EF009C7A74 /* archive_write_set_format_shar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_shar.c; sourceTree = \"<group>\"; };\n\t\tBB10E391248DA6EF009C7A74 /* archive_write_add_filter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter.c; sourceTree = \"<group>\"; };\n\t\tBB10E392248DA6EF009C7A74 /* archive_read_filter.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = archive_read_filter.3; sourceTree = \"<group>\"; };\n\t\tBB10E393248DA6EF009C7A74 /* archive_version_details.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_version_details.c; sourceTree = \"<group>\"; };\n\t\tBB10E394248DA6EF009C7A74 /* archive_read_support_filter_lzop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_lzop.c; sourceTree = \"<group>\"; };\n\t\tBB10E395248DA6EF009C7A74 /* archive_write_open_fd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_open_fd.c; sourceTree = \"<group>\"; };\n\t\tBB10E396248DA6EF009C7A74 /* archive_write_add_filter_lz4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_lz4.c; sourceTree = \"<group>\"; };\n\t\tBB10E397248DA6EF009C7A74 /* archive_rb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_rb.c; sourceTree = \"<group>\"; };\n\t\tBB10E398248DA6EF009C7A74 /* archive_platform_acl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_platform_acl.h; sourceTree = \"<group>\"; };\n\t\tBB10E399248DA6EF009C7A74 /* archive_read_support_format_by_code.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_by_code.c; sourceTree = \"<group>\"; };\n\t\tBB10E39A248DA6EF009C7A74 /* archive_openssl_hmac_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_openssl_hmac_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E39C248DA6EF009C7A74 /* archive_read_set_format.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_set_format.c; sourceTree = \"<group>\"; };\n\t\tBB10E39D248DA6EF009C7A74 /* archive_read_disk_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_read_disk_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E39F248DA6EF009C7A74 /* archive_read_support_filter_bzip2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_bzip2.c; sourceTree = \"<group>\"; };\n\t\tBB10E3A0248DA6EF009C7A74 /* archive_entry_xattr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry_xattr.c; sourceTree = \"<group>\"; };\n\t\tBB10E3A1248DA6EF009C7A74 /* archive_write_add_filter_zstd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_zstd.c; sourceTree = \"<group>\"; };\n\t\tBB10E3A3248DA6EF009C7A74 /* archive_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_string.h; sourceTree = \"<group>\"; };\n\t\tBB10E3A4248DA6EF009C7A74 /* archive_write_set_format_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_write_set_format_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3A5248DA6EF009C7A74 /* archive_pathmatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_pathmatch.c; sourceTree = \"<group>\"; };\n\t\tBB10E3A8248DA6EF009C7A74 /* archive_write_disk_windows.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_disk_windows.c; sourceTree = \"<group>\"; };\n\t\tBB10E3A9248DA6EF009C7A74 /* archive_crc32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_crc32.h; sourceTree = \"<group>\"; };\n\t\tBB10E3AA248DA6EF009C7A74 /* archive_write.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write.c; sourceTree = \"<group>\"; };\n\t\tBB10E3AB248DA6EF009C7A74 /* archive_read_add_passphrase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_add_passphrase.c; sourceTree = \"<group>\"; };\n\t\tBB10E3AD248DA6EF009C7A74 /* archive_write_add_filter_uuencode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_uuencode.c; sourceTree = \"<group>\"; };\n\t\tBB10E3AE248DA6EF009C7A74 /* archive_read_support_filter_all.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_all.c; sourceTree = \"<group>\"; };\n\t\tBB10E3AF248DA6EF009C7A74 /* archive_read_support_format_cab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_cab.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B0248DA6EF009C7A74 /* archive_read_support_filter_grzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_grzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B2248DA6EF009C7A74 /* archive_read_support_format_7zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_7zip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B3248DA6EF009C7A74 /* archive_read_support_format_lha.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_lha.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B4248DA6EF009C7A74 /* archive_write_add_filter_none.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_none.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B5248DA6EF009C7A74 /* archive_entry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_entry.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B6248DA6EF009C7A74 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B8248DA6EF009C7A74 /* archive_read_append_filter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_append_filter.c; sourceTree = \"<group>\"; };\n\t\tBB10E3B9248DA6EF009C7A74 /* archive_read_disk_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_disk_posix.c; sourceTree = \"<group>\"; };\n\t\tBB10E3BA248DA6EF009C7A74 /* archive_xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_xxhash.h; sourceTree = \"<group>\"; };\n\t\tBB10E3BB248DA6EF009C7A74 /* filter_fork_windows.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filter_fork_windows.c; sourceTree = \"<group>\"; };\n\t\tBB10E3BC248DA6EF009C7A74 /* archive_read_support_filter_lz4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_lz4.c; sourceTree = \"<group>\"; };\n\t\tBB10E3BD248DA6EF009C7A74 /* archive_write_set_format_zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_zip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3BE248DA6EF009C7A74 /* archive_write_set_format_xar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_xar.c; sourceTree = \"<group>\"; };\n\t\tBB10E3BF248DA6F0009C7A74 /* archive_read_disk_set_standard_lookup.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_disk_set_standard_lookup.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C0248DA6F0009C7A74 /* archive_write_add_filter_lrzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_lrzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C1248DA6F0009C7A74 /* archive_write_add_filter_xz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_xz.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C2248DA6F0009C7A74 /* archive_write_set_format_ar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_ar.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C3248DA6F0009C7A74 /* archive_write_set_format_raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_raw.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C4248DA6F0009C7A74 /* archive_acl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_acl.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C5248DA6F0009C7A74 /* archive_ppmd8_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_ppmd8_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3C6248DA6F0009C7A74 /* archive_windows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_windows.h; sourceTree = \"<group>\"; };\n\t\tBB10E3C7248DA6F0009C7A74 /* archive_read_support_format_rar5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_rar5.c; sourceTree = \"<group>\"; };\n\t\tBB10E3C8248DA6F0009C7A74 /* archive_openssl_evp_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_openssl_evp_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3CA248DA6F0009C7A74 /* archive_string_sprintf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_string_sprintf.c; sourceTree = \"<group>\"; };\n\t\tBB10E3CB248DA6F0009C7A74 /* filter_fork_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filter_fork_posix.c; sourceTree = \"<group>\"; };\n\t\tBB10E3CC248DA6F0009C7A74 /* archive_write_set_format_mtree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_mtree.c; sourceTree = \"<group>\"; };\n\t\tBB10E3CD248DA6F0009C7A74 /* archive_read_support_format_warc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_warc.c; sourceTree = \"<group>\"; };\n\t\tBB10E3CF248DA6F0009C7A74 /* archive_write_open_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_open_file.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D1248DA6F0009C7A74 /* archive_write_add_filter_gzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_gzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D2248DA6F0009C7A74 /* archive_getdate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_getdate.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D3248DA6F0009C7A74 /* archive_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3D4248DA6F0009C7A74 /* archive_check_magic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_check_magic.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D5248DA6F0009C7A74 /* archive_pack_dev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_pack_dev.h; sourceTree = \"<group>\"; };\n\t\tBB10E3D6248DA6F0009C7A74 /* archive_write_disk_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_disk_posix.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D7248DA6F0009C7A74 /* archive_write_add_filter_compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_compress.c; sourceTree = \"<group>\"; };\n\t\tBB10E3D9248DA6F0009C7A74 /* archive_read_extract2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_extract2.c; sourceTree = \"<group>\"; };\n\t\tBB10E3DA248DA6F0009C7A74 /* archive_read_support_format_cpio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_cpio.c; sourceTree = \"<group>\"; };\n\t\tBB10E3DB248DA6F0009C7A74 /* archive_string_composition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_string_composition.h; sourceTree = \"<group>\"; };\n\t\tBB10E3DD248DA6F0009C7A74 /* archive_read_support_format_iso9660.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_iso9660.c; sourceTree = \"<group>\"; };\n\t\tBB10E3DE248DA6F0009C7A74 /* archive_endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_endian.h; sourceTree = \"<group>\"; };\n\t\tBB10E3DF248DA6F0009C7A74 /* archive_write_add_filter_b64encode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_b64encode.c; sourceTree = \"<group>\"; };\n\t\tBB10E3E1248DA6F0009C7A74 /* archive_blake2sp_ref.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_blake2sp_ref.c; sourceTree = \"<group>\"; };\n\t\tBB10E3E4248DA6F0009C7A74 /* archive_read_set_options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_set_options.c; sourceTree = \"<group>\"; };\n\t\tBB10E3E6248DA6F0009C7A74 /* archive_read_support_format_xar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_xar.c; sourceTree = \"<group>\"; };\n\t\tBB10E3E7248DA6F0009C7A74 /* archive_write_open_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_open_memory.c; sourceTree = \"<group>\"; };\n\t\tBB10E3E9248DA6F0009C7A74 /* archive_read_support_filter_gzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_gzip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3EA248DA6F0009C7A74 /* archive_read_support_format_zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_zip.c; sourceTree = \"<group>\"; };\n\t\tBB10E3EC248DA6F0009C7A74 /* archive_read_support_format_raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_format_raw.c; sourceTree = \"<group>\"; };\n\t\tBB10E3ED248DA6F0009C7A74 /* archive_write_set_format_iso9660.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_format_iso9660.c; sourceTree = \"<group>\"; };\n\t\tBB10E3EE248DA6F0009C7A74 /* archive_disk_acl_linux.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_disk_acl_linux.c; sourceTree = \"<group>\"; };\n\t\tBB10E3EF248DA6F0009C7A74 /* archive_read_open_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_open_memory.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F0248DA6F0009C7A74 /* archive_write_set_options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_set_options.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F2248DA6F0009C7A74 /* archive_cmdline_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_cmdline_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3F3248DA6F0009C7A74 /* archive_string.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_string.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F4248DA6F0009C7A74 /* archive_cryptor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_cryptor_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3F5248DA6F0009C7A74 /* archive_read_data_into_fd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_data_into_fd.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F6248DA6F0009C7A74 /* archive_write_add_filter_bzip2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_write_add_filter_bzip2.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F7248DA6F0009C7A74 /* archive_pathmatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_pathmatch.h; sourceTree = \"<group>\"; };\n\t\tBB10E3F8248DA6F0009C7A74 /* archive_disk_acl_darwin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_disk_acl_darwin.c; sourceTree = \"<group>\"; };\n\t\tBB10E3F9248DA6F0009C7A74 /* archive_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_random.c; sourceTree = \"<group>\"; };\n\t\tBB10E3FA248DA6F0009C7A74 /* archive_ppmd8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_ppmd8.c; sourceTree = \"<group>\"; };\n\t\tBB10E3FB248DA6F0009C7A74 /* filter_fork.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter_fork.h; sourceTree = \"<group>\"; };\n\t\tBB10E3FC248DA6F0009C7A74 /* archive_rb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_rb.h; sourceTree = \"<group>\"; };\n\t\tBB10E3FD248DA6F0009C7A74 /* archive_write_disk_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_write_disk_private.h; sourceTree = \"<group>\"; };\n\t\tBB10E3FE248DA6F0009C7A74 /* archive_read_support_filter_none.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_support_filter_none.c; sourceTree = \"<group>\"; };\n\t\tBB10E400248DA6F0009C7A74 /* archive_read_disk_windows.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = archive_read_disk_windows.c; sourceTree = \"<group>\"; };\n\t\tBB10E401248DA6F0009C7A74 /* archive_platform_xattr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_platform_xattr.h; sourceTree = \"<group>\"; };\n\t\tBBCE66E3249A836200F45269 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = \"<group>\"; };\n\t\tBBCE66E7249A875400F45269 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tBB10E0D3248DA67B009C7A74 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBCE66E8249A875400F45269 /* libxml2.tbd in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tBB10E0BA248DA573009C7A74 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBBCE66E3249A836200F45269 /* config.h */,\n\t\t\t\tBB10E0E3248DA6EA009C7A74 /* libarchive */,\n\t\t\t\tBB10E0C9248DA627009C7A74 /* Products */,\n\t\t\t\tBBCE66E6249A875400F45269 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB10E0C9248DA627009C7A74 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB10E0D6248DA67B009C7A74 /* libarchive.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB10E0E3248DA6EA009C7A74 /* libarchive */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB10E0E4248DA6EA009C7A74 /* archive_read_support_format_rar.c */,\n\t\t\t\tBB10E0E6248DA6EA009C7A74 /* archive_read_support_format_empty.c */,\n\t\t\t\tBB10E0E7248DA6EA009C7A74 /* archive_blake2s_ref.c */,\n\t\t\t\tBB10E0E8248DA6EA009C7A74 /* archive_read_support_filter_zstd.c */,\n\t\t\t\tBB10E0EA248DA6EA009C7A74 /* archive_random_private.h */,\n\t\t\t\tBB10E0EB248DA6EA009C7A74 /* archive_write_set_format_7zip.c */,\n\t\t\t\tBB10E0ED248DA6EA009C7A74 /* config_freebsd.h */,\n\t\t\t\tBB10E0EE248DA6EA009C7A74 /* archive_write_disk_set_standard_lookup.c */,\n\t\t\t\tBB10E0EF248DA6EA009C7A74 /* archive_virtual.c */,\n\t\t\t\tBB10E0F0248DA6EA009C7A74 /* archive_read_support_format_ar.c */,\n\t\t\t\tBB10E0F1248DA6EA009C7A74 /* archive_read_support_filter_lrzip.c */,\n\t\t\t\tBB10E0F2248DA6EA009C7A74 /* archive_entry_copy_bhfi.c */,\n\t\t\t\tBB10E0F3248DA6EA009C7A74 /* archive_write_set_passphrase.c */,\n\t\t\t\tBB10E0F5248DA6EA009C7A74 /* archive_read_support_filter_compress.c */,\n\t\t\t\tBB10E0F6248DA6EA009C7A74 /* archive_util.c */,\n\t\t\t\tBB10E0F7248DA6EA009C7A74 /* archive_read_extract.c */,\n\t\t\t\tBB10E0F8248DA6EA009C7A74 /* archive.h */,\n\t\t\t\tBB10E0F9248DA6EA009C7A74 /* archive_write_add_filter_lzop.c */,\n\t\t\t\tBB10E0FB248DA6EA009C7A74 /* archive_entry.h */,\n\t\t\t\tBB10E0FC248DA6EA009C7A74 /* archive_write_add_filter_grzip.c */,\n\t\t\t\tBB10E0FD248DA6EA009C7A74 /* archive_write_set_format_by_name.c */,\n\t\t\t\tBB10E0FE248DA6EA009C7A74 /* archive_blake2.h */,\n\t\t\t\tBB10E101248DA6EA009C7A74 /* archive_entry_private.h */,\n\t\t\t\tBB10E103248DA6EA009C7A74 /* archive_cmdline.c */,\n\t\t\t\tBB10E104248DA6EA009C7A74 /* archive_read_open_file.c */,\n\t\t\t\tBB10E105248DA6EA009C7A74 /* archive_write_add_filter_program.c */,\n\t\t\t\tBB10E106248DA6EA009C7A74 /* archive_blake2_impl.h */,\n\t\t\t\tBB10E107248DA6EA009C7A74 /* archive_read_support_format_tar.c */,\n\t\t\t\tBB10E108248DA6EA009C7A74 /* archive_entry_stat.c */,\n\t\t\t\tBB10E109248DA6EA009C7A74 /* archive_entry_locale.h */,\n\t\t\t\tBB10E10A248DA6EA009C7A74 /* archive_pack_dev.c */,\n\t\t\t\tBB10E10B248DA6EA009C7A74 /* archive_write_set_format_warc.c */,\n\t\t\t\tBB10E10C248DA6EA009C7A74 /* archive_disk_acl_freebsd.c */,\n\t\t\t\tBB10E10F248DA6EA009C7A74 /* archive_entry_strmode.c */,\n\t\t\t\tBB10E110248DA6EA009C7A74 /* archive_read_disk_entry_from_file.c */,\n\t\t\t\tBB10E111248DA6EA009C7A74 /* archive_ppmd7.c */,\n\t\t\t\tBB10E112248DA6EA009C7A74 /* archive_options.c */,\n\t\t\t\tBB10E362248DA6EE009C7A74 /* archive_hmac.c */,\n\t\t\t\tBB10E363248DA6EE009C7A74 /* archive_digest_private.h */,\n\t\t\t\tBB10E365248DA6EE009C7A74 /* archive_getdate.h */,\n\t\t\t\tBB10E366248DA6EE009C7A74 /* archive_read_support_format_all.c */,\n\t\t\t\tBB10E367248DA6EE009C7A74 /* archive_read_open_filename.c */,\n\t\t\t\tBB10E368248DA6EE009C7A74 /* archive_write_set_format_cpio_newc.c */,\n\t\t\t\tBB10E369248DA6EE009C7A74 /* archive_entry_link_resolver.c */,\n\t\t\t\tBB10E36A248DA6EE009C7A74 /* archive_ppmd7_private.h */,\n\t\t\t\tBB10E36B248DA6EF009C7A74 /* archive_options_private.h */,\n\t\t\t\tBB10E36E248DA6EF009C7A74 /* archive_windows.c */,\n\t\t\t\tBB10E36F248DA6EF009C7A74 /* archive_write_set_format_gnutar.c */,\n\t\t\t\tBB10E370248DA6EF009C7A74 /* archive_write_set_format_filter_by_ext.c */,\n\t\t\t\tBB10E371248DA6EF009C7A74 /* archive_read_support_filter_uu.c */,\n\t\t\t\tBB10E372248DA6EF009C7A74 /* archive_write_set_format_ustar.c */,\n\t\t\t\tBB10E373248DA6EF009C7A74 /* archive_entry_sparse.c */,\n\t\t\t\tBB10E374248DA6EF009C7A74 /* archive_disk_acl_sunos.c */,\n\t\t\t\tBB10E375248DA6EF009C7A74 /* archive_read.c */,\n\t\t\t\tBB10E376248DA6EF009C7A74 /* archive_read_open_fd.c */,\n\t\t\t\tBB10E377248DA6EF009C7A74 /* archive_entry_copy_stat.c */,\n\t\t\t\tBB10E378248DA6EF009C7A74 /* archive_write_set_format_v7tar.c */,\n\t\t\t\tBB10E37B248DA6EF009C7A74 /* archive_platform.h */,\n\t\t\t\tBB10E37C248DA6EF009C7A74 /* archive_write_set_format_cpio.c */,\n\t\t\t\tBB10E37D248DA6EF009C7A74 /* archive_write_open_filename.c */,\n\t\t\t\tBB10E37E248DA6EF009C7A74 /* archive_write_private.h */,\n\t\t\t\tBB10E37F248DA6EF009C7A74 /* archive_ppmd_private.h */,\n\t\t\t\tBB10E380248DA6EF009C7A74 /* archive_acl_private.h */,\n\t\t\t\tBB10E381248DA6EF009C7A74 /* archive_write_add_filter_by_name.c */,\n\t\t\t\tBB10E382248DA6EF009C7A74 /* archive_hmac_private.h */,\n\t\t\t\tBB10E383248DA6EF009C7A74 /* archive_read_support_filter_xz.c */,\n\t\t\t\tBB10E384248DA6EF009C7A74 /* archive_read_support_filter_program.c */,\n\t\t\t\tBB10E386248DA6EF009C7A74 /* archive_write_set_format.c */,\n\t\t\t\tBB10E387248DA6EF009C7A74 /* archive_cryptor.c */,\n\t\t\t\tBB10E388248DA6EF009C7A74 /* archive_write_set_format_pax.c */,\n\t\t\t\tBB10E38A248DA6EF009C7A74 /* archive_read_support_format_mtree.c */,\n\t\t\t\tBB10E38B248DA6EF009C7A74 /* archive_digest.c */,\n\t\t\t\tBB10E38C248DA6EF009C7A74 /* archive_match.c */,\n\t\t\t\tBB10E38D248DA6EF009C7A74 /* archive_read_private.h */,\n\t\t\t\tBB10E38E248DA6EF009C7A74 /* archive_read_support_filter_rpm.c */,\n\t\t\t\tBB10E390248DA6EF009C7A74 /* archive_write_set_format_shar.c */,\n\t\t\t\tBB10E391248DA6EF009C7A74 /* archive_write_add_filter.c */,\n\t\t\t\tBB10E392248DA6EF009C7A74 /* archive_read_filter.3 */,\n\t\t\t\tBB10E393248DA6EF009C7A74 /* archive_version_details.c */,\n\t\t\t\tBB10E394248DA6EF009C7A74 /* archive_read_support_filter_lzop.c */,\n\t\t\t\tBB10E395248DA6EF009C7A74 /* archive_write_open_fd.c */,\n\t\t\t\tBB10E396248DA6EF009C7A74 /* archive_write_add_filter_lz4.c */,\n\t\t\t\tBB10E397248DA6EF009C7A74 /* archive_rb.c */,\n\t\t\t\tBB10E398248DA6EF009C7A74 /* archive_platform_acl.h */,\n\t\t\t\tBB10E399248DA6EF009C7A74 /* archive_read_support_format_by_code.c */,\n\t\t\t\tBB10E39A248DA6EF009C7A74 /* archive_openssl_hmac_private.h */,\n\t\t\t\tBB10E39C248DA6EF009C7A74 /* archive_read_set_format.c */,\n\t\t\t\tBB10E39D248DA6EF009C7A74 /* archive_read_disk_private.h */,\n\t\t\t\tBB10E39F248DA6EF009C7A74 /* archive_read_support_filter_bzip2.c */,\n\t\t\t\tBB10E3A0248DA6EF009C7A74 /* archive_entry_xattr.c */,\n\t\t\t\tBB10E3A1248DA6EF009C7A74 /* archive_write_add_filter_zstd.c */,\n\t\t\t\tBB10E3A3248DA6EF009C7A74 /* archive_string.h */,\n\t\t\t\tBB10E3A4248DA6EF009C7A74 /* archive_write_set_format_private.h */,\n\t\t\t\tBB10E3A5248DA6EF009C7A74 /* archive_pathmatch.c */,\n\t\t\t\tBB10E3A8248DA6EF009C7A74 /* archive_write_disk_windows.c */,\n\t\t\t\tBB10E3A9248DA6EF009C7A74 /* archive_crc32.h */,\n\t\t\t\tBB10E3AA248DA6EF009C7A74 /* archive_write.c */,\n\t\t\t\tBB10E3AB248DA6EF009C7A74 /* archive_read_add_passphrase.c */,\n\t\t\t\tBB10E3AD248DA6EF009C7A74 /* archive_write_add_filter_uuencode.c */,\n\t\t\t\tBB10E3AE248DA6EF009C7A74 /* archive_read_support_filter_all.c */,\n\t\t\t\tBB10E3AF248DA6EF009C7A74 /* archive_read_support_format_cab.c */,\n\t\t\t\tBB10E3B0248DA6EF009C7A74 /* archive_read_support_filter_grzip.c */,\n\t\t\t\tBB10E3B2248DA6EF009C7A74 /* archive_read_support_format_7zip.c */,\n\t\t\t\tBB10E3B3248DA6EF009C7A74 /* archive_read_support_format_lha.c */,\n\t\t\t\tBB10E3B4248DA6EF009C7A74 /* archive_write_add_filter_none.c */,\n\t\t\t\tBB10E3B5248DA6EF009C7A74 /* archive_entry.c */,\n\t\t\t\tBB10E3B6248DA6EF009C7A74 /* xxhash.c */,\n\t\t\t\tBB10E3B8248DA6EF009C7A74 /* archive_read_append_filter.c */,\n\t\t\t\tBB10E3B9248DA6EF009C7A74 /* archive_read_disk_posix.c */,\n\t\t\t\tBB10E3BA248DA6EF009C7A74 /* archive_xxhash.h */,\n\t\t\t\tBB10E3BB248DA6EF009C7A74 /* filter_fork_windows.c */,\n\t\t\t\tBB10E3BC248DA6EF009C7A74 /* archive_read_support_filter_lz4.c */,\n\t\t\t\tBB10E3BD248DA6EF009C7A74 /* archive_write_set_format_zip.c */,\n\t\t\t\tBB10E3BE248DA6EF009C7A74 /* archive_write_set_format_xar.c */,\n\t\t\t\tBB10E3BF248DA6F0009C7A74 /* archive_read_disk_set_standard_lookup.c */,\n\t\t\t\tBB10E3C0248DA6F0009C7A74 /* archive_write_add_filter_lrzip.c */,\n\t\t\t\tBB10E3C1248DA6F0009C7A74 /* archive_write_add_filter_xz.c */,\n\t\t\t\tBB10E3C2248DA6F0009C7A74 /* archive_write_set_format_ar.c */,\n\t\t\t\tBB10E3C3248DA6F0009C7A74 /* archive_write_set_format_raw.c */,\n\t\t\t\tBB10E3C4248DA6F0009C7A74 /* archive_acl.c */,\n\t\t\t\tBB10E3C5248DA6F0009C7A74 /* archive_ppmd8_private.h */,\n\t\t\t\tBB10E3C6248DA6F0009C7A74 /* archive_windows.h */,\n\t\t\t\tBB10E3C7248DA6F0009C7A74 /* archive_read_support_format_rar5.c */,\n\t\t\t\tBB10E3C8248DA6F0009C7A74 /* archive_openssl_evp_private.h */,\n\t\t\t\tBB10E3CA248DA6F0009C7A74 /* archive_string_sprintf.c */,\n\t\t\t\tBB10E3CB248DA6F0009C7A74 /* filter_fork_posix.c */,\n\t\t\t\tBB10E3CC248DA6F0009C7A74 /* archive_write_set_format_mtree.c */,\n\t\t\t\tBB10E3CD248DA6F0009C7A74 /* archive_read_support_format_warc.c */,\n\t\t\t\tBB10E3CF248DA6F0009C7A74 /* archive_write_open_file.c */,\n\t\t\t\tBB10E3D1248DA6F0009C7A74 /* archive_write_add_filter_gzip.c */,\n\t\t\t\tBB10E3D2248DA6F0009C7A74 /* archive_getdate.c */,\n\t\t\t\tBB10E3D3248DA6F0009C7A74 /* archive_private.h */,\n\t\t\t\tBB10E3D4248DA6F0009C7A74 /* archive_check_magic.c */,\n\t\t\t\tBB10E3D5248DA6F0009C7A74 /* archive_pack_dev.h */,\n\t\t\t\tBB10E3D6248DA6F0009C7A74 /* archive_write_disk_posix.c */,\n\t\t\t\tBB10E3D7248DA6F0009C7A74 /* archive_write_add_filter_compress.c */,\n\t\t\t\tBB10E3D9248DA6F0009C7A74 /* archive_read_extract2.c */,\n\t\t\t\tBB10E3DA248DA6F0009C7A74 /* archive_read_support_format_cpio.c */,\n\t\t\t\tBB10E3DB248DA6F0009C7A74 /* archive_string_composition.h */,\n\t\t\t\tBB10E3DD248DA6F0009C7A74 /* archive_read_support_format_iso9660.c */,\n\t\t\t\tBB10E3DE248DA6F0009C7A74 /* archive_endian.h */,\n\t\t\t\tBB10E3DF248DA6F0009C7A74 /* archive_write_add_filter_b64encode.c */,\n\t\t\t\tBB10E3E1248DA6F0009C7A74 /* archive_blake2sp_ref.c */,\n\t\t\t\tBB10E3E4248DA6F0009C7A74 /* archive_read_set_options.c */,\n\t\t\t\tBB10E3E6248DA6F0009C7A74 /* archive_read_support_format_xar.c */,\n\t\t\t\tBB10E3E7248DA6F0009C7A74 /* archive_write_open_memory.c */,\n\t\t\t\tBB10E3E9248DA6F0009C7A74 /* archive_read_support_filter_gzip.c */,\n\t\t\t\tBB10E3EA248DA6F0009C7A74 /* archive_read_support_format_zip.c */,\n\t\t\t\tBB10E3EC248DA6F0009C7A74 /* archive_read_support_format_raw.c */,\n\t\t\t\tBB10E3ED248DA6F0009C7A74 /* archive_write_set_format_iso9660.c */,\n\t\t\t\tBB10E3EE248DA6F0009C7A74 /* archive_disk_acl_linux.c */,\n\t\t\t\tBB10E3EF248DA6F0009C7A74 /* archive_read_open_memory.c */,\n\t\t\t\tBB10E3F0248DA6F0009C7A74 /* archive_write_set_options.c */,\n\t\t\t\tBB10E3F2248DA6F0009C7A74 /* archive_cmdline_private.h */,\n\t\t\t\tBB10E3F3248DA6F0009C7A74 /* archive_string.c */,\n\t\t\t\tBB10E3F4248DA6F0009C7A74 /* archive_cryptor_private.h */,\n\t\t\t\tBB10E3F5248DA6F0009C7A74 /* archive_read_data_into_fd.c */,\n\t\t\t\tBB10E3F6248DA6F0009C7A74 /* archive_write_add_filter_bzip2.c */,\n\t\t\t\tBB10E3F7248DA6F0009C7A74 /* archive_pathmatch.h */,\n\t\t\t\tBB10E3F8248DA6F0009C7A74 /* archive_disk_acl_darwin.c */,\n\t\t\t\tBB10E3F9248DA6F0009C7A74 /* archive_random.c */,\n\t\t\t\tBB10E3FA248DA6F0009C7A74 /* archive_ppmd8.c */,\n\t\t\t\tBB10E3FB248DA6F0009C7A74 /* filter_fork.h */,\n\t\t\t\tBB10E3FC248DA6F0009C7A74 /* archive_rb.h */,\n\t\t\t\tBB10E3FD248DA6F0009C7A74 /* archive_write_disk_private.h */,\n\t\t\t\tBB10E3FE248DA6F0009C7A74 /* archive_read_support_filter_none.c */,\n\t\t\t\tBB10E400248DA6F0009C7A74 /* archive_read_disk_windows.c */,\n\t\t\t\tBB10E401248DA6F0009C7A74 /* archive_platform_xattr.h */,\n\t\t\t);\n\t\t\tname = libarchive;\n\t\t\tpath = libarchive/libarchive;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBCE66E6249A875400F45269 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBBCE66E7249A875400F45269 /* libxml2.tbd */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tBB10E0D5248DA67B009C7A74 /* libarchive */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB10E0DC248DA67B009C7A74 /* Build configuration list for PBXNativeTarget \"libarchive\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB10E0D2248DA67B009C7A74 /* Sources */,\n\t\t\t\tBB10E0D3248DA67B009C7A74 /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = libarchive;\n\t\t\tproductName = archive;\n\t\t\tproductReference = BB10E0D6248DA67B009C7A74 /* libarchive.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tBB10E0BB248DA573009C7A74 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1330;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tBB10E0D5248DA67B009C7A74 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.5;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = BB10E0BE248DA573009C7A74 /* Build configuration list for PBXProject \"libarchive\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = BB10E0BA248DA573009C7A74;\n\t\t\tproductRefGroup = BB10E0C9248DA627009C7A74 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tBB10E0D5248DA67B009C7A74 /* libarchive */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tBB10E0D2248DA67B009C7A74 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB10E576248DA6F4009C7A74 /* archive_read_add_passphrase.c in Sources */,\n\t\t\t\tBB10E57E248DA6F4009C7A74 /* archive_entry.c in Sources */,\n\t\t\t\tBB10E563248DA6F4009C7A74 /* archive_version_details.c in Sources */,\n\t\t\t\tBB10E596248DA6F5009C7A74 /* archive_write_add_filter_gzip.c in Sources */,\n\t\t\t\tBB10E59E248DA6F5009C7A74 /* archive_read_support_format_cpio.c in Sources */,\n\t\t\t\tBB10E58A248DA6F5009C7A74 /* archive_write_set_format_ar.c in Sources */,\n\t\t\t\tBB10E57C248DA6F4009C7A74 /* archive_read_support_format_lha.c in Sources */,\n\t\t\t\tBB10E560248DA6F4009C7A74 /* archive_read_support_filter_rpm.c in Sources */,\n\t\t\t\tBB10E5A3248DA6F5009C7A74 /* archive_blake2sp_ref.c in Sources */,\n\t\t\t\tBB10E555248DA6F4009C7A74 /* archive_write_add_filter_by_name.c in Sources */,\n\t\t\t\tBB10E5A0248DA6F5009C7A74 /* archive_read_support_format_iso9660.c in Sources */,\n\t\t\t\tBB10E40C248DA6F1009C7A74 /* archive_read_support_format_ar.c in Sources */,\n\t\t\t\tBB10E40F248DA6F1009C7A74 /* archive_write_set_passphrase.c in Sources */,\n\t\t\t\tBB10E40B248DA6F1009C7A74 /* archive_virtual.c in Sources */,\n\t\t\t\tBB10E5B5248DA6F5009C7A74 /* archive_random.c in Sources */,\n\t\t\t\tBB10E59D248DA6F5009C7A74 /* archive_read_extract2.c in Sources */,\n\t\t\t\tBB10E54A248DA6F4009C7A74 /* archive_disk_acl_sunos.c in Sources */,\n\t\t\t\tBB10E408248DA6F1009C7A74 /* archive_write_set_format_7zip.c in Sources */,\n\t\t\t\tBB10E547248DA6F4009C7A74 /* archive_read_support_filter_uu.c in Sources */,\n\t\t\t\tBB10E585248DA6F5009C7A74 /* archive_write_set_format_zip.c in Sources */,\n\t\t\t\tBB10E423248DA6F1009C7A74 /* archive_disk_acl_freebsd.c in Sources */,\n\t\t\t\tBB10E584248DA6F5009C7A74 /* archive_read_support_filter_lz4.c in Sources */,\n\t\t\t\tBB10E5A5248DA6F5009C7A74 /* archive_read_support_format_xar.c in Sources */,\n\t\t\t\tBB10E55D248DA6F4009C7A74 /* archive_digest.c in Sources */,\n\t\t\t\tBB10E57F248DA6F4009C7A74 /* xxhash.c in Sources */,\n\t\t\t\tBB10E41E248DA6F1009C7A74 /* archive_read_support_format_tar.c in Sources */,\n\t\t\t\tBB10E414248DA6F1009C7A74 /* archive_write_add_filter_lzop.c in Sources */,\n\t\t\t\tBB10E579248DA6F4009C7A74 /* archive_read_support_format_cab.c in Sources */,\n\t\t\t\tBB10E599248DA6F5009C7A74 /* archive_check_magic.c in Sources */,\n\t\t\t\tBB10E573248DA6F4009C7A74 /* archive_write_disk_windows.c in Sources */,\n\t\t\t\tBB10E557248DA6F4009C7A74 /* archive_read_support_filter_xz.c in Sources */,\n\t\t\t\tBB10E427248DA6F1009C7A74 /* archive_options.c in Sources */,\n\t\t\t\tBB10E597248DA6F5009C7A74 /* archive_getdate.c in Sources */,\n\t\t\t\tBB10E5A2248DA6F5009C7A74 /* archive_write_add_filter_b64encode.c in Sources */,\n\t\t\t\tBB10E40A248DA6F1009C7A74 /* archive_write_disk_set_standard_lookup.c in Sources */,\n\t\t\t\tBB10E595248DA6F5009C7A74 /* archive_write_open_file.c in Sources */,\n\t\t\t\tBB10E40D248DA6F1009C7A74 /* archive_read_support_filter_lrzip.c in Sources */,\n\t\t\t\tBB10E580248DA6F4009C7A74 /* archive_read_append_filter.c in Sources */,\n\t\t\t\tBB10E412248DA6F1009C7A74 /* archive_read_extract.c in Sources */,\n\t\t\t\tBB10E55E248DA6F4009C7A74 /* archive_match.c in Sources */,\n\t\t\t\tBB10E57A248DA6F4009C7A74 /* archive_read_support_filter_grzip.c in Sources */,\n\t\t\t\tBB10E567248DA6F4009C7A74 /* archive_rb.c in Sources */,\n\t\t\t\tBB10E5A6248DA6F5009C7A74 /* archive_write_open_memory.c in Sources */,\n\t\t\t\tBB10E541248DA6F4009C7A74 /* archive_entry_link_resolver.c in Sources */,\n\t\t\t\tBB10E5AD248DA6F5009C7A74 /* archive_write_set_options.c in Sources */,\n\t\t\t\tBB10E591248DA6F5009C7A74 /* archive_string_sprintf.c in Sources */,\n\t\t\t\tBB10E41F248DA6F1009C7A74 /* archive_entry_stat.c in Sources */,\n\t\t\t\tBB10E565248DA6F4009C7A74 /* archive_write_open_fd.c in Sources */,\n\t\t\t\tBB10E5B6248DA6F5009C7A74 /* archive_ppmd8.c in Sources */,\n\t\t\t\tBB10E581248DA6F4009C7A74 /* archive_read_disk_posix.c in Sources */,\n\t\t\t\tBB10E40E248DA6F1009C7A74 /* archive_entry_copy_bhfi.c in Sources */,\n\t\t\t\tBB10E422248DA6F1009C7A74 /* archive_write_set_format_warc.c in Sources */,\n\t\t\t\tBB10E589248DA6F5009C7A74 /* archive_write_add_filter_xz.c in Sources */,\n\t\t\t\tBB10E5B4248DA6F5009C7A74 /* archive_disk_acl_darwin.c in Sources */,\n\t\t\t\tBB10E405248DA6F1009C7A74 /* archive_blake2s_ref.c in Sources */,\n\t\t\t\tBB10E56B248DA6F4009C7A74 /* archive_read_set_format.c in Sources */,\n\t\t\t\tBB10E5BA248DA6F5009C7A74 /* archive_read_support_filter_none.c in Sources */,\n\t\t\t\tBB10E5A7248DA6F5009C7A74 /* archive_read_support_filter_gzip.c in Sources */,\n\t\t\t\tBB10E57B248DA6F4009C7A74 /* archive_read_support_format_7zip.c in Sources */,\n\t\t\t\tBB10E416248DA6F1009C7A74 /* archive_write_add_filter_grzip.c in Sources */,\n\t\t\t\tBB10E410248DA6F1009C7A74 /* archive_read_support_filter_compress.c in Sources */,\n\t\t\t\tBB10E54E248DA6F4009C7A74 /* archive_write_set_format_v7tar.c in Sources */,\n\t\t\t\tBB10E549248DA6F4009C7A74 /* archive_entry_sparse.c in Sources */,\n\t\t\t\tBB10E406248DA6F1009C7A74 /* archive_read_support_filter_zstd.c in Sources */,\n\t\t\t\tBB10E592248DA6F5009C7A74 /* filter_fork_posix.c in Sources */,\n\t\t\t\tBB10E564248DA6F4009C7A74 /* archive_read_support_filter_lzop.c in Sources */,\n\t\t\t\tBB10E54C248DA6F4009C7A74 /* archive_read_open_fd.c in Sources */,\n\t\t\t\tBB10E5AB248DA6F5009C7A74 /* archive_disk_acl_linux.c in Sources */,\n\t\t\t\tBB10E5BB248DA6F5009C7A74 /* archive_read_disk_windows.c in Sources */,\n\t\t\t\tBB10E5A8248DA6F5009C7A74 /* archive_read_support_format_zip.c in Sources */,\n\t\t\t\tBB10E426248DA6F1009C7A74 /* archive_ppmd7.c in Sources */,\n\t\t\t\tBB10E5A4248DA6F5009C7A74 /* archive_read_set_options.c in Sources */,\n\t\t\t\tBB10E56F248DA6F4009C7A74 /* archive_write_add_filter_zstd.c in Sources */,\n\t\t\t\tBB10E411248DA6F1009C7A74 /* archive_util.c in Sources */,\n\t\t\t\tBB10E540248DA6F4009C7A74 /* archive_write_set_format_cpio_newc.c in Sources */,\n\t\t\t\tBB10E559248DA6F4009C7A74 /* archive_write_set_format.c in Sources */,\n\t\t\t\tBB10E54B248DA6F4009C7A74 /* archive_read.c in Sources */,\n\t\t\t\tBB10E578248DA6F4009C7A74 /* archive_read_support_filter_all.c in Sources */,\n\t\t\t\tBB10E5B1248DA6F5009C7A74 /* archive_read_data_into_fd.c in Sources */,\n\t\t\t\tBB10E546248DA6F4009C7A74 /* archive_write_set_format_filter_by_ext.c in Sources */,\n\t\t\t\tBB10E55B248DA6F4009C7A74 /* archive_write_set_format_pax.c in Sources */,\n\t\t\t\tBB10E417248DA6F1009C7A74 /* archive_write_set_format_by_name.c in Sources */,\n\t\t\t\tBB10E593248DA6F5009C7A74 /* archive_write_set_format_mtree.c in Sources */,\n\t\t\t\tBB10E41C248DA6F1009C7A74 /* archive_write_add_filter_program.c in Sources */,\n\t\t\t\tBB10E5AF248DA6F5009C7A74 /* archive_string.c in Sources */,\n\t\t\t\tBB10E545248DA6F4009C7A74 /* archive_write_set_format_gnutar.c in Sources */,\n\t\t\t\tBB10E548248DA6F4009C7A74 /* archive_write_set_format_ustar.c in Sources */,\n\t\t\t\tBB10E5B2248DA6F5009C7A74 /* archive_write_add_filter_bzip2.c in Sources */,\n\t\t\t\tBB10E569248DA6F4009C7A74 /* archive_read_support_format_by_code.c in Sources */,\n\t\t\t\tBB10E5AA248DA6F5009C7A74 /* archive_write_set_format_iso9660.c in Sources */,\n\t\t\t\tBB10E588248DA6F5009C7A74 /* archive_write_add_filter_lrzip.c in Sources */,\n\t\t\t\tBB10E594248DA6F5009C7A74 /* archive_read_support_format_warc.c in Sources */,\n\t\t\t\tBB10E586248DA6F5009C7A74 /* archive_write_set_format_xar.c in Sources */,\n\t\t\t\tBB10E41A248DA6F1009C7A74 /* archive_cmdline.c in Sources */,\n\t\t\t\tBB10E572248DA6F4009C7A74 /* archive_pathmatch.c in Sources */,\n\t\t\t\tBB10E421248DA6F1009C7A74 /* archive_pack_dev.c in Sources */,\n\t\t\t\tBB10E5AC248DA6F5009C7A74 /* archive_read_open_memory.c in Sources */,\n\t\t\t\tBB10E587248DA6F5009C7A74 /* archive_read_disk_set_standard_lookup.c in Sources */,\n\t\t\t\tBB10E561248DA6F4009C7A74 /* archive_write_set_format_shar.c in Sources */,\n\t\t\t\tBB10E562248DA6F4009C7A74 /* archive_write_add_filter.c in Sources */,\n\t\t\t\tBB10E566248DA6F4009C7A74 /* archive_write_add_filter_lz4.c in Sources */,\n\t\t\t\tBB10E424248DA6F1009C7A74 /* archive_entry_strmode.c in Sources */,\n\t\t\t\tBB10E53F248DA6F4009C7A74 /* archive_read_open_filename.c in Sources */,\n\t\t\t\tBB10E403248DA6F1009C7A74 /* archive_read_support_format_rar.c in Sources */,\n\t\t\t\tBB10E583248DA6F5009C7A74 /* filter_fork_windows.c in Sources */,\n\t\t\t\tBB10E425248DA6F1009C7A74 /* archive_read_disk_entry_from_file.c in Sources */,\n\t\t\t\tBB10E53B248DA6F4009C7A74 /* archive_hmac.c in Sources */,\n\t\t\t\tBB10E56D248DA6F4009C7A74 /* archive_read_support_filter_bzip2.c in Sources */,\n\t\t\t\tBB10E55C248DA6F4009C7A74 /* archive_read_support_format_mtree.c in Sources */,\n\t\t\t\tBB10E41B248DA6F1009C7A74 /* archive_read_open_file.c in Sources */,\n\t\t\t\tBB10E551248DA6F4009C7A74 /* archive_write_open_filename.c in Sources */,\n\t\t\t\tBB10E5A9248DA6F5009C7A74 /* archive_read_support_format_raw.c in Sources */,\n\t\t\t\tBB10E58C248DA6F5009C7A74 /* archive_acl.c in Sources */,\n\t\t\t\tBB10E57D248DA6F4009C7A74 /* archive_write_add_filter_none.c in Sources */,\n\t\t\t\tBB10E53E248DA6F4009C7A74 /* archive_read_support_format_all.c in Sources */,\n\t\t\t\tBB10E404248DA6F1009C7A74 /* archive_read_support_format_empty.c in Sources */,\n\t\t\t\tBB10E55A248DA6F4009C7A74 /* archive_cryptor.c in Sources */,\n\t\t\t\tBB10E577248DA6F4009C7A74 /* archive_write_add_filter_uuencode.c in Sources */,\n\t\t\t\tBB10E558248DA6F4009C7A74 /* archive_read_support_filter_program.c in Sources */,\n\t\t\t\tBB10E56E248DA6F4009C7A74 /* archive_entry_xattr.c in Sources */,\n\t\t\t\tBB10E58F248DA6F5009C7A74 /* archive_read_support_format_rar5.c in Sources */,\n\t\t\t\tBB10E544248DA6F4009C7A74 /* archive_windows.c in Sources */,\n\t\t\t\tBB10E59B248DA6F5009C7A74 /* archive_write_disk_posix.c in Sources */,\n\t\t\t\tBB10E59C248DA6F5009C7A74 /* archive_write_add_filter_compress.c in Sources */,\n\t\t\t\tBB10E54D248DA6F4009C7A74 /* archive_entry_copy_stat.c in Sources */,\n\t\t\t\tBB10E575248DA6F4009C7A74 /* archive_write.c in Sources */,\n\t\t\t\tBB10E550248DA6F4009C7A74 /* archive_write_set_format_cpio.c in Sources */,\n\t\t\t\tBB10E58B248DA6F5009C7A74 /* archive_write_set_format_raw.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tBB10E0BF248DA573009C7A74 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBB10E0C0248DA573009C7A74 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB10E0DD248DA67B009C7A74 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = HAVE_CONFIG_H;\n\t\t\t\tGCC_WARN_INHIBIT_ALL_WARNINGS = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = \"$(SRCROOT)/libarchive\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = archive;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBB10E0DE248DA67B009C7A74 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = HAVE_CONFIG_H;\n\t\t\t\tGCC_WARN_INHIBIT_ALL_WARNINGS = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = \"$(SRCROOT)/libarchive\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = archive;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tBB10E0BE248DA573009C7A74 /* Build configuration list for PBXProject \"libarchive\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB10E0BF248DA573009C7A74 /* Debug */,\n\t\t\t\tBB10E0C0248DA573009C7A74 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB10E0DC248DA67B009C7A74 /* Build configuration list for PBXNativeTarget \"libarchive\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB10E0DD248DA67B009C7A74 /* Debug */,\n\t\t\t\tBB10E0DE248DA67B009C7A74 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = BB10E0BB248DA573009C7A74 /* Project object */;\n}\n"
  },
  {
    "path": "deps/libarchive.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:/Users/tbodt/Developer/projects/ish/deps/libarchive-asdfasdfasdf/libarchive.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "deps/libarchive.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "deps/linux-build.sh",
    "content": "#!/bin/bash -e\nset -o pipefail\noutput=\"$1\"\nsrctree=\"$2\"\nobjtree=\"$3\"\ndepfile=\"$4\"\n\n# https://stackoverflow.com/a/3572105/1455016\nrealpath() {\n    [[ $1 = /* ]] && echo \"$1\" || echo \"$PWD/${1#./}\"\n}\n\nmakeargs=()\nmakeargs+=(\"ARCH=ish\")\nif [[ -n \"$HOSTCC\" ]]; then\n    makeargs+=(\"HOSTCC=$HOSTCC\")\nfi\nif [[ -n \"$CC\" ]]; then\n    makeargs+=(\"CC=$CC\")\nfi\nmakeargs+=(\"LLVM_IAS=1\")\n\nmkdir -p \"$objtree\"\n\ndefconfig=\"$srctree/arch/ish/configs/ish_defconfig\"\nfor fragment in \"$defconfig\" $KCONFIG_FRAGMENTS; do\n    if [[ \"$fragment\" -nt \"$objtree/.config\" ]]; then\n        regen_config=1\n    fi\ndone\nif [[ -n \"$regen_config\" ]]; then\n    export KCONFIG_CONFIG=\"$objtree/.config\"\n    \"$srctree/scripts/kconfig/merge_config.sh\" -m \"$srctree/arch/ish/configs/ish_defconfig\" $KCONFIG_FRAGMENTS\n    unset KCONFIG_CONFIG\nfi\n\ncase \"$(uname)\" in\n    Darwin) cpus=$(sysctl -n hw.ncpu) ;;\n    Linux) cpus=$(nproc) ;;\n    *) cpus=1 ;;\nesac\n\nmake -C \"$srctree\" O=\"$(realpath \"$objtree\")\" \"${makeargs[@]}\" olddefconfig\n\n(set -x; make -C \"$objtree\" -j \"$cpus\" \"${makeargs[@]}\" all compile_commands.json --debug=v) | \"$srctree/../makefilter.py\" \"$depfile\" \"$output\" \"$objtree\"\ncp \"$objtree/vmlinux\" \"$output\""
  },
  {
    "path": "deps/linux-sparse.txt",
    "content": "/*\nMakefile\nKbuild\nKconfig*\n\n!/arch/\n/arch/ish/\n/arch/x86/include/asm/\n/arch/x86/include/uapi/\n/arch/x86/entry/syscalls/\n/arch/x86/kernel/sys_ia32.c\n/arch/um/scripts/Makefile.rules\n\n!/drivers/\n/drivers/block/\n/drivers/char/\n/drivers/tty/\n!/drivers/tty/serial\n/drivers/base/\n/drivers/net/Space.c\n/drivers/net/loopback.c\n/drivers/net/veth.c\n\n!/fs/*/\n/fs/xfs/xfs_sysctl.h\n/fs/squashfs/squashfs_fs.h\n/fs/iomap/\n/fs/kernfs/\n/fs/proc/\n/fs/sysfs/\n/fs/devpts/\n/fs/notify/\n/fs/ext4/\n/fs/jbd2/\n/fs/ramfs/\n/fs/overlayfs/\n/fs/hostfs/\n/fs/exportfs/\n\n!/net/*/\n/net/core/\n/net/llc/\n/net/ethernet/\n/net/802/\n/net/sched/\n/net/netlink/\n/net/ethtool/\n/net/netfilter/\n/net/ipv4/\n/net/unix/\n/net/ipv6/\n/net/packet/\n/net/bridge/\n\n!/include/linux/*/\n/include/linux/atomic/\n/include/linux/sched/\n/include/linux/device/\n/include/linux/byteorder/\n/include/linux/netfilter/\n/include/linux/decompress/\n/include/linux/gpio/\n/include/linux/sunrpc/\n/include/linux/unaligned/\n/include/linux/pinctrl/\n/include/linux/raid/\n/include/linux/clk/\n/include/linux/netfilter_ipv4/\n/include/linux/platform_data/dsa.h\n!/include/dt-bindings\n!/include/sound\n\n!/sound\n!/tools\n!/Documentation\n\n!/include/uapi/linux/netfilter/xt_connmark.h\n!/include/uapi/linux/netfilter/xt_dscp.h\n!/include/uapi/linux/netfilter/xt_MARK.h\n!/include/uapi/linux/netfilter/xt_tcpmss.h\n!/include/uapi/linux/netfilter/xt_rateest.h\n!/include/uapi/linux/netfilter_ipv4/ipt_ecn.h\n!/include/uapi/linux/netfilter_ipv4/ipt_ttl.h\n!/include/uapi/linux/netfilter_ipv6/ip6t_hl.h\n!/net/netfilter/xt_dscp.c\n!/net/netfilter/xt_hl.c\n!/net/netfilter/xt_rateest.c\n!/net/netfilter/xt_tcpmss.c\n!/tools/memory-model/litmus-tests/Z6.0+pooncelock+pooncelock+pombonce.litmus\n"
  },
  {
    "path": "deps/linux.config",
    "content": "CONFIG_BLK_DEV_INITRD=n\nCONFIG_ISH_LINK_OBJECT=y\nCONFIG_KALLSYMS=y\nCONFIG_DEBUG_INFO=y\nCONFIG_IP_PNP=n\nCONFIG_STDIO_CONSOLE=n\n"
  },
  {
    "path": "deps/makefilter.py",
    "content": "#!/usr/bin/env -S python3 -u\nimport sys\nimport re\nimport os\nline_re = re.compile(r'^  [A-Z]+\\s+')\ndep_re = [\n\tre.compile(r\"\\s+Prerequisite [`']([^']*)' is (older|newer) than target\"),\n\tre.compile(r\"\\s+No need to remake target [`'][^']*'; using VPATH name `([^']*)'\"),\n\tre.compile(r\"\\s+No need to remake target [`']([^']*)'.\"),\n]\ndeps = set()\nfor line in sys.stdin:\n\tif line_re.match(line):\n\t\tsys.stdout.write(line)\n\tif line.endswith('\\n'):\n\t\tline = line[:-1]\n\tfor re in dep_re:\n\t\tm = re.match(line)\n\t\tif m:\n\t\t\tdeps.add(m.group(1))\n\t\t\tbreak\ndeps = [os.path.realpath(os.path.join(sys.argv[3], dep)) for dep in sorted(deps)]\ndeps = [dep for dep in deps if os.path.exists(dep)]\nwith open(sys.argv[1], 'w') as f:\n\tprint(f'{sys.argv[2]}: \\\\', file=f)\n\tfor dep in deps:\n\t\tprint(f'{dep} \\\\', file=f)\n"
  },
  {
    "path": "deps/meson.build",
    "content": "if get_option('kernel') == 'linux'\n    kconfig = []\n\n    if get_option('engine') in ['asbestos', 'unicorn']\n        kconfig += 'CONFIG_ISH_EMULATOR_NONE=y'\n    else\n        error('Engine \"' + get_option('engine') + '\" not supported for linux kernel')\n    endif\n\n    kconfig += get_option('kconfig')\n    meson_fragment = configure_file(\n        command: [\n            find_program('kconfig-fragment.sh'),\n            '@OUTPUT@',\n        ] + kconfig,\n        output: 'linux-meson.config',\n    )\n\n    build_linux = custom_target('linux',\n        command: [\n            'env',\n            'HOSTCC='+ ' '.join(meson.get_compiler('c', native: true).cmd_array() + get_option('build.c_args')),\n            'CC='+ ' '.join(meson.get_compiler('c', native: false).cmd_array() + get_option('c_args')),\n            # hack to find the right directories because meson is bad\n            'ISH_SRC='+ meson.project_source_root(),\n            'ISH_BUILD='+ meson.project_build_root(),\n            'KCONFIG_FRAGMENTS='+ ' '.join([\n                meson.project_source_root() / 'deps/linux.config',\n                meson.project_build_root() / 'deps/linux-meson.config',\n            ]),\n            find_program('linux-build.sh'),\n            '@OUTPUT0@',\n            '@SOURCE_ROOT@/deps/linux',\n            '@OUTPUT1@',\n            '@DEPFILE@',\n        ],\n        depend_files: [\n            'makefilter.py',\n            'linux.config',\n            meson_fragment,\n        ],\n        console: true,\n        depfile: 'linux.d',\n        output: ['liblinux.a', 'linux'])\n\n    # Create empty include directories so the liblinux declaration doesn't throw File Not Found\n    run_command('mkdir', [\n        '-p',\n        meson.current_build_dir()+'/linux/include/generated/uapi',\n        meson.current_build_dir()+'/linux/arch/ish/include/generated/uapi'],\n        check: false)\n\n    linux_headers = declare_dependency(\n        include_directories: [include_directories(\n            'linux/arch/ish/include',\n            'linux/arch/ish/include/generated',\n            'linux/include',\n            'linux/arch/ish/include/uapi',\n            'linux/arch/ish/include/generated/uapi',\n            'linux/include/uapi',\n            'linux/include/generated/uapi',\n        )] + includes,\n        compile_args: [\n            # '-nostdlibinc',\n            '-include', 'linux/compiler_attributes.h',\n            '-include', 'linux/kconfig.h',\n            '-D__KERNEL__', '-U__weak',\n            # Warnings triggered by kernel headers. Some of these\n            # warn of UB, but I feel all right with silencing them\n            # because if clang does something to break Linux\n            # there'll be hell to pay.\n            '-Wno-unused-parameter',\n            '-Wno-sign-compare',\n            '-Wno-implicit-fallthrough',\n            '-Wno-pointer-sign',\n            '-Wno-null-pointer-arithmetic',\n        ],\n        link_with: [build_linux[0]],\n        sources: [build_linux[1]],\n    )\n\n    user_linux_headers = declare_dependency(\n        include_directories: include_directories(\n            'linux/arch/ish/include',\n            'linux/include',\n        ),\n        compile_args: [\n            '-include', 'user.h',\n            '-include', 'linux/kconfig.h',\n        ],\n        sources: [build_linux[1]],\n    )\n\n    if host_machine.system() == 'darwin'\n        liblinux_link_args = [\n            '-Wl,-ld_classic',\n            '-sectalign', '__DATA', '__percpu_first', '1000',\n            '-sectalign', '__DATA', '__tracepoints', '20',\n        ]\n    elif host_machine.system() == 'linux'\n        liblinux_link_args = [\n            '-Wl,-T', meson.current_build_dir()+'/linux/arch/ish/kernel/vmlinux.lds'\n        ]\n    endif\n    liblinux = declare_dependency(\n        link_whole: [build_linux[0]],\n        sources: [build_linux[1]],\n        link_args: liblinux_link_args,\n    )\nendif\n"
  },
  {
    "path": "emu/cpu.h",
    "content": "#ifndef EMU_H\n#define EMU_H\n\n#include \"misc.h\"\n#include \"emu/mmu.h\"\n#include \"emu/float80.h\"\n\n#ifdef __KERNEL__\n#include <linux/stddef.h>\n#else\n#include <stddef.h>\n#endif\n\nstruct cpu_state;\nstruct tlb;\nint cpu_run_to_interrupt(struct cpu_state *cpu, struct tlb *tlb);\nvoid cpu_poke(struct cpu_state *cpu);\n\nunion mm_reg {\n    qword_t qw;\n    dword_t dw[2];\n};\nunion xmm_reg {\n    unsigned __int128 u128;\n    qword_t qw[2];\n    uint32_t u32[4];\n    uint16_t u16[8];\n    uint8_t u8[16];\n    float f32[4];\n    double f64[2];\n};\nstatic_assert(sizeof(union xmm_reg) == 16, \"xmm_reg size\");\nstatic_assert(sizeof(union mm_reg) == 8, \"mm_reg size\");\n\nstruct cpu_state {\n    struct mmu *mmu;\n    long cycle;\n\n    // general registers\n    // assumes little endian (as does literally everything)\n#define _REG(n) \\\n    union { \\\n        dword_t e##n; \\\n        word_t n; \\\n    }\n#define _REGX(n) \\\n    union { \\\n        dword_t e##n##x; \\\n        word_t n##x; \\\n        struct { \\\n            byte_t n##l; \\\n            byte_t n##h; \\\n        }; \\\n    }\n\n    union {\n        struct {\n            _REGX(a);\n            _REGX(c);\n            _REGX(d);\n            _REGX(b);\n            _REG(sp);\n            _REG(bp);\n            _REG(si);\n            _REG(di);\n        };\n        dword_t regs[8];\n    };\n#undef REGX\n#undef REG\n\n    dword_t eip;\n\n    // flags\n    union {\n        dword_t eflags;\n        struct {\n            bitfield cf_bit:1;\n            bitfield pad1_1:1;\n            bitfield pf:1;\n            bitfield pad2_0:1;\n            bitfield af:1;\n            bitfield pad3_0:1;\n            bitfield zf:1;\n            bitfield sf:1;\n            bitfield tf:1;\n            bitfield if_:1;\n            bitfield df:1;\n            bitfield of_bit:1;\n            bitfield iopl:2;\n        };\n        // for asm\n#define PF_FLAG (1 << 2)\n#define AF_FLAG (1 << 4)\n#define ZF_FLAG (1 << 6)\n#define SF_FLAG (1 << 7)\n#define DF_FLAG (1 << 10)\n    };\n    // please pretend this doesn't exist\n    dword_t df_offset;\n    // for maximum efficiency these are stored in bytes\n    byte_t cf;\n    byte_t of;\n    // whether the true flag values are in the above struct, or computed from\n    // the stored result and operands\n    dword_t res, op1, op2;\n    union {\n        struct {\n            bitfield pf_res:1;\n            bitfield zf_res:1;\n            bitfield sf_res:1;\n            bitfield af_ops:1;\n        };\n        // for asm\n#define PF_RES (1 << 0)\n#define ZF_RES (1 << 1)\n#define SF_RES (1 << 2)\n#define AF_OPS (1 << 3)\n        byte_t flags_res;\n    };\n\n    union mm_reg mm[8];\n    union xmm_reg xmm[8];\n\n    // fpu\n    float80 fp[8];\n    union {\n        word_t fsw;\n        struct {\n            bitfield ie:1; // invalid operation\n            bitfield de:1; // denormalized operand\n            bitfield ze:1; // divide by zero\n            bitfield oe:1; // overflow\n            bitfield ue:1; // underflow\n            bitfield pe:1; // precision\n            bitfield stf:1; // stack fault\n            bitfield es:1; // exception status\n            bitfield c0:1;\n            bitfield c1:1;\n            bitfield c2:1;\n            unsigned top:3;\n            bitfield c3:1;\n            bitfield b:1; // fpu busy (?)\n        };\n    };\n    union {\n        word_t fcw;\n        struct {\n            bitfield im:1;\n            bitfield dm:1;\n            bitfield zm:1;\n            bitfield om:1;\n            bitfield um:1;\n            bitfield pm:1;\n            bitfield pad4:2;\n            bitfield pc:2;\n            bitfield rc:2;\n            bitfield y:1;\n        };\n    };\n\n    // TLS bullshit\n    word_t gs;\n    addr_t tls_ptr;\n\n    // for the page fault handler\n    addr_t segfault_addr;\n    bool segfault_was_write;\n\n    dword_t trapno;\n    // access atomically\n    bool *poked_ptr;\n    bool _poked;\n};\n\n#define CPU_OFFSET(field) offsetof(struct cpu_state, field)\n\nstatic_assert(CPU_OFFSET(eax) == CPU_OFFSET(regs[0]), \"register order\");\nstatic_assert(CPU_OFFSET(ecx) == CPU_OFFSET(regs[1]), \"register order\");\nstatic_assert(CPU_OFFSET(edx) == CPU_OFFSET(regs[2]), \"register order\");\nstatic_assert(CPU_OFFSET(ebx) == CPU_OFFSET(regs[3]), \"register order\");\nstatic_assert(CPU_OFFSET(esp) == CPU_OFFSET(regs[4]), \"register order\");\nstatic_assert(CPU_OFFSET(ebp) == CPU_OFFSET(regs[5]), \"register order\");\nstatic_assert(CPU_OFFSET(esi) == CPU_OFFSET(regs[6]), \"register order\");\nstatic_assert(CPU_OFFSET(edi) == CPU_OFFSET(regs[7]), \"register order\");\nstatic_assert(sizeof(struct cpu_state) < 0xffff, \"cpu struct is too big for vector gadgets\");\n\n// flags\n#define ZF (cpu->zf_res ? cpu->res == 0 : cpu->zf)\n#define SF (cpu->sf_res ? (int32_t) cpu->res < 0 : cpu->sf)\n#define CF (cpu->cf)\n#define OF (cpu->of)\n#define PF (cpu->pf_res ? !__builtin_parity(cpu->res & 0xff) : cpu->pf)\n#define AF (cpu->af_ops ? ((cpu->op1 ^ cpu->op2 ^ cpu->res) >> 4) & 1 : cpu->af)\n\nstatic inline void collapse_flags(struct cpu_state *cpu) {\n    cpu->zf = ZF;\n    cpu->sf = SF;\n    cpu->pf = PF;\n    cpu->zf_res = cpu->sf_res = cpu->pf_res = 0;\n    cpu->of_bit = cpu->of;\n    cpu->cf_bit = cpu->cf;\n    cpu->af = AF;\n    cpu->af_ops = 0;\n    cpu->pad1_1 = 1;\n    cpu->pad2_0 = cpu->pad3_0 = 0;\n    cpu->if_ = 1;\n}\n\nstatic inline void expand_flags(struct cpu_state *cpu) {\n    cpu->of = cpu->of_bit;\n    cpu->cf = cpu->cf_bit;\n    cpu->zf_res = cpu->sf_res = cpu->pf_res = cpu->af_ops = 0;\n}\n\nenum reg32 {\n    reg_eax = 0, reg_ecx, reg_edx, reg_ebx, reg_esp, reg_ebp, reg_esi, reg_edi, reg_count,\n    reg_none = reg_count,\n};\n\nstatic inline const char *reg32_name(enum reg32 reg) {\n    switch (reg) {\n        case reg_eax: return \"eax\";\n        case reg_ecx: return \"ecx\";\n        case reg_edx: return \"edx\";\n        case reg_ebx: return \"ebx\";\n        case reg_esp: return \"esp\";\n        case reg_ebp: return \"ebp\";\n        case reg_esi: return \"esi\";\n        case reg_edi: return \"edi\";\n        default: return \"?\";\n    }\n}\n\n#endif\n"
  },
  {
    "path": "emu/cpuid.h",
    "content": "#ifndef CPUID_H\n#define CPUID_H\n\n#include \"misc.h\"\n\nstatic inline void do_cpuid(dword_t *eax, dword_t *ebx, dword_t *ecx, dword_t *edx) {\n    dword_t leaf = *eax;\n    switch (leaf) {\n        case 0:\n            *eax = 0x01; // we support barely anything\n            *ebx = 0x756e6547; // Genu\n            *edx = 0x49656e69; // ineI\n            *ecx = 0x6c65746e; // ntel\n            break;\n        default: // if leaf is too high, use highest supported leaf\n        case 1:\n            *eax = 0x0; // say nothing about cpu model number\n            *ebx = 0x0; // processor number 0, flushes 0 bytes on clflush\n            *ecx = 0; // we support none of the features in ecx\n            *edx = (1 << 0) // fpu\n                | (1 << 15) // cmov\n                | (1 << 23) // mmx\n                | (1 << 26) // sse2\n                ;\n            break;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "emu/decode.h",
    "content": "#include \"misc.h\"\n#include \"emu/cpu.h\"\n#include \"emu/modrm.h\"\n#include \"emu/interrupt.h\"\n\n#undef oz\n#define oz OP_SIZE\n#define reg_ah reg_sp\n#define reg_ch reg_bp\n#define reg_dh reg_si\n#define reg_bh reg_di\n\n#undef DEFAULT_CHANNEL\n#define DEFAULT_CHANNEL instr\n#define TRACEI(msg, ...) TRACE(msg \"\\t\", ##__VA_ARGS__)\nextern int current_pid(void);\n#define TRACEIP() TRACE(\"%d %08x\\t\", current_pid(), state->ip)\n\n// this will be the next PyEval_EvalFrameEx\n__no_instrument DECODER_RET glue(DECODER_NAME, OP_SIZE)(DECODER_ARGS) {\n    DECLARE_LOCALS;\n\n    byte_t insn;\n    uint64_t imm = 0;\n    struct modrm modrm;\n#define READIMM_(name, size) _READIMM(name, size); TRACE(\"imm %llx \", (long long) name)\n#define READINSN _READIMM(insn, 8); TRACE(\"%02x \", insn)\n#define READIMM READIMM_(imm, OP_SIZE)\n#define READIMMoz READIMM // there's nothing more permanent than a temporary hack\n#define READIMM8 READIMM_(imm, 8); imm = (int8_t) (uint8_t) imm\n#define READIMM16 READIMM_(imm, 16)\n#define READMODRM_MEM READMODRM; if (modrm.type == modrm_reg) UNDEFINED\n#define READMODRM_NOMEM READMODRM; if (modrm.type != modrm_reg) UNDEFINED\n\nrestart:\n    TRACEIP();\n    READINSN;\n    switch (insn) {\n#define MAKE_OP(x, OP, op) \\\n        case x+0x0: TRACEI(op \" reg8, modrm8\"); \\\n                   READMODRM; OP(modrm_reg, modrm_val,8); break; \\\n        case x+0x1: TRACEI(op \" reg, modrm\"); \\\n                   READMODRM; OP(modrm_reg, modrm_val,oz); break; \\\n        case x+0x2: TRACEI(op \" modrm8, reg8\"); \\\n                   READMODRM; OP(modrm_val, modrm_reg,8); break; \\\n        case x+0x3: TRACEI(op \" modrm, reg\"); \\\n                   READMODRM; OP(modrm_val, modrm_reg,oz); break; \\\n        case x+0x4: TRACEI(op \" imm8, al\\t\"); \\\n                   READIMM8; OP(imm, reg_a,8); break; \\\n        case x+0x5: TRACEI(op \" imm, oax\\t\"); \\\n                   READIMM; OP(imm, reg_a,oz); break\n\n        MAKE_OP(0x00, ADD, \"add\");\n        MAKE_OP(0x08, OR, \"or\");\n\n        case 0x0f:\n            // 2-byte opcode prefix\n            READINSN;\n            switch (insn) {\n                case 0x18 ... 0x1f: TRACEI(\"nop modrm\\t\"); READMODRM; break;\n\n                case 0x28: TRACEI(\"movaps xmm:modrm, xmm\");\n                           READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x29: TRACEI(\"movaps xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n\n                case 0x31: TRACEI(\"rdtsc\");\n                           RDTSC; break;\n\n                case 0x40: TRACEI(\"cmovo modrm, reg\");\n                           READMODRM; CMOV(O, modrm_val, modrm_reg,oz); break;\n                case 0x41: TRACEI(\"cmovno modrm, reg\");\n                           READMODRM; CMOVN(O, modrm_val, modrm_reg,oz); break;\n                case 0x42: TRACEI(\"cmovb modrm, reg\");\n                           READMODRM; CMOV(B, modrm_val, modrm_reg,oz); break;\n                case 0x43: TRACEI(\"cmovnb modrm, reg\");\n                           READMODRM; CMOVN(B, modrm_val, modrm_reg,oz); break;\n                case 0x44: TRACEI(\"cmove modrm, reg\");\n                           READMODRM; CMOV(E, modrm_val, modrm_reg,oz); break;\n                case 0x45: TRACEI(\"cmovne modrm, reg\");\n                           READMODRM; CMOVN(E, modrm_val, modrm_reg,oz); break;\n                case 0x46: TRACEI(\"cmovbe modrm, reg\");\n                           READMODRM; CMOV(BE, modrm_val, modrm_reg,oz); break;\n                case 0x47: TRACEI(\"cmova modrm, reg\");\n                           READMODRM; CMOVN(BE, modrm_val, modrm_reg,oz); break;\n                case 0x48: TRACEI(\"cmovs modrm, reg\");\n                           READMODRM; CMOV(S, modrm_val, modrm_reg,oz); break;\n                case 0x49: TRACEI(\"cmovns modrm, reg\");\n                           READMODRM; CMOVN(S, modrm_val, modrm_reg,oz); break;\n                case 0x4a: TRACEI(\"cmovp modrm, reg\");\n                           READMODRM; CMOV(P, modrm_val, modrm_reg,oz); break;\n                case 0x4b: TRACEI(\"cmovnp modrm, reg\");\n                           READMODRM; CMOVN(P, modrm_val, modrm_reg,oz); break;\n                case 0x4c: TRACEI(\"cmovl modrm, reg\");\n                           READMODRM; CMOV(L, modrm_val, modrm_reg,oz); break;\n                case 0x4d: TRACEI(\"cmovnl modrm, reg\");\n                           READMODRM; CMOVN(L, modrm_val, modrm_reg,oz); break;\n                case 0x4e: TRACEI(\"cmovle modrm, reg\");\n                           READMODRM; CMOV(LE, modrm_val, modrm_reg,oz); break;\n                case 0x4f: TRACEI(\"cmovnle modrm, reg\");\n                           READMODRM; CMOVN(LE, modrm_val, modrm_reg,oz); break;\n\n                case 0x77: TRACEI(\"emms (ignored because there is no mmx)\"); break;\n\n                case 0x80: TRACEI(\"jo rel\\t\");\n                           READIMM; J_REL(O, imm); break;\n                case 0x81: TRACEI(\"jno rel\\t\");\n                           READIMM; JN_REL(O, imm); break;\n                case 0x82: TRACEI(\"jb rel\\t\");\n                           READIMM; J_REL(B, imm); break;\n                case 0x83: TRACEI(\"jnb rel\\t\");\n                           READIMM; JN_REL(B, imm); break;\n                case 0x84: TRACEI(\"je rel\\t\");\n                           READIMM; J_REL(E, imm); break;\n                case 0x85: TRACEI(\"jne rel\\t\");\n                           READIMM; JN_REL(E, imm); break;\n                case 0x86: TRACEI(\"jbe rel\\t\");\n                           READIMM; J_REL(BE, imm); break;\n                case 0x87: TRACEI(\"ja rel\\t\");\n                           READIMM; JN_REL(BE, imm); break;\n                case 0x88: TRACEI(\"js rel\\t\");\n                           READIMM; J_REL(S, imm); break;\n                case 0x89: TRACEI(\"jns rel\\t\");\n                           READIMM; JN_REL(S, imm); break;\n                case 0x8a: TRACEI(\"jp rel\\t\");\n                           READIMM; J_REL(P, imm); break;\n                case 0x8b: TRACEI(\"jnp rel\\t\");\n                           READIMM; JN_REL(P, imm); break;\n                case 0x8c: TRACEI(\"jl rel\\t\");\n                           READIMM; J_REL(L, imm); break;\n                case 0x8d: TRACEI(\"jnl rel\\t\");\n                           READIMM; JN_REL(L, imm); break;\n                case 0x8e: TRACEI(\"jle rel\\t\");\n                           READIMM; J_REL(LE, imm); break;\n                case 0x8f: TRACEI(\"jnle rel\\t\");\n                           READIMM; JN_REL(LE, imm); break;\n\n                case 0x90: TRACEI(\"seto\\t\");\n                           READMODRM; SET(O, modrm_val); break;\n                case 0x91: TRACEI(\"setno\\t\");\n                           READMODRM; SETN(O, modrm_val); break;\n                case 0x92: TRACEI(\"setb\\t\");\n                           READMODRM; SET(B, modrm_val); break;\n                case 0x93: TRACEI(\"setnb\\t\");\n                           READMODRM; SETN(B, modrm_val); break;\n                case 0x94: TRACEI(\"sete\\t\");\n                           READMODRM; SET(E, modrm_val); break;\n                case 0x95: TRACEI(\"setne\\t\");\n                           READMODRM; SETN(E, modrm_val); break;\n                case 0x96: TRACEI(\"setbe\\t\");\n                           READMODRM; SET(BE, modrm_val); break;\n                case 0x97: TRACEI(\"setnbe\\t\");\n                           READMODRM; SETN(BE, modrm_val); break;\n                case 0x98: TRACEI(\"sets\\t\");\n                           READMODRM; SET(S, modrm_val); break;\n                case 0x99: TRACEI(\"setns\\t\");\n                           READMODRM; SETN(S, modrm_val); break;\n                case 0x9a: TRACEI(\"setp\\t\");\n                           READMODRM; SET(P, modrm_val); break;\n                case 0x9b: TRACEI(\"setnp\\t\");\n                           READMODRM; SETN(P, modrm_val); break;\n                case 0x9c: TRACEI(\"setl\\t\");\n                           READMODRM; SET(L, modrm_val); break;\n                case 0x9d: TRACEI(\"setnl\\t\");\n                           READMODRM; SETN(L, modrm_val); break;\n                case 0x9e: TRACEI(\"setle\\t\");\n                           READMODRM; SET(LE, modrm_val); break;\n                case 0x9f: TRACEI(\"setnle\\t\");\n                           READMODRM; SETN(LE, modrm_val); break;\n\n                case 0xa2: TRACEI(\"cpuid\"); CPUID(); break;\n\n                case 0xa3: TRACEI(\"bt reg, modrm\");\n                           READMODRM; BT(modrm_reg, modrm_val,oz); break;\n\n                case 0xa4: TRACEI(\"shld imm8, reg, modrm\");\n                           READMODRM; READIMM8; SHLD(imm, modrm_reg, modrm_val,oz); break;\n                case 0xa5: TRACEI(\"shld cl, reg, modrm\");\n                           READMODRM; SHLD(reg_c, modrm_reg, modrm_val,oz); break;\n\n                case 0xab: TRACEI(\"bts reg, modrm\");\n                           READMODRM; BTS(modrm_reg, modrm_val,oz); break;\n\n                case 0xac: TRACEI(\"shrd imm8, reg, modrm\");\n                           READMODRM; READIMM8; SHRD(imm, modrm_reg, modrm_val,oz); break;\n                case 0xad: TRACEI(\"shrd cl, reg, modrm\");\n                           READMODRM; SHRD(reg_c, modrm_reg, modrm_val,oz); break;\n\n                case 0xae: TRACEI(\"fence\"); READMODRM; break;\n\n                case 0xaf: TRACEI(\"imul modrm, reg\");\n                           READMODRM; IMUL2(modrm_val, modrm_reg,oz); break;\n\n                case 0xb0: TRACEI(\"cmpxchg reg8, modrm8\");\n                           READMODRM_MEM; CMPXCHG(modrm_reg, modrm_val,8); break;\n                case 0xb1: TRACEI(\"cmpxchg reg, modrm\");\n                           READMODRM_MEM; CMPXCHG(modrm_reg, modrm_val,oz); break;\n\n                case 0xb3: TRACEI(\"btr reg, modrm\");\n                           READMODRM; BTR(modrm_reg, modrm_val,oz); break;\n\n                case 0xb6: TRACEI(\"movz modrm8, reg\");\n                           READMODRM; MOVZX(modrm_val, modrm_reg,8,oz); break;\n                case 0xb7: TRACEI(\"movz modrm16, reg\");\n                           READMODRM; MOVZX(modrm_val, modrm_reg,16,oz); break;\n\n#define GRP8(bit, val,z) \\\n    switch (modrm.opcode) { \\\n        case 4: TRACEI(\"bt\"); BT(bit, val,z); break; \\\n        case 5: TRACEI(\"bts\"); BTS(bit, val,z); break; \\\n        case 6: TRACEI(\"btr\"); BTR(bit, val,z); break; \\\n        case 7: TRACEI(\"btc\"); BTC(bit, val,z); break; \\\n        default: UNDEFINED; \\\n    }\n\n                case 0xba: TRACEI(\"grp8 imm8, modrm\");\n                           READMODRM; READIMM8; GRP8(imm, modrm_val,oz); break;\n\n#undef GRP8\n\n                case 0xbb: TRACEI(\"btc reg, modrm\");\n                           READMODRM; BTC(modrm_reg, modrm_val,oz); break;\n                case 0xbc: TRACEI(\"bsf modrm, reg\");\n                           READMODRM; BSF(modrm_val, modrm_reg,oz); break;\n                case 0xbd: TRACEI(\"bsr modrm, reg\");\n                           READMODRM; BSR(modrm_val, modrm_reg,oz); break;\n\n                case 0xbe: TRACEI(\"movs modrm8, reg\");\n                           READMODRM; MOVSX(modrm_val, modrm_reg,8,oz); break;\n                case 0xbf: TRACEI(\"movs modrm16, reg\");\n                           READMODRM; MOVSX(modrm_val, modrm_reg,16,oz); break;\n\n                case 0xc0: TRACEI(\"xadd reg8, modrm8\");\n                           READMODRM; XADD(modrm_reg, modrm_val,8); break;\n                case 0xc1: TRACEI(\"xadd reg, modrm\");\n                           READMODRM; XADD(modrm_reg, modrm_val,oz); break;\n                case 0xc2: TRACEI(\"cmppd xmm:modrm, xmm, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(fcmp_p, xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                case 0xc7: READMODRM_MEM; switch (modrm.opcode) {\n                               case 1: TRACEI(\"cmpxchg8b modrm\");\n                                       CMPXCHG8B(modrm_val,64); break;\n                               default: UNDEFINED;\n                           };\n                           break;\n\n#if OP_SIZE != 16\n                case 0xc8: TRACEI(\"bswap eax\");\n                           BSWAP(reg_a); break;\n                case 0xc9: TRACEI(\"bswap ecx\");\n                           BSWAP(reg_c); break;\n                case 0xca: TRACEI(\"bswap edx\");\n                           BSWAP(reg_d); break;\n                case 0xcb: TRACEI(\"bswap ebx\");\n                           BSWAP(reg_b); break;\n                case 0xcc: TRACEI(\"bswap esp\");\n                           BSWAP(reg_sp); break;\n                case 0xcd: TRACEI(\"bswap ebp\");\n                           BSWAP(reg_bp); break;\n                case 0xce: TRACEI(\"bswap esi\");\n                           BSWAP(reg_si); break;\n                case 0xcf: TRACEI(\"bswap edi\");\n                           BSWAP(reg_di); break;\n#endif\n\n#if OP_SIZE == 16\n                case 0x10: TRACEI(\"movupd xmm:modrm, xmm\");\n                           READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x11: TRACEI(\"movupd xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n                case 0x12: TRACEI(\"movlpd xmm, modrm\");\n                           READMODRM; V_OP(movl_p, modrm_val, xmm_modrm_reg,64); break;\n                case 0x13: TRACEI(\"movlpd modrm, xmm\");\n                           READMODRM; V_OP(movl_pm, xmm_modrm_reg, modrm_val,64); break;\n                case 0x14: TRACEI(\"unpcklpd xmm, xmm:modrm\");\n                           READMODRM; V_OP(unpackl_pd, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x15: TRACEI(\"unpckhpd xmm, xmm:modrm\");\n                           READMODRM; V_OP(unpackh_pd, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x16: TRACEI(\"movhpd xmm, modrm\");\n                           READMODRM; V_OP(movh_p, modrm_val, xmm_modrm_reg,64); break;\n                case 0x17: TRACEI(\"movhpd modrm, xmm\");\n                           READMODRM; V_OP(movh_pm, xmm_modrm_reg, modrm_val,64); break;\n                case 0x2e: TRACEI(\"ucomisd xmm, xmm:modrm\");\n                           READMODRM; V_OP(single_ucomi, xmm_modrm_val, xmm_modrm_reg,64); break;\n                case 0x2f: TRACEI(\"comisd xmm, xmm:modrm\");\n                           READMODRM; V_OP(single_ucomi, xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                case 0x50: TRACEI(\"movmskpd xmm:modrm, reg\");\n                           READMODRM; V_OP(fmovmask_d, xmm_modrm_val, modrm_reg,128); break;\n\n                case 0x54: TRACEI(\"andpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(and_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x55: TRACEI(\"andnpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(andn, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x56: TRACEI(\"orpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(or_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x57: TRACEI(\"xorpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(xor_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x58: TRACEI(\"addpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_p, xmm_modrm_val, xmm_modrm_reg,64); break;\n                case 0x59: TRACEI(\"mulpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(mul_p, xmm_modrm_val, xmm_modrm_reg,64); break;\n                case 0x5c: TRACEI(\"subpd xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_p, xmm_modrm_val, xmm_modrm_reg,64); break;\n                case 0x60: TRACEI(\"punpcklbw xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackl_bw, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x61: TRACEI(\"punpcklwd xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackl_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x62: TRACEI(\"punpckldq xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackl_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x63: TRACEI(\"packsswb xmm:modrm, xmm\");\n                           READMODRM; V_OP(packss_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x64: TRACEI(\"pcmpgtb xmm:modrm, xmm\");\n                           READMODRM; V_OP(compares_gtb, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x65: TRACEI(\"pcmpgtw xmm:modrm, xmm\");\n                           READMODRM; V_OP(compares_gtw, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x66: TRACEI(\"pcmpgtd xmm:modrm, xmm\");\n                           READMODRM; V_OP(compares_gtd, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x67: TRACEI(\"packuswb xmm:modrm, xmm\");\n                           READMODRM; V_OP(packsu_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x68: TRACEI(\"punpckhbw xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackh_bw, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x69: TRACEI(\"punpckhwd xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackh_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x6a: TRACEI(\"punpckhdq xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackh_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x6b: TRACEI(\"packssdw xmm:modrm, xmm\");\n                           READMODRM; V_OP(packss_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x6c: TRACEI(\"punpcklqdq xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackl_qdq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x6d: TRACEI(\"punpckhqdq xmm:modrm, xmm\");\n                           READMODRM; V_OP(unpackh_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x6e: TRACEI(\"movd modrm, xmm\");\n                           READMODRM; VMOV(modrm_val, xmm_modrm_reg,32); break;\n\n                case 0x6f: TRACEI(\"movdqa xmm:modrm, xmm\");\n                           READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                case 0x70: TRACEI(\"pshufd xmm:modrm, xmm, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(shuffle_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x71: READMODRM_NOMEM;\n                           switch (modrm.opcode) {\n                                case 2: TRACEI(\"psrlw imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftr_w, imm, xmm_modrm_reg, 128); break;\n                                case 4: TRACEI(\"psraw imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftrs_w, imm, xmm_modrm_reg, 128); break;\n                                case 6: TRACEI(\"psllw imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftl_w, imm, xmm_modrm_reg, 128); break;\n                                default: UNDEFINED;\n                            }\n                            break;\n                case 0x72: READMODRM_NOMEM;\n                            switch (modrm.opcode) {\n                                case 2: TRACEI(\"psrld imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftr_d, imm, xmm_modrm_reg, 128); break;\n                                case 4: TRACEI(\"psrad imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftrs_d, imm, xmm_modrm_reg, 128); break;\n                                case 6: TRACEI(\"pslld imm, xmm\");\n                                        READIMM8; V_OP(imm_shiftl_d, imm, xmm_modrm_reg, 128); break;\n                                default: UNDEFINED;\n                            }\n                            break;\n                case 0x73: READMODRM_NOMEM;\n                           switch (modrm.opcode) {\n                               case 2: TRACEI(\"psrlq imm, xmm\");\n                                       READIMM8; V_OP(imm_shiftr_q, imm, xmm_modrm_reg, 128); break;\n                               case 3: TRACEI(\"psrldq imm, xmm\");\n                                       READIMM8; V_OP(imm_shiftr_dq, imm, xmm_modrm_reg, 128); break;\n                               case 6: TRACEI(\"psllq imm, xmm\");\n                                       READIMM8; V_OP(imm_shiftl_q, imm, xmm_modrm_reg, 128); break;\n                               case 7: TRACEI(\"pslldq imm, xmm\");\n                                       READIMM8; V_OP(imm_shiftl_dq, imm, xmm_modrm_reg, 128); break;\n                               default: UNDEFINED;\n                           }\n                           break;\n\n                case 0x74: TRACEI(\"pcmpeqb xmm:modrm, xmm\");\n                           READMODRM; V_OP(compare_eqb, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x75: TRACEI(\"pcmpeqw xmm:modrm, xmm\");\n                           READMODRM; V_OP(compare_eqw, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x76: TRACEI(\"pcmpeqd xmm:modrm, xmm\");\n                           READMODRM; V_OP(compare_eqd, xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                case 0x7e: TRACEI(\"movd xmm, modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, modrm_val,32); break;\n                case 0x7f: TRACEI(\"movdqa xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n\n                case 0xc4: TRACEI(\"pinsrw xmm, modrm_val, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(insert_w, modrm_val, xmm_modrm_reg,128); break;\n                case 0xc5: TRACEI(\"pextrw xmm, modrm_val, imm8\");\n                           READMODRM_NOMEM; READIMM8; V_OP_IMM(extract_w, xmm_modrm_val, modrm_reg,128); break;\n                case 0xc6: TRACEI(\"shufpd xmm:modrm, xmm, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(shuffle_pd, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xd1: TRACEI(\"psrlw xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftr_w, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xd2: TRACEI(\"psrld xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftr_d, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xd3: TRACEI(\"psrlq xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftr_q, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xd4: TRACEI(\"paddq xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_q, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xd5: TRACEI(\"pmullw xmm:modrm, xmm\");\n                           READMODRM; V_OP(mull, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xd6: TRACEI(\"movq xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,64); break;\n                case 0xd7: TRACEI(\"pmovmskb xmm:modrm, reg\");\n                           READMODRM_NOMEM; V_OP(movmask_b, xmm_modrm_val, modrm_reg,128); break;\n                case 0xd8: TRACEI(\"psubusb xmm:modrm, xmm\");\n                           READMODRM; V_OP(subus_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xd9: TRACEI(\"psubusw xmm:modrm, xmm\");\n                           READMODRM; V_OP(subus_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xda: TRACEI(\"pminub xmm:modrm, xmm\");\n                           READMODRM; V_OP(min_ub, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xdb: TRACEI(\"pand xmm:modrm, xmm\");\n                           READMODRM; V_OP(and_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xdc: TRACEI(\"paddusb xmm:modrm, xmm\");\n                           READMODRM; V_OP(addus_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xdd: TRACEI(\"paddusw xmm:modrm, xmm\");\n                           READMODRM; V_OP(addus_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xde: TRACEI(\"pmaxub xmm:modrm, xmm\");\n                           READMODRM; V_OP(max_ub, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xdf: TRACEI(\"pandn xmm:modrm, xmm\");\n                           READMODRM; V_OP(andn, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xe0: TRACEI(\"pavgb xmm:modrm, xmm\");\n                           READMODRM; V_OP(avg_b, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe1: TRACEI(\"psraw xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftrs_w, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe2: TRACEI(\"psrad xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftrs_d, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe3: TRACEI(\"pavgw xmm:modrm, xmm\");\n                           READMODRM; V_OP(avg_w, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe4: TRACEI(\"pmulhuw xmm:modrm, xmm\");\n                           READMODRM; V_OP(muluu, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe5: TRACEI(\"pmulhw xmm:modrm, xmm\");\n                           READMODRM; V_OP(mulu, xmm_modrm_val, xmm_modrm_reg, 128); break;\n                case 0xe6: TRACEI(\"cvttpd2dq xmm:modrm, xmm\");\n                           READMODRM; V_OP(cvttpd2dq, xmm_modrm_val, xmm_modrm_reg,64); break;\n                case 0xe7: TRACEI(\"movntdq xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n                case 0xe8: TRACEI(\"psubsb xmm:modrm, xmm\");\n                           READMODRM; V_OP(subss_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xe9: TRACEI(\"psubsw xmm:modrm, xmm\");\n                           READMODRM; V_OP(subss_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xea: TRACEI(\"pminsw xmm:modrm, xmm\");\n                           READMODRM; V_OP(mins_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xeb: TRACEI(\"por xmm:modrm, xmm\");\n                           READMODRM; V_OP(or_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xec: TRACEI(\"paddsb xmm:modrm, xmm\");\n                           READMODRM; V_OP(addss_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xed: TRACEI(\"paddsw xmm:modrm, xmm\");\n                           READMODRM; V_OP(addss_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xee: TRACEI(\"pmaxsw xmm:modrm, xmm\");\n                           READMODRM; V_OP(maxs_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xef: TRACEI(\"pxor xmm:modrm, xmm\");\n                           READMODRM; V_OP(xor_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf1: TRACEI(\"psllw xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftl_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf2: TRACEI(\"pslld xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftl_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf3: TRACEI(\"psllq xmm:modrm, xmm\");\n                           READMODRM; V_OP(shiftl_q, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf4: TRACEI(\"pmuludq xmm:modrm, xmm\");\n                           READMODRM; V_OP(mulu_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf5: TRACEI(\"pmaddwd xmm:modrm, xmm\");\n                           READMODRM; V_OP(madd_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf6: TRACEI(\"psadbw xmm:modrm, xmm\");\n                           READMODRM; V_OP(sumabs_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xfb: TRACEI(\"psubq xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_q, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf8: TRACEI(\"psubb xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xf9: TRACEI(\"psubw xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xfa: TRACEI(\"psubd xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xfc: TRACEI(\"paddb xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_b, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xfd: TRACEI(\"paddw xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_w, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xfe: TRACEI(\"paddd xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_d, xmm_modrm_val, xmm_modrm_reg,128); break;\n#else\n                case 0x10: TRACEI(\"movups xmm:modrm, xmm\");\n                           READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x11: TRACEI(\"movups xmm, xmm:modrm\");\n                           READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n                case 0x12: TRACEI(\"movlps xmm, modrm\");\n                           READMODRM; V_OP(movl_p, modrm_val, xmm_modrm_reg,64); break;\n                case 0x13: TRACEI(\"movlps modrm, xmm\");\n                           READMODRM; V_OP(movl_pm, xmm_modrm_reg, modrm_val,64); break;\n                case 0x14: TRACEI(\"unpcklps xmm, xmm:modrm\");\n                           READMODRM; V_OP(unpackl_ps, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x15: TRACEI(\"unpckhps xmm, xmm:modrm\");\n                           READMODRM; V_OP(unpackh_ps, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x16: TRACEI(\"movhps xmm, modrm\");\n                           READMODRM; V_OP(movh_p, modrm_val, xmm_modrm_reg,64); break;\n                case 0x17: TRACEI(\"movhps modrm, xmm\");\n                           READMODRM; V_OP(movh_pm, xmm_modrm_reg, modrm_val,64); break;\n                case 0x2e: TRACEI(\"ucomiss xmm, xmm:modrm\");\n                           READMODRM; V_OP(single_ucomi, xmm_modrm_val, xmm_modrm_reg,32); break;\n                case 0x2f: TRACEI(\"comiss xmm, xmm:modrm\");\n                           READMODRM; V_OP(single_ucomi, xmm_modrm_val, xmm_modrm_reg,32); break;\n\n                case 0x54: TRACEI(\"andps xmm:modrm, xmm\");\n                           READMODRM; V_OP(and_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x55: TRACEI(\"andnps xmm:modrm, xmm\");\n                           READMODRM; V_OP(andn, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x56: TRACEI(\"orps xmm:modrm, xmm\");\n                           READMODRM; V_OP(or_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0x57: TRACEI(\"xorps xmm:modrm, xmm\");\n                           READMODRM; V_OP(xor_dq, xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                case 0x58: TRACEI(\"addps xmm:modrm, xmm\");\n                           READMODRM; V_OP(add_p, xmm_modrm_val, xmm_modrm_reg,32); break;\n                case 0x59: TRACEI(\"mulps xmm:modrm, xmm\");\n                           READMODRM; V_OP(mul_p, xmm_modrm_val, xmm_modrm_reg,32); break;\n                case 0x5c: TRACEI(\"subps xmm:modrm, xmm\");\n                           READMODRM; V_OP(sub_p, xmm_modrm_val, xmm_modrm_reg,32); break;\n\n                case 0x62: TRACEI(\"punpckldq mm:modrm, mm\");\n                           READMODRM; V_OP(unpackl_dq, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x64: TRACEI(\"pcmpgtb mm:modrm, mm\");\n                           READMODRM; V_OP(compares_gtb, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x65: TRACEI(\"pcmpgtw mm:modrm, mm\");\n                           READMODRM; V_OP(compares_gtw, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x66: TRACEI(\"pcmpgtd mm:modrm, mm\");\n                           READMODRM; V_OP(compares_gtd, mm_modrm_val, mm_modrm_reg,64); break;\n\n                case 0x6e: TRACEI(\"movd modrm, mm\");\n                           READMODRM; VMOV(modrm_val, mm_modrm_reg,32); break;\n                case 0x6f: TRACEI(\"movq mm:modrm, mm\");\n                           READMODRM; VMOV(mm_modrm_val, mm_modrm_reg,64); break;\n\n                case 0x70: TRACEI(\"pshufw mm:modrm, mm, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(shuffle_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x71: READMODRM;\n                           switch (modrm.opcode) {\n                               case 2: TRACEI(\"psrlw imm, mm\");\n                                       READIMM8; V_OP(imm_shiftr_w, imm, mm_modrm_reg,64); break;\n                               case 4: TRACEI(\"psraw imm, mm\");\n                                       READIMM8; V_OP(imm_shiftrs_w, imm, mm_modrm_reg,64); break;\n                               case 6: TRACEI(\"psllw imm, mm\");\n                                       READIMM8; V_OP(imm_shiftl_w, imm, mm_modrm_reg,64); break;\n                               default: UNDEFINED;\n                           }\n                           break;\n                case 0x72: READMODRM;\n                           switch (modrm.opcode) {\n                               case 2: TRACEI(\"psrld imm, mm\");\n                                       READIMM8; V_OP(imm_shiftr_d, imm, mm_modrm_reg,64); break;\n                               case 4: TRACEI(\"psrad imm, mm\");\n                                       READIMM8; V_OP(imm_shiftrs_d, imm, mm_modrm_reg,64); break;\n                               case 6: TRACEI(\"pslld imm, mm\");\n                                       READIMM8; V_OP(imm_shiftl_d, imm, mm_modrm_reg,64); break;\n                               default: UNDEFINED;\n                           }\n                           break;\n                case 0x73: READMODRM;\n                           switch (modrm.opcode) {\n                               case 2: TRACEI(\"psrlq imm, mm\");\n                                       READIMM8; V_OP(imm_shiftr_q, imm, mm_modrm_reg,64); break;\n                               case 6: TRACEI(\"psllq imm, mm\");\n                                       READIMM8; V_OP(imm_shiftl_q, imm, mm_modrm_reg,64); break;\n                               default: UNDEFINED;\n                           }\n                           break;\n                case 0x74: TRACEI(\"pcmpeqb mm:modrm, mm\");\n                           READMODRM; V_OP(compare_eqb, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x75: TRACEI(\"pcmpeqw mm:modrm, mm\");\n                           READMODRM; V_OP(compare_eqw, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0x76: TRACEI(\"pcmpeqd mm:modrm, mm\");\n                           READMODRM; V_OP(compare_eqd, mm_modrm_val, mm_modrm_reg,64); break;\n\n                case 0x7e: TRACEI(\"movd mm, modrm\");\n                           READMODRM; VMOV(mm_modrm_reg, modrm_val,32); break;\n                case 0x7f: TRACEI(\"movq mm, mm:modrm\");\n                           READMODRM_MEM; VMOV(mm_modrm_reg, mm_modrm_val,64); break;\n                case 0xc4: TRACEI(\"pinsrw mm, modrm_val, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(insert_w, modrm_val, mm_modrm_reg,64); break;\n                case 0xc6: TRACEI(\"shufps xmm:modrm, xmm, imm8\");\n                           READMODRM; READIMM8; V_OP_IMM(shuffle_ps, xmm_modrm_val, xmm_modrm_reg,128); break;\n                case 0xd1: TRACEI(\"psrlw mm:modrm, mm\");\n                           READMODRM; V_OP(shiftr_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xd2: TRACEI(\"psrld mm:modrm, mm\");\n                           READMODRM; V_OP(shiftr_d, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xd3: TRACEI(\"psrlq mm:modrm, mm\");\n                           READMODRM; V_OP(shiftr_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xd4: TRACEI(\"paddq mm:modrm, mm\");\n                           READMODRM; V_OP(add_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xd5: TRACEI(\"pmullw mm:modrm, mm\");\n                           READMODRM; V_OP(mull, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xd7: TRACEI(\"pmovmskb mm:modrm, reg\");\n                           READMODRM_NOMEM; V_OP(movmask_b, mm_modrm_val, modrm_reg,64); break;\n                case 0xdb: TRACEI(\"pand mm:modrm, mm\");\n                           READMODRM; V_OP(and_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xe1: TRACEI(\"psraw mm:modrm, mm\");\n                           READMODRM; V_OP(shiftrs_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xe2: TRACEI(\"psrad mm:modrm, mm\");\n                           READMODRM; V_OP(shiftrs_d, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xe5: TRACEI(\"pmulhw mm:modrm, mm\");\n                           READMODRM; V_OP(mulu, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xe7: TRACEI(\"movntq mm, mm:modrm\");\n                           READMODRM_MEM; VMOV(mm_modrm_reg, mm_modrm_val,64); break;\n                case 0xeb: TRACEI(\"por mm:modrm, mm\");\n                           READMODRM; V_OP(or_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xef: TRACEI(\"pxor mm:modrm, mm\");\n                           READMODRM; V_OP(xor_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf1: TRACEI(\"psllw mm:modrm, mm\");\n                           READMODRM; V_OP(shiftl_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf2: TRACEI(\"pslld mm:modrm, mm\");\n                           READMODRM; V_OP(shiftl_d, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf3: TRACEI(\"psllq mm:modrm, mm\");\n                           READMODRM; V_OP(shiftl_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf4: TRACEI(\"pmuludq mm:modrm, mm\");\n                           READMODRM; V_OP(mulu_dq, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf8: TRACEI(\"psubb mm:modrm, mm\");\n                           READMODRM; V_OP(sub_b, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xf9: TRACEI(\"psubw mm:modrm, mm\");\n                           READMODRM; V_OP(sub_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xfa: TRACEI(\"psubd mm:modrm, mm\");\n                           READMODRM; V_OP(sub_d, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xfb: TRACEI(\"psubq mm:modrm, mm\");\n                           READMODRM; V_OP(sub_q, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xfc: TRACEI(\"paddb mm:modrm, mm\");\n                           READMODRM; V_OP(add_b, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xfd: TRACEI(\"paddw mm:modrm, mm\");\n                           READMODRM; V_OP(add_w, mm_modrm_val, mm_modrm_reg,64); break;\n                case 0xfe: TRACEI(\"paddd mm:modrm, mm\");\n                           READMODRM; V_OP(add_d, mm_modrm_val, mm_modrm_reg,64); break;\n#endif\n\n                default: TRACEI(\"undefined\");\n                         UNDEFINED;\n            }\n            break;\n\n        MAKE_OP(0x10, ADC, \"adc\");\n        MAKE_OP(0x18, SBB, \"sbb\");\n        MAKE_OP(0x20, AND, \"and\");\n        MAKE_OP(0x28, SUB, \"sub\");\n\n        case 0x2e: TRACEI(\"segment cs (ignoring)\"); goto restart;\n\n        MAKE_OP(0x30, XOR, \"xor\");\n        MAKE_OP(0x38, CMP, \"cmp\");\n\n        case 0x3e: TRACEI(\"segment ds (useless)\"); goto restart;\n\n        case 0x40: TRACEI(\"inc oax\"); INC(reg_a,oz); break;\n        case 0x41: TRACEI(\"inc ocx\"); INC(reg_c,oz); break;\n        case 0x42: TRACEI(\"inc odx\"); INC(reg_d,oz); break;\n        case 0x43: TRACEI(\"inc obx\"); INC(reg_b,oz); break;\n        case 0x44: TRACEI(\"inc osp\"); INC(reg_sp,oz); break;\n        case 0x45: TRACEI(\"inc obp\"); INC(reg_bp,oz); break;\n        case 0x46: TRACEI(\"inc osi\"); INC(reg_si,oz); break;\n        case 0x47: TRACEI(\"inc odi\"); INC(reg_di,oz); break;\n        case 0x48: TRACEI(\"dec oax\"); DEC(reg_a,oz); break;\n        case 0x49: TRACEI(\"dec ocx\"); DEC(reg_c,oz); break;\n        case 0x4a: TRACEI(\"dec odx\"); DEC(reg_d,oz); break;\n        case 0x4b: TRACEI(\"dec obx\"); DEC(reg_b,oz); break;\n        case 0x4c: TRACEI(\"dec osp\"); DEC(reg_sp,oz); break;\n        case 0x4d: TRACEI(\"dec obp\"); DEC(reg_bp,oz); break;\n        case 0x4e: TRACEI(\"dec osi\"); DEC(reg_si,oz); break;\n        case 0x4f: TRACEI(\"dec odi\"); DEC(reg_di,oz); break;\n\n        case 0x50: TRACEI(\"push oax\"); PUSH(reg_a,oz); break;\n        case 0x51: TRACEI(\"push ocx\"); PUSH(reg_c,oz); break;\n        case 0x52: TRACEI(\"push odx\"); PUSH(reg_d,oz); break;\n        case 0x53: TRACEI(\"push obx\"); PUSH(reg_b,oz); break;\n        case 0x54: TRACEI(\"push osp\"); PUSH(reg_sp,oz); break;\n        case 0x55: TRACEI(\"push obp\"); PUSH(reg_bp,oz); break;\n        case 0x56: TRACEI(\"push osi\"); PUSH(reg_si,oz); break;\n        case 0x57: TRACEI(\"push odi\"); PUSH(reg_di,oz); break;\n\n        case 0x58: TRACEI(\"pop oax\"); POP(reg_a,oz); break;\n        case 0x59: TRACEI(\"pop ocx\"); POP(reg_c,oz); break;\n        case 0x5a: TRACEI(\"pop odx\"); POP(reg_d,oz); break;\n        case 0x5b: TRACEI(\"pop obx\"); POP(reg_b,oz); break;\n        case 0x5c: TRACEI(\"pop osp\"); POP(reg_sp,oz); break;\n        case 0x5d: TRACEI(\"pop obp\"); POP(reg_bp,oz); break;\n        case 0x5e: TRACEI(\"pop osi\"); POP(reg_si,oz); break;\n        case 0x5f: TRACEI(\"pop odi\"); POP(reg_di,oz); break;\n\n        case 0x65: TRACE(\"segment gs\\n\"); SEG_GS(); goto restart;\n\n        case 0x60: TRACE(\"pusha\");\n                   PUSH(reg_a,oz); PUSH(reg_c,oz);\n                   PUSH(reg_d,oz); PUSH(reg_b,oz);\n                   PUSH(reg_sp,oz); PUSH(reg_bp,oz); // TODO this is the wrong sp\n                   PUSH(reg_si,oz); PUSH(reg_di,oz);\n                   break;\n        case 0x61: TRACE(\"popa\");\n                   POP(reg_di,oz); POP(reg_si,oz);\n                   // pop reg_sp into reg_b as an easy way to ignore it\n                   POP(reg_bp,oz); POP(reg_b,oz);\n                   POP(reg_b,oz); POP(reg_d,oz);\n                   POP(reg_c,oz); POP(reg_a,oz);\n                   break;\n\n        case 0x66:\n#if OP_SIZE == 32\n            TRACE(\"entering 16 bit mode\\n\");\n            return glue(DECODER_NAME, 16)(DECODER_PASS_ARGS);\n#else\n            TRACE(\"entering 32 bit mode\\n\");\n            return glue(DECODER_NAME, 32)(DECODER_PASS_ARGS);\n#endif\n\n        case 0x67: TRACEI(\"address size prefix (ignored)\"); goto restart;\n\n        case 0x68: TRACEI(\"push imm\\t\");\n                   READIMM; PUSH(imm,oz); break;\n        case 0x69: TRACEI(\"imul imm\\t\");\n                   READMODRM; READIMM; IMUL3(imm, modrm_val, modrm_reg,oz); break;\n        case 0x6a: TRACEI(\"push imm8\\t\");\n                   READIMM8; PUSH(imm,oz); break;\n        case 0x6b: TRACEI(\"imul imm8\\t\");\n                   READMODRM; READIMM8; IMUL3(imm, modrm_val, modrm_reg,oz); break;\n\n        case 0x70: TRACEI(\"jo rel8\\t\");\n                   READIMM8; J_REL(O, imm); break;\n        case 0x71: TRACEI(\"jno rel8\\t\");\n                   READIMM8; JN_REL(O, imm); break;\n        case 0x72: TRACEI(\"jb rel8\\t\");\n                   READIMM8; J_REL(B, imm); break;\n        case 0x73: TRACEI(\"jnb rel8\\t\");\n                   READIMM8; JN_REL(B, imm); break;\n        case 0x74: TRACEI(\"je rel8\\t\");\n                   READIMM8; J_REL(E, imm); break;\n        case 0x75: TRACEI(\"jne rel8\\t\");\n                   READIMM8; JN_REL(E, imm); break;\n        case 0x76: TRACEI(\"jbe rel8\\t\");\n                   READIMM8; J_REL(BE, imm); break;\n        case 0x77: TRACEI(\"ja rel8\\t\");\n                   READIMM8; JN_REL(BE, imm); break;\n        case 0x78: TRACEI(\"js rel8\\t\");\n                   READIMM8; J_REL(S, imm); break;\n        case 0x79: TRACEI(\"jns rel8\\t\");\n                   READIMM8; JN_REL(S, imm); break;\n        case 0x7a: TRACEI(\"jp rel8\\t\");\n                   READIMM8; J_REL(P, imm); break;\n        case 0x7b: TRACEI(\"jnp rel8\\t\");\n                   READIMM8; JN_REL(P, imm); break;\n        case 0x7c: TRACEI(\"jl rel8\\t\");\n                   READIMM8; J_REL(L, imm); break;\n        case 0x7d: TRACEI(\"jnl rel8\\t\");\n                   READIMM8; JN_REL(L, imm); break;\n        case 0x7e: TRACEI(\"jle rel8\\t\");\n                   READIMM8; J_REL(LE, imm); break;\n        case 0x7f: TRACEI(\"jnle rel8\\t\");\n                   READIMM8; JN_REL(LE, imm); break;\n\n#define GRP1(src, dst,z) \\\n    switch (modrm.opcode) { \\\n        case 0: TRACE(\"add\"); \\\n                ADD(src, dst,z); break; \\\n        case 1: TRACE(\"or\"); \\\n                OR(src, dst,z); break; \\\n        case 2: TRACE(\"adc\"); \\\n                ADC(src, dst,z); break; \\\n        case 3: TRACE(\"sbb\"); \\\n                SBB(src, dst,z); break; \\\n        case 4: TRACE(\"and\"); \\\n                AND(src, dst,z); break; \\\n        case 5: TRACE(\"sub\"); \\\n                SUB(src, dst,z); break; \\\n        case 6: TRACE(\"xor\"); \\\n                XOR(src, dst,z); break; \\\n        case 7: TRACE(\"cmp\"); \\\n                CMP(src, dst,z); break; \\\n        default: TRACE(\"undefined\"); \\\n                 UNDEFINED; \\\n    }\n\n        case 0x80: TRACEI(\"grp1 imm8, modrm8\");\n                   READMODRM; READIMM8; GRP1(imm, modrm_val,8); break;\n        case 0x81: TRACEI(\"grp1 imm, modrm\");\n                   READMODRM; READIMM; GRP1(imm, modrm_val,oz); break;\n        case 0x83: TRACEI(\"grp1 imm8, modrm\");\n                   READMODRM; READIMM8; GRP1(imm, modrm_val,oz); break;\n\n#undef GRP1\n\n        case 0x84: TRACEI(\"test reg8, modrm8\");\n                   READMODRM; TEST(modrm_reg, modrm_val,8); break;\n        case 0x85: TRACEI(\"test reg, modrm\");\n                   READMODRM; TEST(modrm_reg, modrm_val,oz); break;\n\n        case 0x86: TRACEI(\"xchg reg8, modrm8\");\n                   READMODRM; XCHG(modrm_reg, modrm_val,8); break;\n        case 0x87: TRACEI(\"xchg reg, modrm\");\n                   READMODRM; XCHG(modrm_reg, modrm_val,oz); break;\n\n        case 0x88: TRACEI(\"mov reg8, modrm8\");\n                   READMODRM; MOV(modrm_reg, modrm_val,8); break;\n        case 0x89: TRACEI(\"mov reg, modrm\");\n                   READMODRM; MOV(modrm_reg, modrm_val,oz); break;\n        case 0x8a: TRACEI(\"mov modrm8, reg8\");\n                   READMODRM; MOV(modrm_val, modrm_reg,8); break;\n        case 0x8b: TRACEI(\"mov modrm, reg\");\n                   READMODRM; MOV(modrm_val, modrm_reg,oz); break;\n\n        case 0x8d: TRACEI(\"lea\\t\\t\"); READMODRM_MEM;\n                   MOV(addr, modrm_reg,oz); break;\n\n        // we only support fs and gs, and that too not very well.\n        // gs does nothing: see comment in sys/tls.c\n        // for fs, we discard writes. if anyone tries to read we trap\n        case 0x8c: TRACEI(\"mov seg, modrm\\t\"); READMODRM;\n            if (modrm.reg != 5 /* gs */) UNDEFINED;\n            MOV(gs, modrm_val,16); break;\n        case 0x8e: TRACEI(\"mov modrm, seg\\t\"); READMODRM;\n            if (modrm.reg == 5 /* gs */) {\n                MOV(modrm_val, gs,16); break;\n            } else if (modrm.reg == 4 /* fs */) {\n                break;\n            } else {\n                UNDEFINED;\n            }\n\n        case 0x8f: TRACEI(\"pop modrm\");\n                   READMODRM; POP(modrm_val,oz); break;\n\n        case 0x90: TRACEI(\"nop\"); break;\n        case 0x91: TRACEI(\"xchg ocx, oax\");\n                   XCHG(reg_c, reg_a,oz); break;\n        case 0x92: TRACEI(\"xchg odx, oax\");\n                   XCHG(reg_d, reg_a,oz); break;\n        case 0x93: TRACEI(\"xchg obx, oax\");\n                   XCHG(reg_b, reg_a,oz); break;\n        case 0x94: TRACEI(\"xchg osp, oax\");\n                   XCHG(reg_sp, reg_a,oz); break;\n        case 0x95: TRACEI(\"xchg obp, oax\");\n                   XCHG(reg_bp, reg_a,oz); break;\n        case 0x96: TRACEI(\"xchg osi, oax\");\n                   XCHG(reg_si, reg_a,oz); break;\n        case 0x97: TRACEI(\"xchg odi, oax\");\n                   XCHG(reg_di, reg_a,oz); break;\n\n        case 0x98: TRACEI(\"cvte\"); CVTE; break;\n        case 0x99: TRACEI(\"cvt\"); CVT; break;\n\n        case 0x9b: TRACEI(\"fwait (ignored)\"); break;\n\n        case 0x9c: TRACEI(\"pushf\"); PUSHF(); break;\n        case 0x9d: TRACEI(\"popf\"); POPF(); break;\n        case 0x9e: TRACEI(\"sahf\\t\\t\"); SAHF; break;\n\n        case 0xa0: TRACEI(\"mov mem, al\\t\");\n                   READADDR; MOV(mem_addr, reg_a,8); break;\n        case 0xa1: TRACEI(\"mov mem, eax\\t\");\n                   READADDR; MOV(mem_addr, reg_a,oz); break;\n        case 0xa2: TRACEI(\"mov al, mem\\t\");\n                   READADDR; MOV(reg_a, mem_addr,8); break;\n        case 0xa3: TRACEI(\"mov oax, mem\\t\");\n                   READADDR; MOV(reg_a, mem_addr,oz); break;\n\n        case 0xa4: TRACEI(\"movsb\"); STR(movs, 8); break;\n        case 0xa5: TRACEI(\"movs\"); STR(movs, oz); break;\n        case 0xa6: TRACEI(\"cmpsb\"); STR(cmps, 8); break;\n        case 0xa7: TRACEI(\"cmps\"); STR(cmps, oz); break;\n\n        case 0xa8: TRACEI(\"test imm8, al\");\n                   READIMM8; TEST(imm, reg_a,8); break;\n        case 0xa9: TRACEI(\"test imm, oax\");\n                   READIMM; TEST(imm, reg_a,oz); break;\n\n        case 0xaa: TRACEI(\"stosb\"); STR(stos, 8); break;\n        case 0xab: TRACEI(\"stos\"); STR(stos, oz); break;\n        case 0xac: TRACEI(\"lodsb\"); STR(lods, 8); break;\n        case 0xad: TRACEI(\"lods\"); STR(lods, oz); break;\n        case 0xae: TRACEI(\"scasb\"); STR(scas, 8); break;\n        case 0xaf: TRACEI(\"scas\"); STR(scas, oz); break;\n\n        case 0xb0: TRACEI(\"mov imm, al\\t\");\n                   READIMM8; MOV(imm, reg_a,8); break;\n        case 0xb1: TRACEI(\"mov imm, cl\\t\");\n                   READIMM8; MOV(imm, reg_c,8); break;\n        case 0xb2: TRACEI(\"mov imm, dl\\t\");\n                   READIMM8; MOV(imm, reg_d,8); break;\n        case 0xb3: TRACEI(\"mov imm, bl\\t\");\n                   READIMM8; MOV(imm, reg_b,8); break;\n        case 0xb4: TRACEI(\"mov imm, ah\\t\");\n                   READIMM8; MOV(imm, reg_ah,8); break;\n        case 0xb5: TRACEI(\"mov imm, ch\\t\");\n                   READIMM8; MOV(imm, reg_ch,8); break;\n        case 0xb6: TRACEI(\"mov imm, dh\\t\");\n                   READIMM8; MOV(imm, reg_dh,8); break;\n        case 0xb7: TRACEI(\"mov imm, bh\\t\");\n                   READIMM8; MOV(imm, reg_bh,8); break;\n\n        case 0xb8: TRACEI(\"mov imm, oax\\t\");\n                   READIMM; MOV(imm, reg_a,oz); break;\n        case 0xb9: TRACEI(\"mov imm, ocx\\t\");\n                   READIMM; MOV(imm, reg_c,oz); break;\n        case 0xba: TRACEI(\"mov imm, odx\\t\");\n                   READIMM; MOV(imm, reg_d,oz); break;\n        case 0xbb: TRACEI(\"mov imm, obx\\t\");\n                   READIMM; MOV(imm, reg_b,oz); break;\n        case 0xbc: TRACEI(\"mov imm, osp\\t\");\n                   READIMM; MOV(imm, reg_sp,oz); break;\n        case 0xbd: TRACEI(\"mov imm, obp\\t\");\n                   READIMM; MOV(imm, reg_bp,oz); break;\n        case 0xbe: TRACEI(\"mov imm, osi\\t\");\n                   READIMM; MOV(imm, reg_si,oz); break;\n        case 0xbf: TRACEI(\"mov imm, odi\\t\");\n                   READIMM; MOV(imm, reg_di,oz); break;\n\n#define GRP2(count, val,z) \\\n    switch (modrm.opcode) { \\\n        case 0: TRACE(\"rol\"); ROL(count, val,z); break; \\\n        case 1: TRACE(\"ror\"); ROR(count, val,z); break; \\\n        case 2: TRACE(\"rcl\"); RCL(count, val,z); break; \\\n        case 3: TRACE(\"rcr\"); RCR(count, val,z); break; \\\n        case 4: \\\n        case 6: TRACE(\"shl\"); SHL(count, val,z); break; \\\n        case 5: TRACE(\"shr\"); SHR(count, val,z); break; \\\n        case 7: TRACE(\"sar\"); SAR(count, val,z); break; \\\n    }\n\n        case 0xc0: TRACEI(\"grp2 imm8, modrm8\");\n                   READMODRM; READIMM8; GRP2(imm, modrm_val,8); break;\n        case 0xc1: TRACEI(\"grp2 imm8, modrm\");\n                   READMODRM; READIMM8; GRP2(imm, modrm_val,oz); break;\n\n        case 0xc2: TRACEI(\"ret near imm\\t\");\n                   READIMM16; RET_NEAR(imm); break;\n        case 0xc3: TRACEI(\"ret near\");\n                   RET_NEAR(0); break;\n\n        case 0xc9: TRACEI(\"leave\");\n                   MOV(reg_bp, reg_sp,oz); POP(reg_bp,oz); break;\n\n        case 0xcc: TRACEI(\"int3\");\n                   INT(INT_BREAKPOINT); break;\n        case 0xcd: TRACEI(\"int imm8\\t\");\n                   READIMM8; INT(imm); break;\n\n        case 0xc6: TRACEI(\"mov imm8, modrm8\");\n                   READMODRM; READIMM8; MOV(imm, modrm_val,8); break;\n        case 0xc7: TRACEI(\"mov imm, modrm\");\n                   READMODRM; READIMM; MOV(imm, modrm_val,oz); break;\n\n        case 0xd0: TRACEI(\"grp2 1, modrm8\");\n                   READMODRM; GRP2(1, modrm_val,8); break;\n        case 0xd1: TRACEI(\"grp2 1, modrm\");\n                   READMODRM; GRP2(1, modrm_val,oz); break;\n        case 0xd2: TRACEI(\"grp2 cl, modrm8\");\n                   READMODRM; GRP2(reg_c, modrm_val,8); break;\n        case 0xd3: TRACEI(\"grp2 cl, modrm\");\n                   READMODRM; GRP2(reg_c, modrm_val,oz); break;\n\n#undef GRP2\n\n        case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:\n            TRACEI(\"fpu\\t\\t\"); READMODRM;\n            if (modrm.type != modrm_reg) {\n                switch (insn << 4 | modrm.opcode) {\n                    case 0xd80: TRACE(\"fadd mem32\"); FADDM(mem_addr_real,32); break;\n                    case 0xd81: TRACE(\"fmul mem32\"); FMULM(mem_addr_real,32); break;\n                    case 0xd82: TRACE(\"fcom mem32\"); FCOMM(mem_addr_real,32); break;\n                    case 0xd83: TRACE(\"fcomp mem32\"); FCOMM(mem_addr_real,32); FPOP; break;\n                    case 0xd84: TRACE(\"fsub mem32\"); FSUBM(mem_addr_real,32); break;\n                    case 0xd85: TRACE(\"fsubr mem32\"); FSUBRM(mem_addr_real,32); break;\n                    case 0xd86: TRACE(\"fdiv mem32\"); FDIVM(mem_addr_real,32); break;\n                    case 0xd87: TRACE(\"fdivr mem32\"); FDIVRM(mem_addr_real,32); break;\n                    case 0xd90: TRACE(\"fld mem32\"); FLDM(mem_addr_real,32); break;\n                    case 0xd92: TRACE(\"fst mem32\"); FSTM(mem_addr_real,32); break;\n                    case 0xd93: TRACE(\"fstp mem32\"); FSTM(mem_addr_real,32); FPOP; break;\n#if OP_SIZE == 32\n                    case 0xd94: TRACE(\"fldenv mem32\"); FLDENV(mem_addr,32); break;\n#endif\n                    case 0xd95: TRACE(\"fldcw mem16\"); FLDCW(mem_addr); break;\n#if OP_SIZE == 32\n                    case 0xd96: TRACE(\"fnstenv mem32\"); FSTENV(mem_addr,32); break;\n#endif\n                    case 0xd97: TRACE(\"fnstcw mem16\"); FSTCW(mem_addr); break;\n                    case 0xda0: TRACE(\"fiadd mem32\"); FIADD(mem_addr,32); break;\n                    case 0xda1: TRACE(\"fimul mem32\"); FIMUL(mem_addr,32); break;\n                    case 0xda2: TRACE(\"ficom mem32\"); FICOM(mem_addr,32); break;\n                    case 0xda3: TRACE(\"ficomp mem32\"); FICOM(mem_addr,32); FPOP; break;\n                    case 0xda4: TRACE(\"fisub mem32\"); FISUB(mem_addr,32); break;\n                    case 0xda5: TRACE(\"fisubr mem32\"); FISUBR(mem_addr,32); break;\n                    case 0xda6: TRACE(\"fidiv mem32\"); FIDIV(mem_addr,32); break;\n                    case 0xda7: TRACE(\"fidivr mem32\"); FIDIVR(mem_addr,32); break;\n                    case 0xdb0: TRACE(\"fild mem32\"); FILD(mem_addr,32); break;\n                    case 0xdb2: TRACE(\"fist mem32\"); FIST(mem_addr,32); break;\n                    case 0xdb3: TRACE(\"fistp mem32\"); FIST(mem_addr,32); FPOP; break;\n                    case 0xdb5: TRACE(\"fld mem80\"); FLDM(mem_addr_real,80); break;\n                    case 0xdb7: TRACE(\"fstp mem80\"); FSTM(mem_addr_real,80); FPOP; break;\n                    case 0xdc0: TRACE(\"fadd mem64\"); FADDM(mem_addr_real,64); break;\n                    case 0xdc1: TRACE(\"fmul mem64\"); FMULM(mem_addr_real,64); break;\n                    case 0xdc2: TRACE(\"fcom mem64\"); FCOMM(mem_addr_real,64); break;\n                    case 0xdc3: TRACE(\"fcomp mem64\"); FCOMM(mem_addr_real,64); FPOP; break;\n                    case 0xdc4: TRACE(\"fsub mem64\"); FSUBM(mem_addr_real,64); break;\n                    case 0xdc5: TRACE(\"fsubr mem64\"); FSUBRM(mem_addr_real,64); break;\n                    case 0xdc6: TRACE(\"fdiv mem64\"); FDIVM(mem_addr_real,64); break;\n                    case 0xdc7: TRACE(\"fdivr mem64\"); FDIVRM(mem_addr_real,64); break;\n                    case 0xdd0: TRACE(\"fld mem64\"); FLDM(mem_addr_real,64); break;\n                    case 0xdd2: TRACE(\"fst mem64\"); FSTM(mem_addr_real,64); break;\n                    case 0xdd3: TRACE(\"fstp mem64\"); FSTM(mem_addr_real,64); FPOP; break;\n                    case 0xdd4: TRACE(\"frstor mem32\"); FRESTORE(mem_addr,32); break;\n                    case 0xdd6: TRACE(\"fnsave mem32\"); FSAVE(mem_addr,32); break;\n                    case 0xde0: TRACE(\"fiadd mem16\"); FIADD(mem_addr,16); break;\n                    case 0xde1: TRACE(\"fimul mem16\"); FIMUL(mem_addr,16); break;\n                    case 0xde2: TRACE(\"ficom mem16\"); FICOM(mem_addr,16); break;\n                    case 0xde3: TRACE(\"ficomp mem16\"); FICOM(mem_addr,16); FPOP; break;\n                    case 0xde4: TRACE(\"fisub mem16\"); FISUB(mem_addr,16); break;\n                    case 0xde5: TRACE(\"fisubr mem16\"); FISUBR(mem_addr,16); break;\n                    case 0xde6: TRACE(\"fidiv mem16\"); FIDIV(mem_addr,16); break;\n                    case 0xde7: TRACE(\"fidivr mem16\"); FIDIVR(mem_addr,16); break;\n                    case 0xdf0: TRACE(\"fild mem16\"); FILD(mem_addr,16); break;\n                    case 0xdf2: TRACE(\"fist mem16\"); FIST(mem_addr,16); break;\n                    case 0xdf3: TRACE(\"fistp mem16\"); FIST(mem_addr,16); FPOP; break;\n                    case 0xdf5: TRACE(\"fild mem64\"); FILD(mem_addr,64); break;\n                    case 0xdf7: TRACE(\"fistp mem64\"); FIST(mem_addr,64); FPOP; break;\n                    default: TRACE(\"undefined\"); UNDEFINED;\n                }\n            } else {\n                switch (insn << 4 | modrm.opcode) {\n                    case 0xd80: TRACE(\"fadd st(i), st\"); FADD(st_i, st_0); break;\n                    case 0xd81: TRACE(\"fmul st(i), st\"); FMUL(st_i, st_0); break;\n                    case 0xd82: TRACE(\"fcom st\"); FCOM(); break;\n                    case 0xd83: TRACE(\"fcomp st\"); FCOM(); FPOP; break;\n                    case 0xd84: TRACE(\"fsub st(i), st\"); FSUB(st_i, st_0); break;\n                    case 0xd85: TRACE(\"fsubr st(i), st\"); FSUBR(st_i, st_0); break;\n                    case 0xd86: TRACE(\"fdiv st(i), st\"); FDIV(st_i, st_0); break;\n                    case 0xd87: TRACE(\"fdivr st(i), st\"); FDIVR(st_i, st_0); break;\n                    case 0xd90: TRACE(\"fld st(i)\"); FLD(); break;\n                    case 0xd91: TRACE(\"fxch st\"); FXCH(); break;\n                    case 0xda0: TRACE(\"fcmovb st, st(i)\"); FCMOVB(st_i); break;\n                    case 0xda1: TRACE(\"fcmove st, st(i)\"); FCMOVE(st_i); break;\n                    case 0xda2: TRACE(\"fcmovbe st, st(i)\"); FCMOVBE(st_i); break;\n                    case 0xda3: TRACE(\"fcmovu st, st(i)\"); FCMOVU(st_i); break;\n                    case 0xdb0: TRACE(\"fcmovnb st, st(i)\"); FCMOVNB(st_i); break;\n                    case 0xdb1: TRACE(\"fcmovne st, st(i)\"); FCMOVNE(st_i); break;\n                    case 0xdb2: TRACE(\"fcmovnbe st, st(i)\"); FCMOVNBE(st_i); break;\n                    case 0xdb3: TRACE(\"fcmovnu st, st(i)\"); FCMOVNU(st_i); break;\n                    case 0xdb5: TRACE(\"fucomi st\"); FUCOMI(); break;\n                    case 0xdb6: TRACE(\"fcomi st\"); FCOMI(); break;\n                    case 0xdc0: TRACE(\"fadd st, st(i)\"); FADD(st_0, st_i); break;\n                    case 0xdc1: TRACE(\"fmul st, st(i)\"); FMUL(st_0, st_i); break;\n                    case 0xdc4: TRACE(\"fsubr st, st(i)\"); FSUBR(st_0, st_i); break;\n                    case 0xdc5: TRACE(\"fsub st, st(i)\"); FSUB(st_0, st_i); break;\n                    case 0xdc6: TRACE(\"fdivr st, st(i)\"); FDIVR(st_0, st_i); break;\n                    case 0xdc7: TRACE(\"fdiv st, st(i)\"); FDIV(st_0, st_i); break;\n                    case 0xdd0: TRACE(\"ffree st(i) (lol)\"); break;\n                    case 0xdd2: TRACE(\"fst st\"); FST(); break;\n                    case 0xdd3: TRACE(\"fstp st\"); FST(); FPOP; break;\n                    case 0xdd4: TRACE(\"fucom st\"); FUCOM(); break;\n                    case 0xdd5: TRACE(\"fucomp st\"); FUCOM(); FPOP; break;\n                    case 0xda5: TRACE(\"fucompp st\"); FUCOM(); FPOP; FPOP; break;\n                    case 0xde0: TRACE(\"faddp st, st(i)\"); FADD(st_0, st_i); FPOP; break;\n                    case 0xde1: TRACE(\"fmulp st, st(i)\"); FMUL(st_0, st_i); FPOP; break;\n                    case 0xde4: TRACE(\"fsubrp st, st(i)\"); FSUBR(st_0, st_i); FPOP; break;\n                    case 0xde5: TRACE(\"fsubp st, st(i)\"); FSUB(st_0, st_i); FPOP; break;\n                    case 0xde6: TRACE(\"fdivrp st, st(i)\"); FDIVR(st_0, st_i); FPOP; break;\n                    case 0xde7: TRACE(\"fdivp st, st(i)\"); FDIV(st_0, st_i); FPOP; break;\n                    case 0xdf0: TRACE(\"ffreep st(i) (omegalul)\"); FPOP; break;\n                    case 0xdf5: TRACE(\"fucomip st\"); FUCOMI(); FPOP; break;\n                    case 0xdf6: TRACE(\"fcomip st\"); FCOMI(); FPOP; break;\n                    default: switch (insn << 8 | modrm.opcode << 4 | modrm.rm_opcode) {\n                    case 0xd940: TRACE(\"fchs\"); FCHS(); break;\n                    case 0xd941: TRACE(\"fabs\"); FABS(); break;\n                    case 0xd944: TRACE(\"ftst\"); FTST(); break;\n                    case 0xd945: TRACE(\"fxam\"); FXAM(); break;\n                    case 0xd950: TRACE(\"fld1\"); FLDC(one); break;\n                    case 0xd951: TRACE(\"fldl2t\"); FLDC(log2t); break;\n                    case 0xd952: TRACE(\"fldl2e\"); FLDC(log2e); break;\n                    case 0xd953: TRACE(\"fldpi\"); FLDC(pi); break;\n                    case 0xd954: TRACE(\"fldlg2\"); FLDC(log2); break;\n                    case 0xd955: TRACE(\"fldln2\"); FLDC(ln2); break;\n                    case 0xd956: TRACE(\"fldz\"); FLDC(zero); break;\n                    case 0xd960: TRACE(\"f2xm1\"); F2XM1(); break;\n                    case 0xd961: TRACE(\"fyl2x\"); FYL2X(); break;\n                    case 0xd963: TRACE(\"fpatan\"); FPATAN(); break;\n                    case 0xd964: TRACE(\"fxtract\"); FXTRACT(); break;\n                    case 0xd967: TRACE(\"fincstp\"); FINCSTP(); break;\n                    case 0xd970: TRACE(\"fprem\"); FPREM(); break;\n                    case 0xd972: TRACE(\"fsqrt\"); FSQRT(); break;\n                    case 0xd974: TRACE(\"frndint\"); FRNDINT(); break;\n                    case 0xd975: TRACE(\"fscale\"); FSCALE(); break;\n                    case 0xd976: TRACE(\"fsin\"); FSIN(); break;\n                    case 0xd977: TRACE(\"fcos\"); FCOS(); break;\n                    case 0xdb42: TRACE(\"fnclex\"); FCLEX(); break;\n                    case 0xde31: TRACE(\"fcompp\"); FCOM(); FPOP; FPOP; break;\n                    case 0xdf40: TRACE(\"fnstsw ax\"); FSTSW(reg_a); break;\n                    default: TRACE(\"undefined\"); UNDEFINED;\n                }}\n            }\n            break;\n\n        case 0xe3: TRACEI(\"jcxz rel8\\t\");\n                   READIMM8; JCXZ_REL(imm); break;\n\n        case 0xe8: TRACEI(\"call near\\t\");\n                   READIMM; CALL_REL(imm); break;\n\n        case 0xe9: TRACEI(\"jmp rel\\t\");\n                   READIMM; JMP_REL(imm); break;\n        case 0xeb: TRACEI(\"jmp rel8\\t\");\n                   READIMM8; JMP_REL(imm); break;\n\n        // lock\n        case 0xf0:\n            lockrestart:\n            READINSN;\n            switch (insn) {\n                case 0x65: TRACE(\"segment gs\\n\"); SEG_GS(); goto lockrestart;\n\n                case 0x66:\n                    // I didn't think this through\n#if OP_SIZE == 32\n                    TRACE(\"locked 16-bit mode\\n\");\n                    RESTORE_IP;\n                    return glue(DECODER_NAME, 16)(DECODER_PASS_ARGS);\n#else\n                    goto lockrestart;\n#endif\n\n\n\n#define MAKE_OP_ATOMIC(x, OP, op) \\\n        case x+0x0: TRACEI(\"lock \" op \" reg8, modrm8\"); \\\n                   READMODRM_MEM; ATOMIC_##OP(modrm_reg, modrm_val,8); break; \\\n        case x+0x1: TRACEI(\"lock \" op \" reg, modrm\"); \\\n                   READMODRM_MEM; ATOMIC_##OP(modrm_reg, modrm_val,oz); break; \\\n\n                MAKE_OP_ATOMIC(0x00, ADD, \"add\");\n                MAKE_OP_ATOMIC(0x08, OR, \"or\");\n                MAKE_OP_ATOMIC(0x10, ADC, \"adc\");\n                MAKE_OP_ATOMIC(0x18, SBB, \"sbb\");\n                MAKE_OP_ATOMIC(0x20, AND, \"and\");\n                MAKE_OP_ATOMIC(0x28, SUB, \"sub\");\n                MAKE_OP_ATOMIC(0x30, XOR, \"xor\");\n\n#undef MAKE_OP_ATOMIC\n\n#define GRP1_ATOMIC(src, dst,z) \\\n    switch (modrm.opcode) { \\\n        case 0: TRACE(\"lock add\"); ATOMIC_ADD(src, dst,z); break; \\\n        case 1: TRACE(\"lock or\");  ATOMIC_OR(src, dst,z); break; \\\n        case 2: TRACE(\"lock adc\"); ATOMIC_ADC(src, dst,z); break; \\\n        case 3: TRACE(\"lock sbb\"); ATOMIC_SBB(src, dst,z); break; \\\n        case 4: TRACE(\"lock and\"); ATOMIC_AND(src, dst,z); break; \\\n        case 5: TRACE(\"lock sub\"); ATOMIC_SUB(src, dst,z); break; \\\n        case 6: TRACE(\"lock xor\"); ATOMIC_XOR(src, dst,z); break; \\\n        default: TRACE(\"undefined\"); UNDEFINED; \\\n    }\n\n                case 0x80: TRACEI(\"lock grp1 imm8, modrm8\");\n                           READMODRM_MEM; READIMM8; GRP1_ATOMIC(imm, modrm_val,8); break;\n                case 0x81: TRACEI(\"lock grp1 imm, modrm\");\n                           READMODRM_MEM; READIMM; GRP1_ATOMIC(imm, modrm_val,oz); break;\n                case 0x83: TRACEI(\"lock grp1 imm8, modrm\");\n                           READMODRM_MEM; READIMM8; GRP1_ATOMIC(imm, modrm_val,oz); break;\n\n#undef GRP1_ATOMIC\n\n                case 0x0f:\n                    READINSN;\n                    switch (insn) {\n                        case 0xab: TRACEI(\"lock bts reg, modrm\");\n                                   READMODRM; ATOMIC_BTS(modrm_reg, modrm_val,oz); break;\n                        case 0xb3: TRACEI(\"lock btr reg, modrm\");\n                                   READMODRM; ATOMIC_BTR(modrm_reg, modrm_val,oz); break;\n                        case 0xbb: TRACEI(\"lock btc reg, modrm\");\n                                   READMODRM; ATOMIC_BTC(modrm_reg, modrm_val,oz); break;\n\n#define GRP8_ATOMIC(bit, val,z) \\\n    switch (modrm.opcode) { \\\n        case 5: TRACEI(\"bts\"); ATOMIC_BTS(bit, val,z); break; \\\n        case 6: TRACEI(\"btr\"); ATOMIC_BTR(bit, val,z); break; \\\n        case 7: TRACEI(\"btc\"); ATOMIC_BTC(bit, val,z); break; \\\n        default: UNDEFINED; \\\n    }\n                        case 0xba: TRACEI(\"lock grp8 imm8, modrm\");\n                                   READMODRM; READIMM8; GRP8_ATOMIC(imm, modrm_val,oz); break;\n#undef GRP8_ATOMIC\n\n                        case 0xb0: TRACEI(\"lock cmpxchg reg8, modrm8\");\n                                   READMODRM_MEM; ATOMIC_CMPXCHG(modrm_reg, modrm_val,8); break;\n                        case 0xb1: TRACEI(\"lock cmpxchg reg, modrm\");\n                                   READMODRM_MEM; ATOMIC_CMPXCHG(modrm_reg, modrm_val,oz); break;\n\n                        case 0xc0: TRACEI(\"lock xadd reg8, modrm8\");\n                                   READMODRM_MEM; ATOMIC_XADD(modrm_reg, modrm_val,8); break;\n                        case 0xc1: TRACEI(\"lock xadd reg, modrm\");\n                                   READMODRM_MEM; ATOMIC_XADD(modrm_reg, modrm_val,oz); break;\n\n                        case 0xc7: READMODRM_MEM; switch (modrm.opcode) {\n                                       case 1: TRACEI(\"lock cmpxchg8b modrm\");\n                                               ATOMIC_CMPXCHG8B(modrm_val,64); break;\n                                       default: UNDEFINED;\n                                   };\n                                   break;\n                        default: TRACE(\"undefined\"); UNDEFINED;\n                    }\n                    break;\n\n#define GRP5_ATOMIC(val,z) \\\n    switch (modrm.opcode) { \\\n        case 0: TRACE(\"lock inc\"); ATOMIC_INC(val,z); break; \\\n        case 1: TRACE(\"lock dec\"); ATOMIC_DEC(val,z); break; \\\n        default: TRACE(\"undefined\"); UNDEFINED; \\\n    }\n\n                case 0xfe: TRACEI(\"lock grp5 modrm8\\t\");\n                           READMODRM_MEM; GRP5_ATOMIC(modrm_val,8); break;\n                case 0xff: TRACEI(\"lock grp5 modrm\\t\");\n                           READMODRM_MEM; GRP5_ATOMIC(modrm_val,oz); break;\n\n                default: TRACE(\"undefined\"); UNDEFINED;\n            }\n            break;\n\n        case 0xf2:\n            READINSN;\n            switch (insn) {\n                case 0x0f:\n                    READINSN;\n                    switch (insn) {\n                        case 0x10: TRACEI(\"movsd xmm:modrm, xmm\");\n                                   READMODRM; VMOV_MERGE_REG(xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x11: TRACEI(\"movsd xmm, xmm:modrm\");\n                                   READMODRM; VMOV_MERGE_REG(xmm_modrm_reg, xmm_modrm_val,64); break;\n\n                        case 0x2a: TRACEI(\"cvtsi2sd modrm, xmm\");\n                                   READMODRM; V_OP(cvtsi2sd, modrm_val, xmm_modrm_reg,32); break;\n                        case 0x2c: TRACEI(\"cvttsd2si reg, xmm:modrm\");\n                                   READMODRM; V_OP(cvttsd2si, xmm_modrm_val, modrm_reg,64); break;\n                        case 0x5a: TRACEI(\"cvtsd2ss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(cvtsd2ss, xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                        case 0x51: TRACEI(\"sqrtsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fsqrt, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x58: TRACEI(\"addsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fadd, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x59: TRACEI(\"mulsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmul, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x5c: TRACEI(\"subsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fsub, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x5d: TRACEI(\"minsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmin, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x5e: TRACEI(\"divsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fdiv, xmm_modrm_val, xmm_modrm_reg,64); break;\n                        case 0x5f: TRACEI(\"maxsd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmax, xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                        case 0x70: TRACEI(\"pshuflw xmm:modrm, xmm, imm8\");\n                                   READMODRM; READIMM8; V_OP_IMM(shuffle_lw, xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                        case 0xc2: TRACEI(\"cmpsd xmm:modrm, xmm, imm8\");\n                                   READMODRM; READIMM8; V_OP_IMM(single_fcmp, xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                        case 0x18 ... 0x1f: TRACEI(\"rep nop modrm\\t\"); READMODRM; break;\n                        default: TRACE(\"undefined\"); UNDEFINED;\n                    }\n                    break;\n\n                case 0xa6: TRACEI(\"repnz cmpsb\"); REPNZ(cmps, 8); break;\n                case 0xa7: TRACEI(\"repnz cmps\"); REPNZ(cmps, oz); break;\n                case 0xae: TRACEI(\"repnz scasb\"); REPNZ(scas, 8); break;\n                case 0xaf: TRACEI(\"repnz scas\"); REPNZ(scas, oz); break;\n                default: TRACE(\"undefined\"); UNDEFINED;\n            }\n            break;\n\n        case 0xf3:\n            READINSN;\n            switch (insn) {\n                case 0x0f:\n                    // 2-byte opcode prefix\n                    // after a rep prefix, means we have sse/mmx insanity\n                    READINSN;\n                    switch (insn) {\n                        case 0x10: TRACEI(\"movss xmm:modrm, xmm\");\n                                   READMODRM; VMOV_MERGE_REG(xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x11: TRACEI(\"movss xmm, xmm:modrm\");\n                                   READMODRM; VMOV_MERGE_REG(xmm_modrm_reg, xmm_modrm_val,32); break;\n\n                        case 0x2a: TRACEI(\"cvtsi2ss modrm, xmm\");\n                                   READMODRM; V_OP(cvtsi2ss, modrm_val, xmm_modrm_reg,32); break;\n                        case 0x2c: TRACEI(\"cvttss2si reg, xmm:modrm\");\n                                   READMODRM; V_OP(cvttss2si, xmm_modrm_val, modrm_reg,32); break;\n                        case 0x51: TRACEI(\"sqrtss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fsqrt, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5a: TRACEI(\"cvtss2sd xmm:modrm, xmm\");\n                                   READMODRM; V_OP(cvtss2sd, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5b: TRACEI(\"cvttps2dq xmm:modrm, xmm\");\n                                   READMODRM; V_OP(cvttps2dq, xmm_modrm_val, xmm_modrm_reg,32); break;\n\n                        case 0x58: TRACEI(\"addss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fadd, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x59: TRACEI(\"mulss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmul, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5c: TRACEI(\"subss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fsub, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5d: TRACEI(\"minss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmin, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5e: TRACEI(\"divss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fdiv, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x5f: TRACEI(\"maxss xmm:modrm, xmm\");\n                                   READMODRM; V_OP(single_fmax, xmm_modrm_val, xmm_modrm_reg,32); break;\n                        case 0x6f: TRACEI(\"movdqu xmm:modrm, xmm\");\n                                   READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                        case 0x7e: TRACEI(\"movq xmm:modrm, xmm\");\n                                   READMODRM; VMOV(xmm_modrm_val, xmm_modrm_reg,64); break;\n\n                        case 0x18 ... 0x1f: TRACEI(\"repz nop modrm\\t\"); READMODRM; break;\n\n                        case 0x70: TRACEI(\"pshufhw xmm:modrm, xmm, imm8\");\n                                   READMODRM; READIMM8; V_OP_IMM(shuffle_hw, xmm_modrm_val, xmm_modrm_reg,128); break;\n\n                        case 0x7f: TRACEI(\"movdqu xmm, xmm:modrm\");\n                                   READMODRM; VMOV(xmm_modrm_reg, xmm_modrm_val,128); break;\n\n                        // tzcnt is like bsf but the result when the input is zero is defined as the operand size\n                        // for now, it can just be an alias\n                        case 0xbc: TRACEI(\"~~tzcnt~~ bsf modrm, reg\");\n                                   READMODRM; BSF(modrm_val, modrm_reg,oz); break;\n                        case 0xbd: TRACEI(\"~~lzcnt~~ bsr modrm, reg\");\n                                   READMODRM; BSR(modrm_val, modrm_reg,oz); break;\n\n                        case 0xc2: TRACEI(\"cmpss xmm:modrm, xmm, imm8\");\n                                   READMODRM; READIMM8; V_OP_IMM(single_fcmp, xmm_modrm_val, xmm_modrm_reg,32); break;\n\n                        default: TRACE(\"undefined\"); UNDEFINED;\n                    }\n                    break;\n\n                case 0x90: TRACEI(\"pause\"); break;\n\n                case 0xa4: TRACEI(\"rep movsb\"); REP(movs, 8); break;\n                case 0xa5: TRACEI(\"rep movs\"); REP(movs, oz); break;\n                case 0xa6: TRACEI(\"repz cmpsb\"); REPZ(cmps, 8); break;\n                case 0xa7: TRACEI(\"repz cmps\"); REPZ(cmps, oz); break;\n                case 0xaa: TRACEI(\"rep stosb\"); REP(stos, 8); break;\n                case 0xab: TRACEI(\"rep stos\"); REP(stos, oz); break;\n                case 0xac: TRACEI(\"rep lodsb\"); REP(lods, 8); break;\n                case 0xad: TRACEI(\"rep lods\"); REP(lods, oz); break;\n                case 0xae: TRACEI(\"repz scasb\"); REPZ(scas, 8); break;\n                case 0xaf: TRACEI(\"repz scas\"); REPZ(scas, oz); break;\n\n                // repz ret is equivalent to ret but on some amd chips there's\n                // a branch prediction penalty if the target of a branch is a\n                // ret. gcc used to use nop ret but repz ret is only one\n                // instruction\n                case 0xc3: TRACEI(\"repz ret\\t\"); RET_NEAR(0); break;\n                default: TRACE(\"undefined\\n\"); UNDEFINED;\n            }\n            break;\n\n#define GRP3(val,z) \\\n    switch (modrm.opcode) { \\\n        case 0: \\\n        case 1: TRACE(\"test imm \"); \\\n                READIMM##z; TEST(imm, val,z); break; \\\n        case 2: TRACE(\"not\"); \\\n                NOT(val,z); break; \\\n        case 3: TRACE(\"neg\"); \\\n                NEG(val,z); break; \\\n        case 4: TRACE(\"mul\"); \\\n                MUL1(modrm_val,z); break; \\\n        case 5: TRACE(\"imul\"); \\\n                IMUL1(modrm_val,z); break; \\\n        case 6: TRACE(\"div\"); \\\n                DIV(modrm_val,z); break; \\\n        case 7: TRACE(\"idiv\"); \\\n                IDIV(modrm_val,z); break; \\\n        default: TRACE(\"undefined\"); UNDEFINED; \\\n    }\n\n        case 0xf6: TRACEI(\"grp3 modrm8\\t\");\n                   READMODRM; GRP3(modrm_val,8); break;\n        case 0xf7: TRACEI(\"grp3 modrm\\t\");\n                   READMODRM; GRP3(modrm_val,oz); break;\n\n#undef GRP3\n\n        case 0xfc: TRACEI(\"cld\"); CLD; break;\n        case 0xfd: TRACEI(\"std\"); STD; break;\n\n#define GRP5(val,z) \\\n    switch (modrm.opcode) { \\\n        case 0: TRACE(\"inc\"); \\\n                INC(val,z); break; \\\n        case 1: TRACE(\"dec\"); \\\n                DEC(val,z); break; \\\n        case 2: TRACE(\"call indirect near\"); \\\n                CALL(val); break; \\\n        case 3: TRACE(\"call indirect far\"); UNDEFINED; \\\n        case 4: TRACE(\"jmp indirect near\"); \\\n                JMP(val); break; \\\n        case 5: TRACE(\"jmp indirect far\"); UNDEFINED; \\\n        case 6: TRACE(\"push\"); \\\n                PUSH(val,z); break; \\\n        case 7: TRACE(\"undefined\"); UNDEFINED; \\\n    }\n\n        case 0xfe: TRACEI(\"grp5 modrm8\\t\");\n                   READMODRM; GRP5(modrm_val,8); break;\n        case 0xff: TRACEI(\"grp5 modrm\\t\");\n                   READMODRM; GRP5(modrm_val,oz); break;\n\n#undef GRP5\n\n        default:\n            TRACE(\"undefined\\n\");\n            UNDEFINED;\n    }\n    TRACE(\"\\n\");\n    FINISH;\n}\n"
  },
  {
    "path": "emu/float80-test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <math.h>\n#include <fenv.h>\n#include \"float80.h\"\n\n#pragma GCC diagnostic ignored \"-Wliteral-range\"\n\n//#define DENORMAL 1e-310\n#define DENORMAL 1.11253692925360069155e-308\n\nint deconst_dummy;\n#define deconst(x) (deconst_dummy? 0 : x)\n\n#define neg(x) (x == 0 ? -1e-200/1e200 : -x)\n\nunion f80 {\n    float80 f;\n    long double ld;\n};\n\nunion f80 gf;\n\nstatic bool bitwise_eq(long double a, long double b) {\n    union f80 ua = (union f80) a;\n    union f80 ub = (union f80) b;\n    return ua.f.signif == ub.f.signif && ua.f.signExp == ub.f.signExp;\n}\n\nstatic int tests_passed = 0;\nstatic int tests_total = 0;\nstatic int suite_passed = 0;\nstatic int suite_total = 0;\n\n#define suite_start() _suite_start(__FUNCTION__)\n#define suite_end() _suite_end(__FUNCTION__)\nvoid _suite_start(const char *suite) {\n    const char *rounding_mode_str;\n    switch (fegetround()) {\n        case FE_TONEAREST: rounding_mode_str = \"nearest/even\"; break;\n        case FE_DOWNWARD: rounding_mode_str = \"down\"; break;\n        case FE_UPWARD: rounding_mode_str = \"up\"; break;\n        case FE_TOWARDZERO: rounding_mode_str = \"towards zero\"; break;\n        default: abort();\n    }\n    printf(\"==== %s, round %s ====\\n\", suite, rounding_mode_str);\n    suite_passed = 0;\n    suite_total = 0;\n}\nvoid _suite_end(const char *suite) {\n    printf(\"%s: %d/%d passed (%.0f%%)\\n\", suite, suite_passed, suite_total, (double) suite_passed / suite_total * 100);\n}\n\nvoid assertf(int cond, const char *msg, ...) {\n    tests_total++;\n    suite_total++;\n    if (cond) {\n        tests_passed++;\n        suite_passed++;\n    }\n\n    printf(cond ? \"PASS \": \"FAIL \");\n    char buf[1024];\n    va_list args;\n    va_start(args, msg);\n    vsprintf(buf, msg, args);\n    va_end(args);\n    puts(buf);\n}\n\nvoid test_int_convert() {\n    suite_start();\n    union f80 u;\n    int64_t i;\n#define test(x) \\\n    u.f = f80_from_int(x); \\\n    assertf((int64_t) lrintl(u.ld) == x, \"f80_from_int(%ld) = %.20Le\", (int64_t) x, u.ld); \\\n    i = f80_to_int(u.f); \\\n    assertf(i == x, \"f80_to_int(%.20Le) = %ld\", u.ld, i)\n\n    test(0);\n    test(123); test(-123);\n    test(9489919999192); test(-9489919999192);\n    test(INT64_MIN); test(INT64_MAX);\n#undef test\n\n#define test(x) \\\n    u.f = f80_from_double(x); \\\n    i = f80_to_int(u.f); \\\n    assertf(i == (int64_t) lrintl(x), \"f80_to_int(f80_from_double(%.20Le)) = %ld\", u.ld, i)\n\n    test(0.75); test(-0.75);\n#undef test\n\n    suite_end();\n}\n\nvoid test_double_convert() {\n    suite_start();\n    union f80 u;\n    double d;\n#define test(x) \\\n    u.f = f80_from_double(x); \\\n    assertf(bitwise_eq(u.ld, x), \"f80_from_double(%e) = %Le\", (double) x, u.ld); \\\n    d = f80_to_double(u.f); \\\n    assertf(bitwise_eq(d, x), \"f80_to_double(%Le) = %e\", u.ld, d)\n\n    test(0); test(-0);\n    test(123); test(-123);\n    test(3991994929919994995881.0);\n    test(9.223372036854776e18);\n    test(DENORMAL);\n    test(1e-310);\n    test(INFINITY); test(-INFINITY);\n    test(NAN);\n#undef test\n    suite_end();\n}\n\nvoid test_round() {\n    suite_start();\n    union f80 u, ur;\n    long double r;\n#define test(x) \\\n    u.ld = x; \\\n    ur.f = f80_round(u.f); \\\n    r = rintl(u.ld); \\\n    assertf(bitwise_eq(r, ur.ld), \"f80_round(%Le) = %Le (%Le)\", (long double) x, ur.ld, r)\n\n    test(0); test(-0);\n    test(1./2); test(-1./2);\n    test(1./65536); test(-1./65536);\n    test(1e-100); test(-1e-100);\n    test(1.5); test(-1.5);\n    test(123); test(-123);\n    test(3991994929919994995881.123);\n    test(9.223372036854776e18);\n    test(1e-4949l);\n    test(DENORMAL);\n    test(1e-310);\n    test(INFINITY); test(-INFINITY);\n    test(NAN);\n#undef test\n    suite_end();\n}\n\nvoid test_math() {\n    suite_start();\n    union f80 ua, ub, u;\n    long double expected;\n#define cop_add +\n#define cop_sub -\n#define cop_mul *\n#define cop_div /\n#define _test(op, a, b) \\\n    ua.ld = a; ub.ld = b; \\\n    u.f = f80_##op(ua.f, ub.f); \\\n    expected = deconst((long double) a) cop_##op deconst((long double) b); \\\n    assertf(bitwise_eq(u.ld, expected) || (isnan(u.ld) && isnan(expected)) || (ua.ld == 0 && ub.ld == 0 && u.ld == 0 && expected == 0), \"f80_\"#op\"(%.20Le, %.20Le) = %.20Le (%.20Le)\", ua.ld, ub.ld, u.ld, expected)\n#define test(op, a, b) \\\n    _test(op, a, b); \\\n    _test(op, neg(a), b); \\\n    _test(op, a, neg(b)); \\\n    _test(op, neg(a), neg(b))\n\n    test(add, 1, 1);\n    test(add, 123, 123);\n    test(add, 9942, 13459);\n    test(add, 222, 0.);\n    test(add, 0., 0.);\n    test(add, 12.0499, 91999);\n    test(add, 1e100, 100);\n    test(add, 1e-4949l, 1);\n    test(add, 1e-4949l, 1e-4949l);\n    test(add, 1e-4949l, 2e-4949l);\n    test(add, 18446744073709551616.l, 1.5);\n    test(add, 1e4932l, 1e4932l);\n    test(add, INFINITY, 1);\n    test(add, INFINITY, 123);\n    test(add, INFINITY, INFINITY);\n    test(add, NAN, 123);\n    test(add, NAN, NAN);\n\n    test(mul, 0, 1);\n    test(mul, 1, 1);\n    test(mul, 123, 123);\n    test(mul, 1e100, 100);\n    test(mul, 12.3993l, 91934);\n    test(mul, 1e-4949l, 1);\n    test(mul, 1e-4949l, 1e-4949l);\n    test(mul, INFINITY, 11);\n    test(mul, INFINITY, INFINITY);\n    test(mul, INFINITY, 0);\n    test(mul, NAN, 123);\n    test(mul, NAN, NAN);\n\n    test(div, 1, 1);\n    test(div, 3, 2);\n    test(div, 0, 1);\n    test(div, 0, 0);\n    test(div, 1, 1e1000l);\n    test(div, 1, 1e-1000l);\n    test(div, 12.4123331, 934.11223e200);\n    test(div, 1288490188200, 210);\n    test(div, 1e-4949l, 10);\n    test(div, 10, 1e-4949l);\n    test(div, 1e-4949l, 1e-4949l);\n    test(div, INFINITY, 100);\n    test(div, 100, INFINITY);\n    test(div, INFINITY, INFINITY);\n    test(div, INFINITY, 0);\n    test(div, NAN, 123);\n    test(div, NAN, NAN);\n#undef test\n#undef _test\n    suite_end();\n}\n\nvoid test_compare() {\n    suite_start();\n    union f80 ua, ub;\n    bool expected, actual;\n#define cop_eq ==\n#define cop_lt <\n#define _test(op, a, b) \\\n    ua.ld = a; ub.ld = b; \\\n    actual = f80_##op(ua.f, ub.f); \\\n    expected = (long double) a cop_##op (long double) b; \\\n    assertf(actual == expected, \"f80_\"#op\"(%Le, %Le) = %s\", ua.ld, ub.ld, actual ? \"true\" : \"false\")\n#define test(op, a, b) \\\n    _test(op, a, b); \\\n    _test(op, neg(a), b); \\\n    _test(op, a, neg(b)); \\\n    _test(op, neg(a), neg(b))\n\n    test(eq, 0, 0);\n    test(eq, 1, 1);\n    test(eq, 0, 1);\n    test(eq, 1, 0);\n    test(eq, INFINITY, INFINITY);\n    test(eq, 1, INFINITY);\n    test(eq, NAN, 123);\n    test(eq, NAN, NAN);\n    test(lt, 0, 0);\n    test(lt, 1, 1);\n    test(lt, 0, 1);\n    test(lt, 1, 0);\n    test(lt, INFINITY, INFINITY);\n    test(lt, 1, INFINITY);\n    test(lt, NAN, 123);\n    test(lt, NAN, NAN);\n#undef test\n    suite_end();\n}\n\nuint64_t fnmulh(uint64_t a, uint64_t b) {\n    return ((unsigned __int128) a * b) >> 64;\n}\n\nint main() {\n    for (int rounding_mode = 0; rounding_mode < 4; rounding_mode++) {\n        f80_rounding_mode = rounding_mode;\n        switch (rounding_mode) {\n            case round_to_nearest: fesetround(FE_TONEAREST); break;\n            case round_down: fesetround(FE_DOWNWARD); break;\n            case round_up: fesetround(FE_UPWARD); break;\n            case round_chop: fesetround(FE_TOWARDZERO); break;\n        }\n        fesetround(rounding_mode);\n\n        test_int_convert();\n        test_double_convert();\n        test_round();\n        test_math();\n        test_compare();\n    }\n    printf(\"%d/%d passed (%.0f%%)\", tests_passed, tests_total, (double) tests_passed / tests_total * 100);\n    return tests_passed == tests_total ? 0 : 1;\n}\n"
  },
  {
    "path": "emu/float80.c",
    "content": "#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <math.h>\n#include \"float80.h\"\n#include \"misc.h\"\n\ntypedef unsigned __int128 uint128_t;\n\n// If you don't understand why something is the way it is, change it and run\n// the test suite and all will become clear.\n\n// exponent is stored with a constant added to it, because that's apparently\n// easier than just saying the exponent is two's complement\n#define BIAS80 0x3fff\n#define EXP_MAX 0x7ffe\n#define EXP_MIN 0x0001\n#define EXP_SPECIAL 0x7fff\n#define EXP_DENORMAL 0\nstatic unsigned bias(int exp) {\n    return exp + BIAS80;\n}\nstatic int unbias(unsigned exp) {\n    return exp - BIAS80;\n}\n// returns the correct answer for denormal numbers\nstatic int unbias_denormal(unsigned exp) {\n    if (exp == EXP_DENORMAL)\n        return unbias(EXP_MIN);\n    return unbias(exp);\n}\n\n#define CURSED_BIT (1ul << 63)\n\n__thread enum f80_rounding_mode f80_rounding_mode;\n\nstatic bool round_away_from_zero(int sign) {\n    return (f80_rounding_mode == round_up && !sign) ||\n        (f80_rounding_mode == round_down && sign);\n}\n\n// shift a 128 bit integer right but using the floating point rounding mode\n// used by f80_shift_right and to round the 128-bit result of multiplying significands\n// sign is necessary to decide which way to round when mode is round_up or round_down\nstatic uint128_t u128_shift_right_round(uint128_t i, int shift, int sign) {\n    // we're going to be shifting stuff by shift - 1, so stay safe\n    if (shift == 0)\n        return i;\n    if (shift > 127) {\n        // If we should be rounding away from zero, and there are any nonzero\n        // bits involved, an infinite amount of right shift should give 1\n        if (round_away_from_zero(sign) && i != 0)\n            return 1;\n        return 0;\n    }\n\n    // stuff necessary for rounding to nearest or even. reference: https://stackoverflow.com/a/8984135\n    // grab the guard bit, the last bit shifted out\n    int guard = (i >> (shift - 1)) & 1;\n    // now grab the rest of the bits being shifted out\n    uint64_t rest = i & ~((uint128_t) -1 << (shift - 1));\n\n    i >>= shift;\n    // if all the bits shifted out were zeroes, we're done\n    if (guard == 0 && rest == 0)\n        return i;\n\n    if (round_away_from_zero(sign)) {\n        i++;\n    } else if (f80_rounding_mode == round_to_nearest && guard) {\n        if (rest != 0)\n            i++; // round up\n        else if (i & 1)\n            i++; // round to nearest even\n    }\n    return i;\n}\n\n// may overflow\nstatic float80 f80_shift_left(float80 f, int shift) {\n    f.signif <<= shift;\n    f.exp -= shift;\n    return f;\n}\n\n// may lose precision\nstatic float80 f80_shift_right(float80 f, int shift) {\n    f.signif = u128_shift_right_round(f.signif, shift, f.sign);\n    f.exp += shift;\n    return f;\n}\n\n// a number is unsupported if the cursed bit (first bit of the significand,\n// also known as the integer bit) is incorrect. it must be 0 for denormals and\n// 1 for any other type of number.\nbool f80_is_supported(float80 f) {\n    if (f.exp == EXP_DENORMAL)\n        return f.signif >> 63 == 0;\n    return f.signif >> 63 == 1;\n}\n\nbool f80_isnan(float80 f) {\n    return f.exp == EXP_SPECIAL && (f.signif & (-1ul >> 1)) != 0;\n}\nbool f80_isinf(float80 f) {\n    return f.exp == EXP_SPECIAL && (f.signif & (-1ul >> 1)) == 0;\n}\nbool f80_iszero(float80 f) {\n    return f.exp == EXP_DENORMAL && f.signif == 0;\n}\nbool f80_isdenormal(float80 f) {\n    return f.exp == EXP_DENORMAL && f.signif != 0;\n}\n\nstatic float80 f80_normalize(float80 f) {\n    // this function probably can't handle unsupported numbers\n    // except cursed normals which are just unnormals, and working with them is the point of this function\n    if (f.exp == EXP_DENORMAL || f.exp == EXP_SPECIAL)\n        assert(f80_is_supported(f));\n\n    // denormals (and zero) are already normalized (unlike the name suggests)\n    if (f.exp == EXP_DENORMAL)\n        return f;\n    // shift left as many times as possible without overflow\n    // number of leading zeroes = how many times we can shift out a leading digit before overflow\n    int shift;\n    if (f.signif != 0)\n        shift = __builtin_clzl(f.signif);\n    else\n        shift = 64; // __builtin_clzl has undefined result with zero\n    if (f.exp - shift < EXP_MIN) {\n        // if we shifted this much, exponent would go below its minimum\n        // so shift as much as possible and create a denormal\n        f = f80_shift_left(f, f.exp - EXP_MIN);\n        f.exp = EXP_DENORMAL;\n        return f;\n    }\n    return f80_shift_left(f, shift);\n}\n\nstatic int u128_clz(uint128_t x) {\n    // correctly counting leading zeros on a 128-bit int is interesting\n    int zeros;\n    if (x >> 64 != 0)\n        zeros = __builtin_clzl((uint64_t) (x >> 64));\n    else if (x != 0)\n        zeros = 64 + __builtin_clzl((uint64_t) x);\n    else\n        zeros = 128;\n    return zeros;\n}\n\nstatic float80 u128_normalize_round(uint128_t signif, int exp, int sign) {\n    int shift = u128_clz(signif);\n    // now shift left\n    if (exp - shift < unbias(EXP_MIN)) {\n        if (exp > unbias(EXP_MIN))\n            signif <<= exp - unbias(EXP_MIN);\n        else\n            signif = u128_shift_right_round(signif, unbias(EXP_MIN) - exp, sign);\n        exp = unbias(EXP_DENORMAL);\n    } else if (exp - shift > unbias(EXP_MAX)) {\n        //printf(\"0x%.16llx%.16llx \", (unsigned long long) (signif >> 64), (unsigned long long) signif);\n        // too big to represent, so either construct infinity, or round it to the largest representable number\n        float80 f;\n        if (signif == ((uint128_t) 1 << 127))\n            f = F80_INF;\n        else if ((f80_rounding_mode == round_up && sign) || (f80_rounding_mode == round_down && !sign) || f80_rounding_mode == round_chop)\n            f = (float80) {.exp = EXP_MAX, .signif = -1};\n        else\n            f = F80_INF;\n        f.sign = sign;\n        return f;\n    } else {\n        signif <<= shift;\n        exp -= shift;\n    }\n    // and round\n    float80 f;\n    f.exp = bias(exp);\n    // hack around cases where u128_shift_right_round returns 0x10000000000000000\n    // such as signif = 0xffffffffffffffff0000000000000000\n    signif = u128_shift_right_round(signif, 64, sign);\n    if (signif >> 64 != 0) {\n        signif >>= 1;\n        f.exp++;\n    }\n    f.signif = signif;\n    f.sign = sign;\n    return f;\n}\n\nfloat80 f80_from_int(int64_t i) {\n    // stick i in the significand, give it an exponent of 2^63 to offset the\n    // implicit binary point after the first bit, and then normalize\n    float80 f = {\n        .signif = i,\n        .exp = bias(63),\n        .sign = 0,\n    };\n    if (i == 0)\n        f.exp = 0;\n    if (i < 0) {\n        f.sign = 1;\n        f.signif = -(uint64_t) i;\n    }\n    return f80_normalize(f);\n}\n\nint64_t f80_to_int(float80 f) {\n    if (!f80_is_supported(f))\n        return INT64_MIN; // indefinite\n    // if you need an exponent greater than 2^63 to represent this number, it\n    // can't be represented as a 64-bit integer\n    if (f.exp > bias(63))\n        return INT64_MIN; // also indefinite\n    // shift right (reduce precision) until the exponent is 2^63\n    f = f80_shift_right(f, bias(63) - f.exp);\n    // and the answer should be the significand!\n    return !f.sign ? f.signif : -f.signif;\n}\n\nstruct double_bits {\n    unsigned long signif:52;\n    unsigned exp:11;\n    unsigned sign:1;\n};\n#define EXP64_MAX 0x7fe\n#define EXP64_MIN 0x001\n#define EXP64_SPECIAL 0x7ff\n#define EXP64_DENORMAL 0x000\n\n// unsupported?\nfloat80 f80_from_double(double d) {\n    struct double_bits db;\n    memcpy(&db, &d, sizeof(db));\n    float80 f;\n\n    if (db.exp == EXP64_SPECIAL)\n        f.exp = EXP_SPECIAL;\n    else if (db.exp == EXP64_DENORMAL)\n        // denormals actually have an exponent of EXP_MIN, the special exponent\n        // is needed to indicate the integer bit is 0\n        // zeroes have the same exponent as denormals but need to be handled\n        // differently\n        f.exp = db.signif == 0 ? 0 : bias(1 - 0x3ff);\n    else\n        f.exp = bias((int) db.exp - 0x3ff);\n\n    f.signif = (uint64_t) db.signif << 11;\n    if (db.exp != EXP64_DENORMAL)\n        f.signif |= CURSED_BIT;\n    f.sign = db.sign;\n    return f80_normalize(f);\n}\n\ndouble f80_to_double(float80 f) {\n    if (!f80_is_supported(f))\n        return NAN;\n    struct double_bits db;\n    db.sign = f.sign;\n    int new_exp = unbias(f.exp) + 0x3ff;\n    if (f.exp == EXP_SPECIAL)\n        new_exp = EXP64_SPECIAL;\n    else if (new_exp > EXP64_MAX)\n        // out of range\n        return !f.sign ? INFINITY : -INFINITY;\n    if (new_exp <= 0) {\n        // number can only be represented in double precision as a denormal\n        // shift it enough to make the exponent into EXP64_MIN\n        // does it work on numbers that are not denormal but are too small to represent as double?\n        f.signif >>= 1;\n        f = f80_shift_right(f, -new_exp);\n        new_exp = unbias(f.exp) + 0x3ff;\n    }\n    db.exp = new_exp;\n    uint64_t db_signif = u128_shift_right_round(f.signif, 11, f.sign);\n    // handle the case when f.signif becomes 0x1fffffffffffff after shifting\n    // and then is rounded up\n    if (db_signif & (1ul << 53)) {\n        db_signif >>= 1;\n        db.exp++;\n    }\n    db.signif = db_signif;\n    double d;\n    memcpy(&d, &db, sizeof(db));\n    return d;\n}\n\nfloat80 f80_round(float80 f) {\n    if (!f80_is_supported(f))\n        return F80_NAN;\n    // Shift out all the bits to the right of the point (early exit if there are none)\n    int bits_to_clear = 63 - unbias(f.exp);\n    if (bits_to_clear <= 0)\n        return f;\n\n    f = f80_shift_right(f, bits_to_clear);\n    if (f.signif == 0) {\n        // If that just totally eradicated the significand, guess the answer is 0\n        f.exp = EXP_DENORMAL;\n    } else {\n        f = f80_normalize(f);\n    }\n    return f;\n}\n\nfloat80 f80_neg(float80 f) {\n    f.sign = ~f.sign;\n    return f;\n}\nfloat80 f80_abs(float80 f) {\n    f.sign = 0;\n    return f;\n}\n\n#define handle_nans(a, b) do { \\\n    if (!f80_is_supported(a) || !f80_is_supported(b)) \\\n        return F80_NAN; \\\n    /* this case is bizarre but hey I don't make the chips. though the amd spec\n     * says it's undefined which nan is returned if both have the same\n     * significant and different sign, so why am I doing this */\\\n    if (f80_isnan(a) && f80_isnan(b) && a.sign && !b.sign) \\\n        return b; \\\n    if (f80_isnan(a)) \\\n        return a; \\\n    if (f80_isnan(b)) \\\n        return b; \\\n} while(0)\n\nfloat80 f80_add(float80 a, float80 b) {\n    handle_nans(a, b);\n\n    // a has larger exponent, b has smaller exponent\n    if (a.exp < b.exp) {\n        float80 tmp = a;\n        a = b;\n        b = tmp;\n    }\n\n    // reduce the number of cases to deal with\n    bool flipped = false;\n    if (a.sign) {\n        a.sign = ~a.sign;\n        b.sign = ~b.sign;\n        flipped = true;\n    }\n    // now either both are positive (addition) or a is positive and b is\n    // negative (subtraction)\n\n    // do the addition in insane precision to fix that bug with adding 2^64 and 1.5\n    uint128_t a_signif = (uint128_t) a.signif << 64;\n    uint128_t b_signif = (uint128_t) b.signif << 64;\n    // shift b (smaller exponent) right until the exponents are equal\n    b_signif = u128_shift_right_round(b_signif, a.exp - b.exp, b.sign ^ flipped);\n\n    int sign = a.sign;\n    int exp = unbias_denormal(a.exp);\n    uint128_t signif = a_signif;\n    if (!b.sign) {\n        // b is positive, so add\n        if (!f80_isinf(a)) {\n            if (__builtin_add_overflow(a_signif, b_signif, &signif)) {\n                // in case of overflow, lose 1 bit of precision\n                signif = u128_shift_right_round(signif, 1, sign);\n                signif |= (uint128_t) 1 << 127; // recover the bit lost by the overflow\n                exp++;\n            }\n        }\n    } else {\n        // b is negative, so subtract\n        // but first, special case time!\n\n        // infinity - infinity is indefinite, not zero\n        if (f80_isinf(a) && f80_isinf(b))\n            return F80_NAN;\n\n        // When subtracting a (relatively) very small number in chop mode, all\n        // the bits will get shifted out and nothing will happen, but this\n        // should give a smaller result.\n        if (f80_rounding_mode == round_chop && b_signif == 0 && b.signif != 0)\n            b_signif = 1;\n\n        // Depending on the rounding mode, it's possible that shifting out all\n        // the bits produced 1 instead of 0. Subtracting 1 from infinity would\n        // give 2^16384-1 which is wrong.\n        if (f80_isinf(a))\n            b_signif = 0;\n\n        if (a_signif >= b_signif) {\n            // we can subtract without underflow\n            signif = a_signif - b_signif;\n        } else {\n            // the answer will be negative\n            signif = b_signif - a_signif;\n            sign = 1;\n        }\n\n        // a bizarre special case. from https://twitter.com/tblodt/status/1262145524620234752:\n        // > why does 1 - 1 = -0 when the x86 rounding mode is set to \"round down\"? it's just 0 for any other rounding mode\n        if (signif == 0 && a_signif != 0 && f80_rounding_mode == round_down)\n            return (float80) {.sign = 1};\n\n        // a - a = 0\n        if (signif == 0)\n            return (float80) {0};\n    }\n\n    if (flipped)\n        sign = !sign;\n    float80 f = u128_normalize_round(signif, exp, sign);\n    assert(f80_is_supported(f));\n    return f;\n}\nfloat80 f80_sub(float80 a, float80 b) {\n    return f80_add(a, f80_neg(b));\n}\n\nfloat80 f80_mul(float80 a, float80 b) {\n    handle_nans(a, b);\n\n    if (f80_isinf(a) || f80_isinf(b)) {\n        // infinity times zero is undefined\n        if (f80_iszero(a) || f80_iszero(b))\n            return F80_NAN;\n        // infinity times anything else is infinity\n        float80 f = F80_INF;\n        f.sign = a.sign ^ b.sign;\n        return f;\n    }\n\n    // add exponents (the +1 is necessary to be correct in 128-bit precision)\n    int f_exp = unbias_denormal(a.exp) + unbias_denormal(b.exp) + 1;\n    // multiply significands\n    uint128_t f_signif = (uint128_t) a.signif * b.signif;\n    // normalize and round the 128-bit result\n    float80 f = u128_normalize_round(f_signif, f_exp, a.sign ^ b.sign);\n    // xor signs\n    f.sign = a.sign ^ b.sign;\n    return f;\n}\n\nfloat80 f80_div(float80 a, float80 b) {\n    handle_nans(a, b);\n\n    float80 f;\n    if (f80_isinf(a)) {\n        // dividing into infinity gives infinity\n        f = F80_INF;\n        // except infinity / infinity is nan\n        if (f80_isinf(b))\n            return F80_NAN;\n    } else if (f80_isinf(b)) {\n        // dividing by infinity gives zero\n        f = (float80) {0};\n    } else if (f80_iszero(b)) {\n        // division by zero gives infinity\n        f = F80_INF;\n        // except 0 / 0 is nan\n        if (f80_iszero(a))\n            f = F80_NAN;\n    } else {\n        int b_trailing = __builtin_ctzl(b.signif);\n        b.signif >>= b_trailing;\n        uint128_t signif = ((uint128_t) a.signif << 64) / b.signif;\n        uint128_t remainder = ((uint128_t) a.signif << 64) % b.signif;\n        // extend this to 128 bit precision because hell yeah\n        int extra_bits = 0;\n        if (signif != 0) {\n            extra_bits = u128_clz(signif);\n            signif <<= extra_bits;\n            signif |= (remainder << extra_bits) / b.signif;\n        }\n        int exp = unbias_denormal(a.exp) - unbias_denormal(b.exp) + 63 - b_trailing - extra_bits;\n        f = u128_normalize_round(signif, exp, a.sign ^ b.sign);\n    }\n\n    f.sign = a.sign ^ b.sign;\n    return f;\n}\n\nfloat80 f80_mod(float80 x, float80 y) {\n    float80 quotient = f80_div(x, y);\n    enum f80_rounding_mode old_mode = f80_rounding_mode;\n    f80_rounding_mode = round_chop;\n    quotient = f80_round(quotient);\n    f80_rounding_mode = old_mode;\n    return f80_sub(x, f80_mul(quotient, y));\n}\n\nbool f80_uncomparable(float80 a, float80 b) {\n    if (!f80_is_supported(a) || !f80_is_supported(b))\n        return true;\n    if (f80_isnan(a) || f80_isnan(b))\n        return true;\n    return false;\n}\n\nbool f80_lt(float80 a, float80 b) {\n    if (f80_uncomparable(a, b))\n        return false;\n    // same signed infinities are equal, not less (though subtraction would produce nan)\n    if (f80_isinf(a) && f80_isinf(b) && a.sign == b.sign)\n        return false;\n    // zeroes are always equal\n    if (f80_iszero(a) && f80_iszero(b))\n        return false;\n    // if a < b then a - b < 0\n    float80 diff = f80_sub(a, b);\n    return diff.sign == 1 && !f80_iszero(diff);\n}\nbool f80_eq(float80 a, float80 b) {\n    if (f80_uncomparable(a, b))\n        return false;\n    if (f80_iszero(a)) a.sign = 0;\n    if (f80_iszero(a)) b.sign = 0;\n    return a.sign == b.sign && a.exp == b.exp && a.signif == b.signif;\n}\n\nbool f80_lte(float80 a, float80 b) {\n    return f80_lt(a, b) || f80_eq(a, b);\n}\nbool f80_gt(float80 a, float80 b) {\n    return !f80_lte(a, b);\n}\n\nfloat80 f80_log2(float80 x) {\n    float80 zero = f80_from_int(0);\n    float80 one = f80_from_int(1);\n    float80 two = f80_from_int(2);\n    if (f80_isnan(x) || f80_lte(x, zero))\n        return F80_NAN;\n\n    int ipart = 0;\n    while (f80_lt(x, one)) {\n        ipart--;\n        x = f80_mul(x, two);\n    }\n    while (f80_gt(x, two)) {\n        ipart++;\n        x = f80_div(x, two);\n    }\n    float80 res = f80_from_int(ipart);\n\n    float80 bit = one;\n    while (f80_gt(bit, zero)) {\n        while (f80_lte(x, two) && f80_gt(bit, zero)) {\n            x = f80_mul(x, x);\n            bit = f80_div(bit, two);\n        }\n        float80 oldres = res;\n        res = f80_add(res, bit);\n        if (oldres.signif == res.signif && oldres.exp == res.exp && oldres.sign == res.sign)\n            break;\n        x = f80_div(x, two);\n    }\n    return res;\n}\n\nfloat80 f80_sqrt(float80 x) {\n    if (f80_iszero(x))\n        return x;\n    if (f80_isnan(x) || x.sign)\n        return F80_NAN;\n    // for a rough guess, just cut the exponent by 2\n    float80 guess = x;\n    guess.exp = bias(unbias(guess.exp) / 2);\n    // now converge on the answer, using newton's method\n    float80 old_guess;\n    float80 two = f80_from_int(2);\n    int i = 0;\n    do {\n        old_guess = guess;\n        guess = f80_div(f80_add(guess, f80_div(x, guess)), two);\n    } while (!f80_eq(guess, old_guess) && i++ < 100);\n    return guess;\n}\n\nfloat80 f80_scale(float80 x, int scale) {\n    if (!f80_is_supported(x) || f80_isnan(x))\n        return F80_NAN;\n    return u128_normalize_round((uint128_t) x.signif << 64, unbias(x.exp) + scale, x.sign);\n}\n\nvoid f80_xtract(float80 f, int *exp, float80 *signif) {\n    *exp = unbias(f.exp);\n    *signif = f;\n    signif->exp = bias(0);\n}\n"
  },
  {
    "path": "emu/float80.h",
    "content": "#ifndef FLOAT80_H\n#define FLOAT80_H\n\n#include \"misc.h\"\n\ntypedef struct {\n    uint64_t signif;\n    union {\n        uint16_t signExp;\n        struct {\n            unsigned exp:15;\n            unsigned sign:1;\n        };\n    };\n} float80;\n\nfloat80 f80_from_int(int64_t i);\nint64_t f80_to_int(float80 f);\nfloat80 f80_from_double(double d);\ndouble f80_to_double(float80 f);\nfloat80 f80_round(float80 f);\n\nbool f80_isnan(float80 f);\nbool f80_isinf(float80 f);\nbool f80_iszero(float80 f);\nbool f80_isdenormal(float80 f);\nbool f80_is_supported(float80 f);\n\nfloat80 f80_add(float80 a, float80 b);\nfloat80 f80_sub(float80 a, float80 b);\nfloat80 f80_mul(float80 a, float80 b);\nfloat80 f80_div(float80 a, float80 b);\nfloat80 f80_mod(float80 a, float80 b);\nfloat80 f80_rem(float80 a, float80 b);\n\nbool f80_lt(float80 a, float80 b);\nbool f80_eq(float80 a, float80 b);\nbool f80_uncomparable(float80 a, float80 b);\n\nfloat80 f80_neg(float80 f);\nfloat80 f80_abs(float80 f);\n\nfloat80 f80_log2(float80 x);\nfloat80 f80_sqrt(float80 x);\n\nfloat80 f80_scale(float80 x, int scale);\n\n// Used to implement fxtract\nvoid f80_xtract(float80 f, int *exp, float80 *signif);\n\nenum f80_rounding_mode {\n    round_to_nearest = 0,\n    round_down = 1,\n    round_up = 2,\n    round_chop = 3,\n};\nextern __thread enum f80_rounding_mode f80_rounding_mode;\n\n#define F80_NAN ((float80) {.signif = 0xc000000000000000, .exp = 0x7fff, .sign = 0})\n#define F80_INF ((float80) {.signif = 0x8000000000000000, .exp = 0x7fff, .sign = 0})\n\n#endif\n"
  },
  {
    "path": "emu/fpu.c",
    "content": "// I don't remember if the interpreter was supposed to use this in addition to asbestos\n#include <math.h>\n#include <string.h>\n#include \"emu/cpu.h\"\n#include \"emu/float80.h\"\n#include \"emu/fpu.h\"\n\n#define ST(i) cpu->fp[(cpu->top + i) % 8]\n\nstatic void fpu_push(struct cpu_state *cpu, float80 f) {\n    cpu->top--;\n    ST(0) = f;\n}\n#define fpush(f) fpu_push(cpu, f)\nvoid fpu_pop(struct cpu_state *cpu) {\n    cpu->top++;\n}\n\nvoid fpu_xch(struct cpu_state *cpu, int i) {\n    float80 tmp = ST(0);\n    ST(0) = ST(i);\n    ST(i) = tmp;\n}\n\nvoid fpu_incstp(struct cpu_state *cpu) {\n    // This is different from just popping the stack, it doesn't tag the stack\n    // element as free. We don't have stack tagging yet so in practice there's\n    // no difference.\n    cpu->top++;\n}\n\n// loads\n\nvoid fpu_ld(struct cpu_state *cpu, int i) {\n    fpush(ST(i));\n}\n\nvoid fpu_ldc(struct cpu_state *cpu, enum fpu_const c) {\n    fpush(fpu_consts[c]);\n}\n\nvoid fpu_ild16(struct cpu_state *cpu, int16_t *i) {\n    fpush(f80_from_int(*i));\n}\nvoid fpu_ild32(struct cpu_state *cpu, int32_t *i) {\n    fpush(f80_from_int(*i));\n}\nvoid fpu_ild64(struct cpu_state *cpu, int64_t *i) {\n    fpush(f80_from_int(*i));\n}\n\nvoid fpu_ldm32(struct cpu_state *cpu, float32 *f) {\n    fpush(f80_from_double(*f));\n}\nvoid fpu_ldm64(struct cpu_state *cpu, float64 *f) {\n    fpush(f80_from_double(*f));\n}\nvoid fpu_ldm80(struct cpu_state *cpu, float80 *f) {\n    fpush(*f);\n}\n\n// stores\n\nvoid fpu_st(struct cpu_state *cpu, int i) {\n    ST(i) = ST(0);\n}\nvoid fpu_ist16(struct cpu_state *cpu, int16_t *i) {\n    int64_t res = f80_to_int(ST(0));\n    if (res < INT16_MIN || res > INT16_MAX)\n        res = INT16_MIN;\n    *i = (int16_t) res;\n}\nvoid fpu_ist32(struct cpu_state *cpu, int32_t *i) {\n    int64_t res = f80_to_int(ST(0));\n    if (res < INT32_MIN || res > INT32_MAX)\n        res = INT32_MIN;\n    *i = (int32_t) res;\n}\nvoid fpu_ist64(struct cpu_state *cpu, int64_t *i) {\n    *i = f80_to_int(ST(0));\n}\n\nvoid fpu_stm32(struct cpu_state *cpu, float32 *f) {\n    *f = f80_to_double(ST(0));\n}\nvoid fpu_stm64(struct cpu_state *cpu, float64 *f) {\n    *f = f80_to_double(ST(0));\n}\nvoid fpu_stm80(struct cpu_state *cpu, float80 *f) {\n    // intel guarantees this will only write 10 bytes, not 12 or anything weird like that\n    memcpy(f, &ST(0), 10);\n}\n\n// moves\n\n#define FCMOVcc(instr, cond) \\\n    void fpu_cmov##instr(struct cpu_state *cpu, int i) { \\\n        if (cond) \\\n            ST(0) = ST(i); \\\n    }\nFCMOVcc(b, cpu->cf)\nFCMOVcc(e, cpu->zf)\nFCMOVcc(be, cpu->cf | cpu->zf)\nFCMOVcc(u, cpu->pf)\nFCMOVcc(nb, !cpu->cf)\nFCMOVcc(ne, !cpu->zf)\nFCMOVcc(nbe, !(cpu->cf | cpu->zf))\nFCMOVcc(nu, !cpu->pf)\n\n// math\n\nvoid fpu_prem(struct cpu_state *cpu) {\n    ST(0) = f80_mod(ST(0), ST(1));\n    cpu->c2 = 0; // say we finished the entire remainder\n}\n\nvoid fpu_scale(struct cpu_state *cpu) {\n    enum f80_rounding_mode old_mode = f80_rounding_mode;\n    f80_rounding_mode = round_chop;\n    int scale = f80_to_int(ST(1));\n    f80_rounding_mode = old_mode;\n    ST(0) = f80_scale(ST(0), scale);\n}\n\nvoid fpu_rndint(struct cpu_state *cpu) {\n    if (f80_isinf(ST(0)) || f80_isnan(ST(0)))\n        return;\n    ST(0) = f80_round(ST(0));\n}\n\nvoid fpu_sqrt(struct cpu_state *cpu) {\n    ST(0) = f80_sqrt(ST(0));\n}\n\nvoid fpu_yl2x(struct cpu_state *cpu) {\n    ST(1) = f80_mul(ST(1), f80_log2(ST(0)));\n    fpu_pop(cpu);\n}\n\nvoid fpu_2xm1(struct cpu_state *cpu) {\n    // an example of the ancient chinese art of chi ting\n    ST(0) = f80_from_double(pow(2, f80_to_double(ST(0))) - 1);\n}\n\nstatic void fpu_comparei(struct cpu_state *cpu, float80 x) {\n    cpu->zf_res = cpu->pf_res = 0;\n    cpu->zf = cpu->pf = cpu->cf = 0;\n    cpu->cf = f80_lt(ST(0), x);\n    cpu->zf = f80_eq(ST(0), x);\n    if (f80_uncomparable(ST(0), x))\n        cpu->zf = cpu->pf = cpu->cf = 1;\n}\nstatic void fpu_compare(struct cpu_state *cpu, float80 x) {\n    cpu->c2 = cpu->c1 = 0;\n    cpu->c0 = f80_lt(ST(0), x);\n    cpu->c3 = f80_eq(ST(0), x);\n    if (f80_uncomparable(ST(0), x))\n        cpu->c0 = cpu->c2 = cpu->c3 = 1;\n}\nvoid fpu_com(struct cpu_state *cpu, int i) {\n    fpu_compare(cpu, ST(i));\n}\nvoid fpu_comi(struct cpu_state *cpu, int i) {\n    fpu_comparei(cpu, ST(i));\n}\nvoid fpu_comm32(struct cpu_state *cpu, float *f) {\n    fpu_compare(cpu, f80_from_double(*f));\n}\nvoid fpu_comm64(struct cpu_state *cpu, double *f) {\n    fpu_compare(cpu, f80_from_double(*f));\n}\nvoid fpu_icom16(struct cpu_state *cpu, int16_t *i) {\n    fpu_compare(cpu, f80_from_int(*i));\n}\nvoid fpu_icom32(struct cpu_state *cpu, int32_t *i) {\n    fpu_compare(cpu, f80_from_int(*i));\n}\nvoid fpu_tst(struct cpu_state *cpu) {\n    fpu_compare(cpu, fpu_consts[fconst_zero]);\n}\n\nvoid fpu_abs(struct cpu_state *cpu) {\n    ST(0) = f80_abs(ST(0));\n}\n\nvoid fpu_chs(struct cpu_state *cpu) {\n    ST(0) = f80_neg(ST(0));\n}\n\nvoid fpu_add(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_add(ST(dsti), ST(srci));\n}\nvoid fpu_sub(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_sub(ST(dsti), ST(srci));\n}\nvoid fpu_subr(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_sub(ST(srci), ST(dsti));\n}\nvoid fpu_mul(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_mul(ST(dsti), ST(srci));\n}\nvoid fpu_div(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_div(ST(dsti), ST(srci));\n}\nvoid fpu_divr(struct cpu_state *cpu, int srci, int dsti) {\n    ST(dsti) = f80_div(ST(srci), ST(dsti));\n}\n\nvoid fpu_iadd16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_add(ST(0), f80_from_int(*i));\n}\nvoid fpu_isub16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_sub(ST(0), f80_from_int(*i));\n}\nvoid fpu_isubr16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_sub(f80_from_int(*i), ST(0));\n}\nvoid fpu_imul16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_mul(ST(0), f80_from_int(*i));\n}\nvoid fpu_idiv16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_div(ST(0), f80_from_int(*i));\n}\nvoid fpu_idivr16(struct cpu_state *cpu, int16_t *i) {\n    ST(0) = f80_div(f80_from_int(*i), ST(0));\n}\n\nvoid fpu_iadd32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_add(ST(0), f80_from_int(*i));\n}\nvoid fpu_isub32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_sub(ST(0), f80_from_int(*i));\n}\nvoid fpu_isubr32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_sub(f80_from_int(*i), ST(0));\n}\nvoid fpu_imul32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_mul(ST(0), f80_from_int(*i));\n}\nvoid fpu_idiv32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_div(ST(0), f80_from_int(*i));\n}\nvoid fpu_idivr32(struct cpu_state *cpu, int32_t *i) {\n    ST(0) = f80_div(f80_from_int(*i), ST(0));\n}\n\nvoid fpu_addm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_add(ST(0), f80_from_double(*f));\n}\nvoid fpu_subm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_sub(ST(0), f80_from_double(*f));\n}\nvoid fpu_subrm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_sub(f80_from_double(*f), ST(0));\n}\nvoid fpu_mulm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_mul(ST(0), f80_from_double(*f));\n}\nvoid fpu_divm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_div(ST(0), f80_from_double(*f));\n}\nvoid fpu_divrm32(struct cpu_state *cpu, float32 *f) {\n    ST(0) = f80_div(f80_from_double(*f), ST(0));\n}\n\nvoid fpu_addm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_add(ST(0), f80_from_double(*f));\n}\nvoid fpu_subm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_sub(ST(0), f80_from_double(*f));\n}\nvoid fpu_subrm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_sub(f80_from_double(*f), ST(0));\n}\nvoid fpu_mulm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_mul(ST(0), f80_from_double(*f));\n}\nvoid fpu_divm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_div(ST(0), f80_from_double(*f));\n}\nvoid fpu_divrm64(struct cpu_state *cpu, float64 *f) {\n    ST(0) = f80_div(f80_from_double(*f), ST(0));\n}\n\nvoid fpu_patan(struct cpu_state *cpu) {\n    // there's no native atan2 for 80-bit float yet.\n    ST(1) = f80_from_double(atan2(f80_to_double(ST(1)), f80_to_double(ST(0))));\n    fpu_pop(cpu);\n}\n\nvoid fpu_sin(struct cpu_state *cpu) {\n    ST(0) = f80_from_double(sin(f80_to_double(ST(0))));\n}\nvoid fpu_cos(struct cpu_state *cpu) {\n    ST(0) = f80_from_double(cos(f80_to_double(ST(0))));\n}\n\nvoid fpu_xtract(struct cpu_state *cpu) {\n    int exp;\n    float80 signif;\n    f80_xtract(ST(0), &exp, &signif);\n    ST(0) = f80_from_int(exp);\n    fpush(signif);\n}\n\nvoid fpu_xam(struct cpu_state *cpu) {\n    float80 f = ST(0);\n    int outflags = 0;\n    if (!f80_is_supported(f)) {\n        outflags = 0b000;\n    } else if (f80_isnan(f)) {\n        outflags = 0b001;\n    } else if (f80_isinf(f)) {\n        outflags = 0b011;\n    } else if (f80_iszero(f)) {\n        outflags = 0b100;\n    } else if (f80_isdenormal(f)) {\n        outflags = 0b110;\n    } else {\n        // normal.\n        // todo: empty\n        outflags = 0b010;\n    }\n    cpu->c1 = f.sign;\n    cpu->c0 = outflags & 1;\n    cpu->c2 = (outflags >> 1) & 1;\n    cpu->c3 = (outflags >> 2) & 1;\n}\n\n// meta\n\nvoid fpu_stcw16(struct cpu_state *cpu, uint16_t *i) {\n    *i = cpu->fcw;\n}\nvoid fpu_ldcw16(struct cpu_state *cpu, uint16_t *i) {\n    cpu->fcw = *i;\n    f80_rounding_mode = cpu->rc;\n}\n\nstruct fpu_env32 {\n    uint32_t control;\n    uint32_t status;\n    uint32_t tag;\n    uint32_t ip;\n    uint32_t ip_selector;\n    uint32_t operand;\n    uint32_t operand_selector;\n};\n\nvoid fpu_stenv32(struct cpu_state *cpu, struct fpu_env32 *env) {\n    env->control = cpu->fcw;\n    env->status = cpu->fsw;\n    // hope nobody looks at these\n    env->tag = 0;\n    env->ip = env->ip_selector = 0;\n    env->operand = env->operand_selector = 0;\n}\nvoid fpu_ldenv32(struct cpu_state *cpu, struct fpu_env32 *env) {\n    cpu->fcw = env->control;\n    cpu->fsw = env->status;\n}\n\nstruct fpu_state32 {\n    struct fpu_env32 env;\n    uint8_t regs[8][10];\n};\n\nvoid fpu_save32(struct cpu_state *cpu, struct fpu_state32 *state) {\n    fpu_stenv32(cpu, &state->env);\n    for (int i = 0; i < 8; i++)\n        memcpy(state->regs[i], &ST(i), 10);\n}\n\nvoid fpu_restore32(struct cpu_state *cpu, struct fpu_state32 *state) {\n    fpu_ldenv32(cpu, &state->env);\n    for (int i = 0; i < 8; i++)\n        memcpy(&ST(i), state->regs[i], 10);\n}\n\nvoid fpu_clex(struct cpu_state *cpu) {\n    cpu->pe = cpu->ue = cpu->oe = cpu->ze = cpu->de = cpu->ie = cpu->es = cpu->sf = cpu->b = 0;\n}\n"
  },
  {
    "path": "emu/fpu.h",
    "content": "#ifndef EMU_FPU_H\n#define EMU_FPU_H\n#include \"emu/float80.h\"\nstruct cpu_state;\nstruct fpu_env32;\nstruct fpu_state32;\n\ntypedef float float32;\ntypedef double float64;\n\nenum fpu_const {\n    fconst_one = 0,\n    fconst_log2t = 1,\n    fconst_log2e = 2,\n    fconst_pi = 3,\n    fconst_log2 = 4,\n    fconst_ln2 = 5,\n    fconst_zero = 6,\n};\nstatic const float80 fpu_consts[] = {\n    [fconst_one]   = (float80) {.signif = 0x8000000000000000, .signExp = 0x3fff},\n    [fconst_log2t] = (float80) {.signif = 0xd49a784bcd1b8afe, .signExp = 0x4000},\n    [fconst_log2e] = (float80) {.signif = 0xb8aa3b295c17f0bc, .signExp = 0x3fff},\n    [fconst_pi]    = (float80) {.signif = 0xc90fdaa22168c235, .signExp = 0x4000},\n    [fconst_log2]  = (float80) {.signif = 0x9a209a84fbcff799, .signExp = 0x3ffd},\n    [fconst_ln2]   = (float80) {.signif = 0xb17217f7d1cf79ac, .signExp = 0x3ffe},\n    [fconst_zero]  = (float80) {.signif = 0x0000000000000000, .signExp = 0x0000},\n};\n\nvoid fpu_pop(struct cpu_state *cpu);\nvoid fpu_xch(struct cpu_state *cpu, int i);\nvoid fpu_incstp(struct cpu_state *cpu);\n\nvoid fpu_st(struct cpu_state *cpu, int i);\nvoid fpu_ist16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_ist32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_ist64(struct cpu_state *cpu, int64_t *i);\nvoid fpu_stm32(struct cpu_state *cpu, float *f);\nvoid fpu_stm64(struct cpu_state *cpu, double *f);\nvoid fpu_stm80(struct cpu_state *cpu, float80 *f);\n\nvoid fpu_cmovb(struct cpu_state *cpu, int i);\nvoid fpu_cmove(struct cpu_state *cpu, int i);\nvoid fpu_cmovbe(struct cpu_state *cpu, int i);\nvoid fpu_cmovu(struct cpu_state *cpu, int i);\nvoid fpu_cmovnb(struct cpu_state *cpu, int i);\nvoid fpu_cmovne(struct cpu_state *cpu, int i);\nvoid fpu_cmovnbe(struct cpu_state *cpu, int i);\nvoid fpu_cmovnu(struct cpu_state *cpu, int i);\n\nvoid fpu_ld(struct cpu_state *cpu, int i);\nvoid fpu_ldc(struct cpu_state *cpu, enum fpu_const c);\nvoid fpu_ild16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_ild32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_ild64(struct cpu_state *cpu, int64_t *i);\nvoid fpu_ldm32(struct cpu_state *cpu, float *f);\nvoid fpu_ldm64(struct cpu_state *cpu, double *f);\nvoid fpu_ldm80(struct cpu_state *cpu, float80 *f);\n\nvoid fpu_prem(struct cpu_state *cpu);\nvoid fpu_rndint(struct cpu_state *cpu);\nvoid fpu_scale(struct cpu_state *cpu);\nvoid fpu_abs(struct cpu_state *cpu);\nvoid fpu_chs(struct cpu_state *cpu);\nvoid fpu_sqrt(struct cpu_state *cpu);\nvoid fpu_yl2x(struct cpu_state *cpu);\nvoid fpu_2xm1(struct cpu_state *cpu);\n\nvoid fpu_com(struct cpu_state *cpu, int i);\nvoid fpu_comm32(struct cpu_state *cpu, float *f);\nvoid fpu_comm64(struct cpu_state *cpu, double *f);\nvoid fpu_icom16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_icom32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_comi(struct cpu_state *cpu, int i);\nvoid fpu_tst(struct cpu_state *cpu);\n#define fpu_ucom fpu_com\n#define fpu_ucomi fpu_comi\n\nvoid fpu_add(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_sub(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_subr(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_mul(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_div(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_divr(struct cpu_state *cpu, int srci, int dsti);\nvoid fpu_iadd16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_isub16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_isubr16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_imul16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_idiv16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_idivr16(struct cpu_state *cpu, int16_t *i);\nvoid fpu_iadd32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_isub32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_isubr32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_imul32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_idiv32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_idivr32(struct cpu_state *cpu, int32_t *i);\nvoid fpu_addm32(struct cpu_state *cpu, float *f);\nvoid fpu_subm32(struct cpu_state *cpu, float *f);\nvoid fpu_subrm32(struct cpu_state *cpu, float *f);\nvoid fpu_mulm32(struct cpu_state *cpu, float *f);\nvoid fpu_divm32(struct cpu_state *cpu, float *f);\nvoid fpu_divrm32(struct cpu_state *cpu, float *f);\nvoid fpu_addm64(struct cpu_state *cpu, double *f);\nvoid fpu_subm64(struct cpu_state *cpu, double *f);\nvoid fpu_subrm64(struct cpu_state *cpu, double *f);\nvoid fpu_mulm64(struct cpu_state *cpu, double *f);\nvoid fpu_divm64(struct cpu_state *cpu, double *f);\nvoid fpu_divrm64(struct cpu_state *cpu, double *f);\n\nvoid fpu_patan(struct cpu_state *cpu);\nvoid fpu_sin(struct cpu_state *cpu);\nvoid fpu_cos(struct cpu_state *cpu);\nvoid fpu_xam(struct cpu_state *cpu);\nvoid fpu_xtract(struct cpu_state *cpu);\n\nvoid fpu_stcw16(struct cpu_state *cpu, uint16_t *i);\nvoid fpu_ldcw16(struct cpu_state *cpu, uint16_t *i);\nvoid fpu_stenv32(struct cpu_state *cpu, struct fpu_env32 *env);\nvoid fpu_ldenv32(struct cpu_state *cpu, struct fpu_env32 *env);\nvoid fpu_save32(struct cpu_state *cpu, struct fpu_state32 *state);\nvoid fpu_restore32(struct cpu_state *cpu, struct fpu_state32 *state);\nvoid fpu_clex(struct cpu_state *cpu);\n\n#endif\n"
  },
  {
    "path": "emu/interrupt.h",
    "content": "// Intel standard interrupts\n// Any interrupt not handled specially becomes a SIGSEGV\n#define INT_NONE -1\n#define INT_DIV 0\n#define INT_DEBUG 1\n#define INT_NMI 2\n#define INT_BREAKPOINT 3\n#define INT_OVERFLOW 4\n#define INT_BOUND 5\n#define INT_UNDEFINED 6\n#define INT_FPU 7 // do not try to use the fpu. instead, try to realize the truth: there is no fpu.\n#define INT_DOUBLE 8 // interrupt during interrupt, i.e. interruptception\n#define INT_GPF 13\n#define INT_TIMER 32\n#define INT_SYSCALL 0x80\n"
  },
  {
    "path": "emu/mmu.h",
    "content": "#ifndef EMU_CPU_MEM_H\n#define EMU_CPU_MEM_H\n\n#include \"misc.h\"\n\n// top 20 bits of an address, i.e. address >> 12\ntypedef dword_t page_t;\n#define BAD_PAGE 0x10000\n\n#ifndef __KERNEL__\n#define PAGE_BITS 12\n#undef PAGE_SIZE // defined in system headers somewhere\n#define PAGE_SIZE (1 << PAGE_BITS)\n#define PAGE(addr) ((addr) >> PAGE_BITS)\n#define PGOFFSET(addr) ((addr) & (PAGE_SIZE - 1))\ntypedef dword_t pages_t;\n// bytes MUST be unsigned if you would like this to overflow to zero\n#define PAGE_ROUND_UP(bytes) (PAGE((bytes) + PAGE_SIZE - 1))\n#define MEM_PAGES (1 << 20) // at least on 32-bit\n#endif\n\nstruct mmu {\n    struct mmu_ops *ops;\n    struct asbestos *asbestos;\n    uint64_t changes;\n};\n\n#define MEM_READ 0\n#define MEM_WRITE 1\n#define MEM_WRITE_PTRACE 2\n\nstruct mmu_ops {\n    // type is MEM_READ or MEM_WRITE\n    void *(*translate)(struct mmu *mmu, addr_t addr, int type);\n};\n\nstatic inline void *mmu_translate(struct mmu *mmu, addr_t addr, int type) {\n    return mmu->ops->translate(mmu, addr, type);\n}\n\n#endif"
  },
  {
    "path": "emu/mmx.c",
    "content": "//\n//  mmx.c\n//  libISH_emu\n//\n//  Created by Jason Conway on 02/02/23.\n//\n\n#include <math.h>\n#include <string.h>\n\n#include \"emu/vec.h\"\n#include \"emu/cpu.h\"\n\nunion vec {\n    uint64_t qw;\n    uint8_t u8[8];\n    uint16_t u16[4];\n    uint32_t u32[2];\n    uint64_t u64[1];\n};\n\n#define VEC_MMX_OP(name, suffix, op, size) \\\n    void vec_##name##_##suffix##64(NO_CPU, const union mm_reg *src, union mm_reg *dst) { \\\n        union vec s = { .qw = src->qw }, d = { .qw = dst->qw }; \\\n        for (unsigned i = 0; i < array_size(s.u##size); i++) \\\n            d.u##size[i] op##= s.u##size[i]; \\\n        dst->qw = d.qw; \\\n    }\n\n#define _VEC_MMX_CMP(sgn, usgn, suffix, relop, size) \\\n    void vec_compare##sgn##_##suffix##64(NO_CPU, const union mm_reg *src, union mm_reg *dst) { \\\n        union vec s = { .qw = src->qw }, d = { .qw = dst->qw }; \\\n        for (unsigned i = 0; i < array_size(s.u##size); i++) \\\n            d.u##size[i] = (usgn##int##size##_t)d.u##size[i] relop (usgn##int##size##_t)s.u##size[i] ? ~0 : 0;\\\n        dst->qw = d.qw; \\\n    }\n\n#define _SHIFT(op, size) \\\n    do { \\\n        if (unlikely(amount > (size)-1)) { \\\n            dst->qw = 0; \\\n        } else { \\\n            union vec d = { .qw = dst->qw }; \\\n            for (unsigned i = 0; i < array_size(d.u##size); i++) \\\n                d.u##size[i] op##= amount; \\\n            dst->qw = d.qw; \\\n        } \\\n    } while (0)\n\n#define VEC_MMX_SHIFT(dir, suffix, op, size) \\\n    void vec_shift##dir##_##suffix##64(NO_CPU, const union mm_reg *src, union mm_reg *dst) { \\\n        const uint8_t amount = src->qw; \\\n        _SHIFT(op, size); \\\n    } \\\n    void vec_imm_shift##dir##_##suffix##64(NO_CPU, const uint8_t amount, union mm_reg *dst) { \\\n        _SHIFT(op, size); \\\n    }\n\n#define VEC_MMX_CMPD(suffix, relop, size) \\\n    _VEC_MMX_CMP(, u, suffix, relop, size)\n#define VEC_MMX_CMPS(suffix, relop, size) \\\n    _VEC_MMX_CMP(s,, suffix, relop, size)\n\nVEC_MMX_OP(add, b, +, 8)\nVEC_MMX_OP(add, w, +, 16)\nVEC_MMX_OP(add, d, +, 32)\nVEC_MMX_OP(add, q, +, 64)\n\nVEC_MMX_OP(sub, b, -, 8)\nVEC_MMX_OP(sub, w, -, 16)\nVEC_MMX_OP(sub, d, -, 32)\nVEC_MMX_OP(sub, q, -, 64)\n\nVEC_MMX_OP(and, q, &, 64)\nVEC_MMX_OP(or,  q, |, 64)\nVEC_MMX_OP(xor, q, ^, 64)\n\nVEC_MMX_CMPD(eqb, ==,  8)\nVEC_MMX_CMPD(eqw, ==, 16)\nVEC_MMX_CMPD(eqd, ==, 32)\n\nVEC_MMX_CMPS(gtb, >,  8)\nVEC_MMX_CMPS(gtw, >, 16)\nVEC_MMX_CMPS(gtd, >, 32)\n\nVEC_MMX_SHIFT(r, w, >>, 16)\nVEC_MMX_SHIFT(r, d, >>, 32)\nVEC_MMX_SHIFT(r, q, >>, 64)\n\nVEC_MMX_SHIFT(l, w, <<, 16)\nVEC_MMX_SHIFT(l, d, <<, 32)\nVEC_MMX_SHIFT(l, q, <<, 64)\n\nvoid vec_shiftrs_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst) {\n    union vec d = { .qw = dst->qw };\n    const uint8_t amount = src->qw;\n    for (unsigned i = 0; i < 4; i++) {\n        if (amount > 15)\n            d.u16[i] = ((d.u16[i] >> 15) & (uint16_t)1) ? 0xffff : 0;\n        else\n            d.u16[i] = ((int16_t)(d.u16[i])) >> amount;\n    }\n    dst->qw = d.qw;\n}\nvoid vec_shiftrs_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst) {\n    union vec d = { .qw = dst->qw };\n    const uint8_t amount = src->qw;\n    for (unsigned i = 0; i < 2; i++) {\n        if (amount > 31)\n            d.u32[i] = ((d.u32[i] >> 31) & (uint32_t)1) ? 0xffffffff : 0;\n        else\n            d.u32[i] = ((int32_t)(d.u32[i])) >> amount;\n    }\n    dst->qw = d.qw;\n}\nvoid vec_imm_shiftrs_w64(NO_CPU, const uint8_t amount, union mm_reg *dst) {\n    union vec d = { .qw = dst->qw };\n    for (unsigned i = 0; i < 4; i++) {\n        if (amount > 15)\n            d.u16[i] = ((d.u16[i] >> 15) & (uint16_t)1) ? 0xffff : 0;\n        else\n            d.u16[i] = ((int16_t)(d.u16[i])) >> amount;\n    }\n    dst->qw = d.qw;\n}\nvoid vec_imm_shiftrs_d64(NO_CPU, const uint8_t amount, union mm_reg *dst) {\n    union vec d = { .qw = dst->qw };\n    for (unsigned i = 0; i < 2; i++) {\n        if (amount > 31)\n            d.u32[i] = ((d.u32[i] >> 31) & (uint32_t)1) ? 0xffffffff : 0;\n        else\n            d.u32[i] = ((int32_t)(d.u32[i])) >> amount;\n    }\n    dst->qw = d.qw;\n}\n\nvoid vec_mulu64(NO_CPU, const union mm_reg *src, union mm_reg *dst) {\n    union vec s = { .qw = src->qw }, d = { .qw = dst->qw };\n    for (unsigned i = 0; i < 4; i++) {\n        uint32_t res = ((int16_t)d.u16[i] * (int16_t)s.u16[i]);\n        d.u16[i] = ((res >> 16) & 0xffff);\n    }\n    dst->qw = d.qw;\n}\nvoid vec_mull64(NO_CPU, const union mm_reg *src, union mm_reg *dst) {\n    union vec s = { .qw = src->qw }, d = { .qw = dst->qw };\n    for (int i = 0; i < 4; i++) {\n        d.u16[i] = (uint16_t)(d.u16[i] * s.u16[i]);\n    }\n    dst->qw = d.qw;\n}\nvoid vec_mulu_dq64(NO_CPU, union mm_reg *src, union mm_reg *dst) {\n    dst->qw = (uint64_t) src->dw[0] * dst->dw[0];\n}\n\nvoid vec_unpackl_dq64(NO_CPU, const union mm_reg *src, union mm_reg *dst) {\n    dst->dw[1] = src->dw[0];\n}\n\nvoid vec_shuffle_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst, uint8_t encoding) {\n    union vec s = { .qw = src->qw }, d = { .qw = dst->qw };\n    for (unsigned i = 0; i < 4; i++)\n        d.u16[i] = s.u16[(encoding >> (2 * i)) % 4];\n    dst->qw = d.qw;\n}\n\nvoid vec_movmask_b64(NO_CPU, const union mm_reg *src, uint32_t *dst) {\n    union vec s = { .qw = src->qw };\n    *dst = 0;\n    for (unsigned i = 0; i < array_size(s.u8); i++) {\n        if (s.u8[i] & (1 << 7))\n            *dst |= 1 << i;\n    }\n}\n\nvoid vec_insert_w64(NO_CPU, const uint32_t *src, union mm_reg *dst, uint8_t index) {\n    union vec d = { .qw = dst->qw };\n    d.u16[index % 4] = (uint16_t)*src;\n    dst->qw = d.qw;\n}\n"
  },
  {
    "path": "emu/modrm.h",
    "content": "#ifndef MODRM_H\n#define MODRM_H\n\n#include \"debug.h\"\n#include \"misc.h\"\n#include \"emu/cpu.h\"\n#include \"emu/tlb.h\"\n\n#undef DEFAULT_CHANNEL\n#define DEFAULT_CHANNEL instr\n\nstruct modrm {\n    union {\n        enum reg32 reg;\n        unsigned opcode;\n    };\n    enum {\n        modrm_reg, modrm_mem, modrm_mem_si\n    } type;\n    union {\n        enum reg32 base;\n        unsigned rm_opcode;\n    };\n    int32_t offset;\n    enum reg32 index;\n    enum {\n        times_1 = 0,\n        times_2 = 1,\n        times_4 = 2,\n    } shift;\n};\n\nstatic const unsigned rm_sib = reg_esp;\nstatic const unsigned rm_none = reg_esp;\nstatic const unsigned rm_disp32 = reg_ebp;\n#define MOD(byte) ((byte & 0b11000000) >> 6)\n#define REG(byte) ((byte & 0b00111000) >> 3)\n#define RM(byte)  ((byte & 0b00000111) >> 0)\n\n// read modrm and maybe sib, output information into *modrm, return false for segfault\nstatic inline bool modrm_decode32(addr_t *ip, struct tlb *tlb, struct modrm *modrm) {\n#define READ(thing) \\\n    *ip += sizeof(thing); \\\n    if (!tlb_read(tlb, *ip - sizeof(thing), &(thing), sizeof(thing))) \\\n        return false\n\n    byte_t modrm_byte;\n    READ(modrm_byte);\n\n    enum {\n        mode_disp0,\n        mode_disp8,\n        mode_disp32,\n        mode_reg,\n    } mode = MOD(modrm_byte);\n    modrm->type = modrm_mem;\n    modrm->reg = REG(modrm_byte);\n    modrm->rm_opcode = RM(modrm_byte);\n    if (mode == mode_reg) {\n        modrm->type = modrm_reg;\n    } else if (modrm->rm_opcode == rm_disp32 && mode == mode_disp0) {\n        modrm->base = reg_none;\n        mode = mode_disp32;\n    } else if (modrm->rm_opcode == rm_sib && mode != mode_reg) {\n        byte_t sib_byte;\n        READ(sib_byte);\n        modrm->base = RM(sib_byte);\n        // wtf intel\n        if (modrm->rm_opcode == rm_disp32) {\n            if (mode == mode_disp0) {\n                modrm->base = reg_none;\n                mode = mode_disp32;\n            } else {\n                modrm->base = reg_ebp;\n            }\n        }\n        modrm->index = REG(sib_byte);\n        modrm->shift = MOD(sib_byte);\n        if (modrm->index != rm_none)\n            modrm->type = modrm_mem_si;\n    }\n\n    if (mode == mode_disp0) {\n        modrm->offset = 0;\n    } else if (mode == mode_disp8) {\n        int8_t offset;\n        READ(offset);\n        modrm->offset = offset;\n    } else if (mode == mode_disp32) {\n        int32_t offset;\n        READ(offset);\n        modrm->offset = offset;\n    }\n#undef READ\n\n    TRACE(\"reg=%s opcode=%d \", reg32_name(modrm->reg), modrm->opcode);\n    TRACE(\"base=%s \", reg32_name(modrm->base));\n    if (modrm->type != modrm_reg)\n        TRACE(\"offset=%s0x%x \", modrm->offset < 0 ? \"-\" : \"\", modrm->offset);\n    if (modrm->type == modrm_mem_si)\n        TRACE(\"index=%s<<%d \", reg32_name(modrm->index), modrm->shift);\n\n    return true;\n}\n\n#endif\n"
  },
  {
    "path": "emu/tlb.c",
    "content": "#include \"emu/cpu.h\"\n#include \"emu/tlb.h\"\n\nvoid tlb_refresh(struct tlb *tlb, struct mmu *mmu) {\n    if (tlb->mmu == mmu && tlb->mem_changes == mmu->changes)\n        return;\n    tlb->mmu = mmu;\n    tlb->dirty_page = TLB_PAGE_EMPTY;\n    tlb->mem_changes = mmu->changes;\n    tlb_flush(tlb);\n}\n\nvoid tlb_flush(struct tlb *tlb) {\n    tlb->mem_changes = tlb->mmu->changes;\n    for (unsigned i = 0; i < TLB_SIZE; i++)\n        tlb->entries[i] = (struct tlb_entry) {.page = 1, .page_if_writable = 1};\n}\n\nvoid tlb_free(struct tlb *tlb) {\n    free(tlb);\n}\n\nbool __tlb_read_cross_page(struct tlb *tlb, addr_t addr, char *value, unsigned size) {\n    char *ptr1 = __tlb_read_ptr(tlb, addr);\n    if (ptr1 == NULL)\n        return false;\n    char *ptr2 = __tlb_read_ptr(tlb, (PAGE(addr) + 1) << PAGE_BITS);\n    if (ptr2 == NULL)\n        return false;\n    size_t part1 = PAGE_SIZE - PGOFFSET(addr);\n    assert(part1 < size);\n    memcpy(value, ptr1, part1);\n    memcpy(value + part1, ptr2, size - part1);\n    return true;\n}\n\nbool __tlb_write_cross_page(struct tlb *tlb, addr_t addr, const char *value, unsigned size) {\n    char *ptr1 = __tlb_write_ptr(tlb, addr);\n    if (ptr1 == NULL)\n        return false;\n    char *ptr2 = __tlb_write_ptr(tlb, (PAGE(addr) + 1) << PAGE_BITS);\n    if (ptr2 == NULL)\n        return false;\n    size_t part1 = PAGE_SIZE - PGOFFSET(addr);\n    assert(part1 < size);\n    memcpy(ptr1, value, part1);\n    memcpy(ptr2, value + part1, size - part1);\n    return true;\n}\n\n__no_instrument void *tlb_handle_miss(struct tlb *tlb, addr_t addr, int type) {\n    char *ptr = mmu_translate(tlb->mmu, TLB_PAGE(addr), type);\n    if (tlb->mmu->changes != tlb->mem_changes)\n        tlb_flush(tlb);\n    if (ptr == NULL) {\n        tlb->segfault_addr = addr;\n        return NULL;\n    }\n    tlb->dirty_page = TLB_PAGE(addr);\n\n    struct tlb_entry *tlb_ent = &tlb->entries[TLB_INDEX(addr)];\n    tlb_ent->page = TLB_PAGE(addr);\n    if (type == MEM_WRITE)\n        tlb_ent->page_if_writable = tlb_ent->page;\n    else\n        // 1 is not a valid page so this won't look like a hit\n        tlb_ent->page_if_writable = TLB_PAGE_EMPTY;\n    tlb_ent->data_minus_addr = (uintptr_t) ptr - TLB_PAGE(addr);\n    return (void *) (tlb_ent->data_minus_addr + addr);\n}\n"
  },
  {
    "path": "emu/tlb.h",
    "content": "#ifndef TLB_H\n#define TLB_H\n\n#include <string.h>\n#include \"emu/mmu.h\"\n#include \"debug.h\"\n\nstruct tlb_entry {\n    page_t page;\n    page_t page_if_writable;\n    uintptr_t data_minus_addr;\n};\n#define TLB_BITS 10\n#define TLB_SIZE (1 << TLB_BITS)\nstruct tlb {\n    struct mmu *mmu;\n    page_t dirty_page;\n    unsigned mem_changes;\n    // this is basically one of the return values of tlb_handle_miss, tlb_{read,write}, and __tlb_{read,write}_cross_page\n    // yes, this sucks\n    addr_t segfault_addr;\n    struct tlb_entry entries[TLB_SIZE];\n};\n\n#define TLB_INDEX(addr) (((addr >> PAGE_BITS) & (TLB_SIZE - 1)) ^ (addr >> (PAGE_BITS + TLB_BITS)))\n#define TLB_PAGE(addr) (addr & 0xfffff000)\n#define TLB_PAGE_EMPTY 1\nvoid tlb_refresh(struct tlb *tlb, struct mmu *mmu);\nvoid tlb_free(struct tlb *tlb);\nvoid tlb_flush(struct tlb *tlb);\nvoid *tlb_handle_miss(struct tlb *tlb, addr_t addr, int type);\n\nforceinline __no_instrument void *__tlb_read_ptr(struct tlb *tlb, addr_t addr) {\n    struct tlb_entry entry = tlb->entries[TLB_INDEX(addr)];\n    if (entry.page == TLB_PAGE(addr)) {\n        void *address = (void *) (entry.data_minus_addr + addr);\n        posit(address != NULL);\n        return address;\n    }\n    return tlb_handle_miss(tlb, addr, MEM_READ);\n}\nbool __tlb_read_cross_page(struct tlb *tlb, addr_t addr, char *out, unsigned size);\nforceinline __no_instrument bool tlb_read(struct tlb *tlb, addr_t addr, void *out, unsigned size) {\n    if (PGOFFSET(addr) > PAGE_SIZE - size)\n        return __tlb_read_cross_page(tlb, addr, out, size);\n    void *ptr = __tlb_read_ptr(tlb, addr);\n    if (ptr == NULL)\n        return false;\n    memcpy(out, ptr, size);\n    return true;\n}\n\nforceinline __no_instrument void *__tlb_write_ptr(struct tlb *tlb, addr_t addr) {\n    struct tlb_entry entry = tlb->entries[TLB_INDEX(addr)];\n    if (entry.page_if_writable == TLB_PAGE(addr)) {\n        tlb->dirty_page = TLB_PAGE(addr);\n        void *address = (void *) (entry.data_minus_addr + addr);\n        posit(address != NULL);\n        return address;\n    }\n    return tlb_handle_miss(tlb, addr, MEM_WRITE);\n}\nbool __tlb_write_cross_page(struct tlb *tlb, addr_t addr, const char *value, unsigned size);\nforceinline __no_instrument bool tlb_write(struct tlb *tlb, addr_t addr, const void *value, unsigned size) {\n    if (PGOFFSET(addr) > PAGE_SIZE - size)\n        return __tlb_write_cross_page(tlb, addr, value, size);\n    void *ptr = __tlb_write_ptr(tlb, addr);\n    if (ptr == NULL)\n        return false;\n    memcpy(ptr, value, size);\n    return true;\n}\n\n#endif\n"
  },
  {
    "path": "emu/vec.c",
    "content": "#include <math.h>\n#include <string.h>\n\n#include \"emu/vec.h\"\n#include \"emu/cpu.h\"\n\nunion vec {\n    uint8_t u8[16];\n    uint16_t u16[8];\n    uint32_t u32[4];\n    uint64_t u64[2];\n    __uint128_t u128[1];\n    __uint128_t dqw;\n};\n\nstatic inline void zero_xmm(union xmm_reg *xmm) {\n    xmm->qw[0] = 0;\n    xmm->qw[1] = 0;\n}\n\nstatic inline int32_t satsw(int32_t dw) {\n    if (dw > 0xff80)\n        dw &= 0xff;\n    else if (dw > 0x7fff)\n        dw = 0x80;\n    else if (dw > 0x7f)\n        dw = 0x7f;\n    return dw;\n}\nstatic inline uint32_t satud(uint32_t dw) {\n    if (dw > 0xffff8000)\n        dw &= 0xffff;\n    else if (dw > 0x7fffffff)\n        dw = 0x8000;\n    else if (dw > 0x7fff)\n        dw = 0x7fff;\n    return dw;\n}\nstatic inline uint32_t satub(uint32_t dw) {\n    if (dw >= 0x8000)\n        dw = 0;\n    else if (dw > 0xff)\n        dw = 0xff;\n    return dw;\n}\nstatic inline uint32_t satsb(uint32_t dw) {\n    if (dw > 0xffffff80)\n        dw &= 0xff;\n    else if (dw > 0x7fffffff)\n        dw = 0x80;\n    else if (dw > 0x7f)\n        dw = 0x7f;\n    return dw;\n}\n\n#define VEC_ZERO_COPY(zero, copy) \\\n    void vec_zero##zero##_copy##copy(NO_CPU, const void *src, void *dst) { \\\n        memcpy(dst, src, copy/8); \\\n        memset((char *) dst + copy/8, 0, (zero-copy)/8); \\\n    }\nVEC_ZERO_COPY(128, 128)\nVEC_ZERO_COPY(128, 64)\nVEC_ZERO_COPY(128, 32)\nVEC_ZERO_COPY(64, 64)\nVEC_ZERO_COPY(64, 32)\nVEC_ZERO_COPY(32, 32)\n\nvoid vec_merge32(NO_CPU, const void *src, void *dst) {\n    memcpy(dst, src, 4);\n}\nvoid vec_merge64(NO_CPU, const void *src, void *dst) {\n    memcpy(dst, src, 8);\n}\nvoid vec_merge128(NO_CPU, const void *src, void *dst) {\n    memcpy(dst, src, 16);\n}\n\n#define _SHIFT(op, size) \\\n    do { \\\n        if (unlikely(amount > (size)-1)) { \\\n            zero_xmm(dst); \\\n        } else { \\\n            union vec d = { .dqw = dst->u128 }; \\\n            for (unsigned i = 0; i < array_size(d.u##size); i++) \\\n                d.u##size[i] op##= amount; \\\n            dst->u128 = d.dqw; \\\n        } \\\n    } while (0)\n\n#define VEC_SSE_SHIFT(dir, suffix, op, size) \\\n    void vec_shift##dir##_##suffix##128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) { \\\n        const uint8_t amount = src->u8[0]; \\\n        _SHIFT(op, size); \\\n    } \\\n    void vec_imm_shift##dir##_##suffix##128(NO_CPU, const uint8_t amount, union xmm_reg *dst) { \\\n        _SHIFT(op, size); \\\n    }\n\n#define _VEC_SSE_CMP(sgn, usgn, suffix, relop, size) \\\n    void vec_compare##sgn##_##suffix##128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) { \\\n        union vec s = { .dqw = src->u128 }, d = { .dqw = dst->u128 }; \\\n        for (unsigned i = 0; i < array_size(s.u##size); i++) \\\n            d.u##size[i] = (usgn##int##size##_t)d.u##size[i] relop (usgn##int##size##_t)s.u##size[i] ? ~0 : 0;\\\n        dst->u128 = d.dqw; \\\n    }\n\n#define VEC_SSE_CMPD(suffix, relop, size) \\\n    _VEC_SSE_CMP(, u, suffix, relop, size)\n#define VEC_SSE_CMPS(suffix, relop, size) \\\n    _VEC_SSE_CMP(s,, suffix, relop, size)\n\n#define VEC_SSE_OP(name, suffix, op, size) \\\n    void vec_##name##_##suffix##128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) { \\\n        union vec s = { .dqw = src->u128 }, d = { .dqw = dst->u128 }; \\\n        for (unsigned i = 0; i < array_size(s.u##size); i++) \\\n            d.u##size[i] op##= s.u##size[i]; \\\n        dst->u128 = d.dqw; \\\n    }\n\nVEC_SSE_SHIFT(r, w, >>, 16)\nVEC_SSE_SHIFT(r, d, >>, 32)\nVEC_SSE_SHIFT(r, q, >>, 64)\n\nVEC_SSE_SHIFT(l, w, <<, 16)\nVEC_SSE_SHIFT(l, d, <<, 32)\nVEC_SSE_SHIFT(l, q, <<, 64)\n\nVEC_SSE_CMPD(eqb, ==,  8)\nVEC_SSE_CMPD(eqw, ==, 16)\nVEC_SSE_CMPD(eqd, ==, 32)\n\nVEC_SSE_CMPS(gtb, >,  8)\nVEC_SSE_CMPS(gtw, >, 16)\nVEC_SSE_CMPS(gtd, >, 32)\n\nVEC_SSE_OP(add, b, +, 8)\nVEC_SSE_OP(add, w, +, 16)\nVEC_SSE_OP(add, d, +, 32)\nVEC_SSE_OP(add, q, +, 64)\n\nVEC_SSE_OP(sub, b, -, 8)\nVEC_SSE_OP(sub, w, -, 16)\nVEC_SSE_OP(sub, d, -, 32)\nVEC_SSE_OP(sub, q, -, 64)\n\nVEC_SSE_OP(and, dq, &, 128)\nVEC_SSE_OP(or,  dq, |, 128)\nVEC_SSE_OP(xor, dq, ^, 128)\n\nvoid vec_imm_shiftl_dq128(NO_CPU, uint8_t amount, union xmm_reg *dst) {\n    if (amount >= 16)\n        zero_xmm(dst);\n    else\n        dst->u128 <<= amount * 8;\n}\nvoid vec_imm_shiftr_dq128(NO_CPU, uint8_t amount, union xmm_reg *dst) {\n    if (amount >= 16)\n        zero_xmm(dst);\n    else\n        dst->u128 >>= amount * 8;\n}\nvoid vec_shiftrs_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    const uint8_t amount = src->u8[0];\n    for (unsigned i = 0; i < 8; i++) {\n        if (unlikely(amount > 15))\n            dst->u16[i] = ((dst->u16[i] >> 15) & (uint16_t)1) ? 0xffff : 0;\n        else\n            dst->u16[i] = ((int16_t)(dst->u16[i])) >> amount;\n    }\n}\nvoid vec_shiftrs_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    const uint8_t amount = src->u8[0];\n    for (unsigned i = 0; i < 4; i++) {\n        if (unlikely(amount > 31))\n            dst->u32[i] = ((dst->u32[i] >> 31) & (uint32_t)1) ? 0xffffffff : 0;\n        else\n            dst->u32[i] = ((int32_t)(dst->u32[i])) >> amount;\n    }\n}\nvoid vec_imm_shiftrs_w128(NO_CPU, const uint8_t amount, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++) {\n        if (unlikely(amount > 15))\n            dst->u16[i] = ((dst->u16[i] >> 15) & (uint16_t)1) ? 0xffff : 0;\n        else\n            dst->u16[i] = ((int16_t)(dst->u16[i])) >> amount;\n    }\n}\nvoid vec_imm_shiftrs_d128(NO_CPU, const uint8_t amount, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 4; i++) {\n        if (unlikely(amount > 31))\n            dst->u32[i] = ((dst->u32[i] >> 31) & (uint32_t)1) ? 0xffffffff : 0;\n        else\n            dst->u32[i] = ((int32_t)(dst->u32[i])) >> amount;\n    }\n}\n\nvoid vec_addus_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 16; i++) {\n        const int32_t sb = dst->u8[i] + src->u8[i];\n        dst->u8[i] = sb > 0xff ? 0xff : sb;\n    }\n}\nvoid vec_addus_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++) {\n        const int32_t sw = dst->u16[i] + src->u16[i];\n        dst->u16[i] = sw > 0xffff ? 0xffff : sw;\n    }\n}\nvoid vec_addss_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 16; i++)\n        dst->u8[i] = satsb((int8_t)dst->u8[i] + (int8_t)src->u8[i]);\n}\nvoid vec_addss_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++)\n        dst->u16[i] = satud((int16_t)dst->u16[i] + (int16_t)src->u16[i]);\n}\n\nvoid vec_subus_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 16; i++) {\n        const int32_t sb = dst->u8[i] - src->u8[i];\n        dst->u8[i] = sb < 0 ? 0 : sb;\n    }\n}\nvoid vec_subus_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++) {\n        const int32_t sw = dst->u16[i] - src->u16[i];\n        dst->u16[i] = sw < 0 ? 0 : sw;\n    }\n}\nvoid vec_subss_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 16; i++)\n        dst->u8[i] = satsb((int8_t)dst->u8[i] - (int8_t)src->u8[i]);\n}\nvoid vec_subss_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++)\n        dst->u16[i] = satud((int16_t)dst->u16[i] - (int16_t)src->u16[i]);\n}\n\nvoid vec_madd_d128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = (int32_t)((int16_t)dst->u16[0] * (int16_t)src->u16[0]) +\n                  (int32_t)((int16_t)dst->u16[1] * (int16_t)src->u16[1]);\n    dst->u32[1] = (int32_t)((int16_t)dst->u16[2] * (int16_t)src->u16[2]) +\n                  (int32_t)((int16_t)dst->u16[3] * (int16_t)src->u16[3]);\n    dst->u32[2] = (int32_t)((int16_t)dst->u16[4] * (int16_t)src->u16[4]) +\n                  (int32_t)((int16_t)dst->u16[5] * (int16_t)src->u16[5]);\n    dst->u32[3] = (int32_t)((int16_t)dst->u16[6] * (int16_t)src->u16[6]) +\n                  (int32_t)((int16_t)dst->u16[7] * (int16_t)src->u16[7]);\n}\n\nvoid vec_sumabs_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    uint32_t sum[2] = { 0, 0 };\n    for (unsigned i = 0; i < 8; i++) {\n        int32_t difflo = dst->u8[i + 0] - src->u8[i + 0];\n        int32_t diffhi = dst->u8[i + 8] - src->u8[i + 8];\n        sum[0] += (difflo < 0) ? -(uint32_t)difflo : difflo;\n        sum[1] += (diffhi < 0) ? -(uint32_t)diffhi : diffhi;\n    }\n    dst->u32[0] = sum[0];\n    dst->u32[2] = sum[1];\n    dst->u32[1] = dst->u32[3] = 0;\n}\n\nvoid vec_mulu_dq128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    dst->qw[0] = (uint64_t) src->u32[0] * dst->u32[0];\n    dst->qw[1] = (uint64_t) src->u32[2] * dst->u32[2];\n}\n\nvoid vec_andn128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    dst->qw[0] = ~dst->qw[0] & src->qw[0];\n    dst->qw[1] = ~dst->qw[1] & src->qw[1];\n}\n\nvoid vec_min_ub128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < array_size(src->u8); i++)\n        if (src->u8[i] < dst->u8[i])\n            dst->u8[i] = src->u8[i];\n}\nvoid vec_max_ub128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < array_size(src->u8); i++)\n        if (src->u8[i] > dst->u8[i])\n            dst->u8[i] = src->u8[i];\n}\nvoid vec_mins_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++)\n        dst->u16[i] = (int16_t)dst->u16[i] < (int16_t)src->u16[i] ? dst->u16[i] : src->u16[i];\n}\n\nvoid vec_maxs_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++)\n        dst->u16[i] = (int16_t)dst->u16[i] > (int16_t)src->u16[i] ? dst->u16[i] : src->u16[i];\n}\n\nstatic bool cmpd(double a, double b, int type) {\n    bool res;\n    switch (type % 4) {\n        case 0: res = a == b; break;\n        case 1: res = a < b; break;\n        case 2: res = a <= b; break;\n        case 3: res = isnan(a) || isnan(b); break;\n    }\n    if (type >= 4) res = !res;\n    return res;\n}\nstatic bool cmps(float a, float b, int type) {\n    bool res;\n    switch (type % 4) {\n        case 0: res = a == b; break;\n        case 1: res = a < b; break;\n        case 2: res = a <= b; break;\n        case 3: res = isnan(a) || isnan(b); break;\n    }\n    if (type >= 4) res = !res;\n    return res;\n}\n\nvoid vec_single_fcmp64(NO_CPU, const double *src, union xmm_reg *dst, uint8_t type) {\n    dst->qw[0] = cmpd(dst->f64[0], *src, type) ? -1 : 0;\n}\nvoid vec_single_fcmp32(NO_CPU, const float *src, union xmm_reg *dst, uint8_t type) {\n    dst->u32[0] = cmps(dst->f32[0], *src, type) ? -1 : 0;\n}\n\nvoid vec_single_fadd64(NO_CPU, const double *src, double *dst) { *dst += *src; }\nvoid vec_single_fadd32(NO_CPU, const float *src, float *dst) { *dst += *src; }\nvoid vec_single_fmul64(NO_CPU, const double *src, double *dst) { *dst *= *src; }\nvoid vec_single_fmul32(NO_CPU, const float *src, float *dst) { *dst *= *src; }\nvoid vec_single_fsub64(NO_CPU, const double *src, double *dst) { *dst -= *src; }\nvoid vec_single_fsub32(NO_CPU, const float *src, float *dst) { *dst -= *src; }\nvoid vec_single_fdiv64(NO_CPU, const double *src, double *dst) { *dst /= *src; }\nvoid vec_single_fdiv32(NO_CPU, const float *src, float *dst) { *dst /= *src; }\n\nvoid vec_single_fsqrt64(NO_CPU, const double *src, double *dst) { *dst = sqrt(*src); }\nvoid vec_single_fsqrt32(NO_CPU, const float *src, float *dst) { *dst = sqrtf(*src); }\n\nvoid vec_single_fmax64(NO_CPU, const double *src, double *dst) {\n    if (*src > *dst || isnan(*src) || isnan(*dst)) *dst = *src;\n}\nvoid vec_single_fmin64(NO_CPU, const double *src, double *dst) {\n    if (*src < *dst || isnan(*src) || isnan(*dst)) *dst = *src;\n}\nvoid vec_single_fmax32(NO_CPU, const float *src, float *dst) {\n    if (*src > *dst || isnan(*src) || isnan(*dst)) *dst = *src;\n}\nvoid vec_single_fmin32(NO_CPU, const float *src, float *dst) {\n    if (*src < *dst || isnan(*src) || isnan(*dst)) *dst = *src;\n}\n\nvoid vec_single_ucomi32(struct cpu_state *cpu, const float *src, const float *dst) {\n    cpu->zf_res = cpu->pf_res = 0;\n    cpu->zf = *src == *dst;\n    cpu->cf = *src > *dst;\n    cpu->pf = 0;\n    if (isnan(*src) || isnan(*dst))\n        cpu->zf = cpu->cf = cpu->pf = 1;\n    cpu->of = cpu->sf = cpu->af = 0;\n    cpu->sf_res = 0;\n}\n\nvoid vec_single_ucomi64(struct cpu_state *cpu, const double *src, const double *dst) {\n    cpu->zf_res = cpu->pf_res = 0;\n    cpu->zf = *src == *dst;\n    cpu->cf = *src > *dst;\n    cpu->pf = 0;\n    if (isnan(*src) || isnan(*dst))\n        cpu->zf = cpu->cf = cpu->pf = 1;\n    cpu->of = cpu->sf = cpu->af = 0;\n    cpu->sf_res = 0;\n}\n\n#define VEC_PACKED_OP(name, op, field, size, n) \\\n    void vec_##name##size(NO_CPU, union xmm_reg *src, union xmm_reg *dst) { \\\n        for (int i = 0; i < n; ++i) { \\\n            dst->field[i] op##= src->field[i]; \\\n        } \\\n    }\n\nVEC_PACKED_OP(add_p, +, f64, 64, 2)\nVEC_PACKED_OP(add_p, +, f32, 32, 4)\nVEC_PACKED_OP(sub_p, -, f64, 64, 2)\nVEC_PACKED_OP(sub_p, -, f32, 32, 4)\nVEC_PACKED_OP(mul_p, *, f64, 64, 2)\nVEC_PACKED_OP(mul_p, *, f32, 32, 4)\n\nvoid vec_fcmp_p64(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t type) {\n    for (size_t i = 0; i < sizeof(dst->f64) / sizeof(*dst->f64); ++i) {\n        dst->qw[i] = cmpd(dst->f64[i], src->f64[i], type) ? -1 : 0;\n    }\n}\n\n// come to the dark side of macros\n#define _ISNAN_int32_t(x) false\n#define _ISNAN_float(x) isnan(x)\n#define _ISNAN_double(x) isnan(x)\n#define _ISNAN(x, t) _ISNAN_##t(x)\n#define _VEC_CVT(src, dst, src_t, dst_t, n) \\\n    do { \\\n        for (int i = 0; i < n; ++i) { \\\n            if (_ISNAN(((src_t *)src)[i], src_t)) \\\n                ((dst_t *)dst)[i] = INT32_MIN; \\\n            else \\\n                ((dst_t *)dst)[i] = ((src_t *)src)[i]; \\\n        } \\\n    } while (0)\n\n#define VEC_CVT(name, src_t, dst_t) \\\n    void vec_cvt##name(NO_CPU, const src_t *src, dst_t *dst) { \\\n        _VEC_CVT(src, dst, src_t, dst_t, 1); \\\n    }\n\n#define PACKED_VEC_CVT(name, src_field, dst_field, src_t, dst_t, n) \\\n    void vec_cvt##name(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) { \\\n        _VEC_CVT(src->src_field, dst->dst_field, src_t, dst_t, n); \\\n        /* Note: this needs to be second, because src and dst may alias */ \\\n        memset(dst->dst_field + n, 0, sizeof(*dst) - n * sizeof(*dst->dst_field)); \\\n    }\n\nVEC_CVT(si2sd32, int32_t, double)\nVEC_CVT(tsd2si64, double, int32_t)\nVEC_CVT(sd2ss64, double, float)\nVEC_CVT(si2ss32, int32_t, float)\nVEC_CVT(tss2si32, float, int32_t)\nVEC_CVT(ss2sd32, float, double)\n\nPACKED_VEC_CVT(tpd2dq64, f64, u32, double, int32_t, 2)\nPACKED_VEC_CVT(tps2dq32, f32, u32, float, int32_t, 4)\n\nvoid vec_unpackl_bw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 7; i >= 0; i--) {\n        dst->u8[i*2 + 1] = src->u8[i];\n        dst->u8[i*2] = dst->u8[i];\n    }\n}\nvoid vec_unpackl_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 3; i >= 0; i--) {\n        dst->u16[i*2 + 1] = src->u16[i];\n        dst->u16[i*2] = dst->u16[i];\n    }\n}\nvoid vec_unpackl_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[3] = src->u32[1];\n    dst->u32[2] = dst->u32[1];\n    dst->u32[1] = src->u32[0];\n}\nvoid vec_unpackl_qdq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->qw[1] = src->qw[0];\n}\nvoid vec_unpackl_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[2] = dst->u32[1];\n    dst->u32[1] = src->u32[0];\n    dst->u32[3] = src->u32[1];\n}\nvoid vec_unpackl_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->f64[1] = src->f64[0];\n}\nvoid vec_unpackh_bw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 0; i < 8; i++) {\n        dst->u8[2 * i + 0] = dst->u8[i + 8];\n        dst->u8[2 * i + 1] = src->u8[i + 8];\n    }\n}\nvoid vec_unpackh_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 0; i < 4; i++) {\n        dst->u16[2 * i + 0] = dst->u16[i + 4];\n        dst->u16[2 * i + 1] = src->u16[i + 4];\n    }\n}\nvoid vec_unpackh_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = dst->u32[2];\n    dst->u32[1] = src->u32[2];\n    dst->u32[2] = dst->u32[3];\n    dst->u32[3] = src->u32[3];\n}\nvoid vec_unpackh_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->qw[0] = dst->qw[1];\n    dst->qw[1] = src->qw[1];\n}\nvoid vec_unpackh_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = dst->u32[2];\n    dst->u32[1] = src->u32[2];\n    dst->u32[2] = dst->u32[3];\n    dst->u32[3] = src->u32[3];\n}\nvoid vec_unpackh_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->f64[0] = dst->f64[1];\n    dst->f64[1] = src->f64[1];\n}\n\nvoid vec_packss_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = (satsw(dst->u16[0]) << 0x00) | (satsw(dst->u16[1]) << 0x08) |\n                  (satsw(dst->u16[2]) << 0x10) | (satsw(dst->u16[3]) << 0x18);\n    dst->u32[1] = (satsw(dst->u16[4]) << 0x00) | (satsw(dst->u16[5]) << 0x08) |\n                  (satsw(dst->u16[6]) << 0x10) | (satsw(dst->u16[7]) << 0x18);\n    dst->u32[2] = (satsw(src->u16[0]) << 0x00) | (satsw(src->u16[1]) << 0x08) |\n                  (satsw(src->u16[2]) << 0x10) | (satsw(src->u16[3]) << 0x18);\n    dst->u32[3] = (satsw(src->u16[4]) << 0x00) | (satsw(src->u16[5]) << 0x08) |\n                  (satsw(src->u16[6]) << 0x10) | (satsw(src->u16[7]) << 0x18);\n}\nvoid vec_packss_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = satud(dst->u32[0]) | (satud(dst->u32[1]) << 16);\n    dst->u32[1] = satud(dst->u32[2]) | (satud(dst->u32[3]) << 16);\n    dst->u32[2] = satud(src->u32[0]) | (satud(src->u32[1]) << 16);\n    dst->u32[3] = satud(src->u32[2]) | (satud(src->u32[3]) << 16);\n}\nvoid vec_packsu_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    dst->u32[0] = (satub(dst->u16[0]) << 0x00) | (satub(dst->u16[1]) << 0x08) |\n                  (satub(dst->u16[2]) << 0x10) | (satub(dst->u16[3]) << 0x18);\n    dst->u32[1] = (satub(dst->u16[4]) << 0x00) | (satub(dst->u16[5]) << 0x08) |\n                  (satub(dst->u16[6]) << 0x10) | (satub(dst->u16[7]) << 0x18);\n    dst->u32[2] = (satub(src->u16[0]) << 0x00) | (satub(src->u16[1]) << 0x08) |\n                  (satub(src->u16[2]) << 0x10) | (satub(src->u16[3]) << 0x18);\n    dst->u32[3] = (satub(src->u16[4]) << 0x00) | (satub(src->u16[5]) << 0x08) |\n                  (satub(src->u16[6]) << 0x10) | (satub(src->u16[7]) << 0x18);\n}\n\nvoid vec_shuffle_lw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding) {\n    union xmm_reg src_copy = *src;\n    for (int i = 0; i < 4; i++)\n        dst->u16[i] = src_copy.u16[(encoding >> (i*2)) % 4];\n    dst->qw[1] = src->qw[1];\n}\nvoid vec_shuffle_hw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding) {\n    union xmm_reg src_copy = *src;\n    dst->qw[0] = src->qw[0];\n    dst->u32[2] = src_copy.u16[(encoding >> 0 & 3) | 4] | src_copy.u16[(encoding >> 2 & 3) | 4] << 16;\n    dst->u32[3] = src_copy.u16[(encoding >> 4 & 3) | 4] | src_copy.u16[(encoding >> 6 & 3) | 4] << 16;\n}\n\nvoid vec_shuffle_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding) {\n    union xmm_reg src_copy = *src;\n    for (int i = 0; i < 4; i++)\n        dst->u32[i] = src_copy.u32[(encoding >> (i*2)) % 4];\n}\nvoid vec_shuffle_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding) {\n    dst->u32[0] = dst->u32[(encoding >> 0) & 3];\n    dst->u32[1] = dst->u32[(encoding >> 2) & 3];\n    dst->u32[2] = src->u32[(encoding >> 4) & 3];\n    dst->u32[3] = src->u32[(encoding >> 6) & 3];\n}\nvoid vec_shuffle_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding) {\n    dst->qw[0] = dst->qw[(encoding >> 0) & 1];\n    dst->qw[1] = src->qw[(encoding >> 1) & 1];\n}\n\nvoid vec_movmask_b128(NO_CPU, const union xmm_reg *src, uint32_t *dst) {\n    *dst = 0;\n    for (unsigned i = 0; i < array_size(src->u8); i++) {\n        if (src->u8[i] & (1 << 7))\n            *dst |= 1 << i;\n    }\n}\nvoid vec_fmovmask_d128(NO_CPU, const union xmm_reg *src, uint32_t *dst) {\n    *dst = 0;\n    for (unsigned i = 0; i < array_size(src->f64); i++) {\n        if (signbit(src->f64[i]))\n            *dst |= 1 << i;\n    }\n}\n\nvoid vec_movl_p64(NO_CPU, const uint64_t *src, union xmm_reg *dst) {\n    dst->qw[0] = *src;\n}\nvoid vec_movl_pm64(NO_CPU, const union xmm_reg *src, uint64_t *dst) {\n    *dst = src->qw[0];\n}\nvoid vec_movh_p64(NO_CPU, const uint64_t *src, union xmm_reg *dst) {\n    dst->qw[1] = *src;\n}\nvoid vec_movh_pm64(NO_CPU, const union xmm_reg *src, uint64_t *dst) {\n    *dst = src->qw[1];\n}\n\nvoid vec_insert_w128(NO_CPU, const uint32_t *src, union xmm_reg *dst, uint8_t index) {\n    dst->u16[index % 8] = (uint16_t)*src;\n}\nvoid vec_extract_w128(NO_CPU, const union xmm_reg *src, uint32_t *dst, uint8_t index) {\n    *dst = src->u16[index % 8];\n}\n\nvoid vec_avg_b128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 16; i++)\n        dst->u8[i] = (1 + dst->u8[i] + src->u8[i]) >> 1;\n}\nvoid vec_avg_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (unsigned i = 0; i < 8; i++)\n        dst->u16[i] = (1 + dst->u16[i] + src->u16[i]) >> 1;\n}\n\nvoid vec_mull128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 0; i < 8; i++) {\n        dst->u16[i] = (uint16_t)(dst->u16[i] * src->u16[i]);\n    }\n}\n\nvoid vec_mulu128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 0; i < 8; i++) {\n        uint32_t res = ((int16_t)dst->u16[i] * (int16_t)src->u16[i]);\n        dst->u16[i] = ((res >> 16) & 0xffff);\n    }\n}\nvoid vec_muluu128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst) {\n    for (int i = 0; i < 8; i++) {\n        uint32_t res = dst->u16[i] * src->u16[i];\n        dst->u16[i] = ((res >> 16) & 0xffff);\n    }\n}\n"
  },
  {
    "path": "emu/vec.h",
    "content": "#ifndef EMU_SSE_H\n#define EMU_SSE_H\n\n#include \"emu/cpu.h\"\n\n#define NO_CPU struct cpu_state *UNUSED(cpu)\n\n// arguments are in src, dst order\n\nvoid vec_zero128_copy128(NO_CPU, const void *src, void *dst);\nvoid vec_zero128_copy64(NO_CPU, const void *src, void *dst);\nvoid vec_zero128_copy32(NO_CPU, const void *src, void *dst);\nvoid vec_zero64_copy64(NO_CPU, const void *src, void *dst);\nvoid vec_zero64_copy32(NO_CPU, const void *src, void *dst);\nvoid vec_zero32_copy32(NO_CPU, const void *src, void *dst);\n// \"merge\" means don't zero the register before writing to it\nvoid vec_merge32(NO_CPU, const void *src, void *dst);\nvoid vec_merge64(NO_CPU, const void *src, void *dst);\nvoid vec_merge128(NO_CPU, const void *src, void *dst);\n\nvoid vec_shiftl_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftl_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftl_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftr_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftr_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftr_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftrs_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_shiftrs_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\n\nvoid vec_shiftl_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftl_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftl_q128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftr_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftr_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftr_q128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftrs_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_shiftrs_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_imm_shiftl_w64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftl_d64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftl_q64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftr_w64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftr_d64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftr_q64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftrs_w64(NO_CPU, const uint8_t amount, union mm_reg *dst);\nvoid vec_imm_shiftrs_d64(NO_CPU, const uint8_t amount, union mm_reg *dst);\n\nvoid vec_imm_shiftl_w128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftl_q128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftl_d128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftl_dq128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\n\nvoid vec_imm_shiftr_q128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftr_w128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftr_d128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\n\nvoid vec_imm_shiftr_dq128(NO_CPU, uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftrs_w128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\nvoid vec_imm_shiftrs_d128(NO_CPU, const uint8_t amount, union xmm_reg *dst);\n\nvoid vec_add_b64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_add_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_add_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_add_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\n\nvoid vec_sub_b64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_sub_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_sub_d64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_sub_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\n\nvoid vec_add_b128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_add_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_add_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_add_q128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_addus_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_addus_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_addss_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_addss_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_sub_b128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sub_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sub_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sub_q128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_subus_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_subus_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_subss_b128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_subss_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mulu_dq128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mulu_dq64(NO_CPU, union mm_reg *src, union mm_reg *dst);\nvoid vec_mulu64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_mull64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_mulu128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_muluu128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mull128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_madd_d128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sumabs_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_add_p64(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_add_p32(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sub_p64(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_sub_p32(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mul_p64(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mul_p32(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_or_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_xor_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_and_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_andn128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_or_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_and_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_xor_q64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\n\nvoid vec_min_ub128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_mins_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_max_ub128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\nvoid vec_maxs_w128(NO_CPU, union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_single_fadd64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fadd32(NO_CPU, const float *src, float *dst);\nvoid vec_single_fmul64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fmul32(NO_CPU, const float *src, float *dst);\nvoid vec_single_fsub64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fsub32(NO_CPU, const float *src, float *dst);\nvoid vec_single_fdiv64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fdiv32(NO_CPU, const float *src, float *dst);\nvoid vec_single_fsqrt64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fsqrt32(NO_CPU, const float *src, float *dst);\n\nvoid vec_single_fmax64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fmax32(NO_CPU, const float *src, float *dst);\nvoid vec_single_fmin64(NO_CPU, const double *src, double *dst);\nvoid vec_single_fmin32(NO_CPU, const float *src, float *dst);\nvoid vec_single_ucomi32(struct cpu_state *cpu, const float *src, const float *dst);\nvoid vec_single_ucomi64(struct cpu_state *cpu, const double *src, const double *dst);\nvoid vec_single_fcmp64(NO_CPU, const double *src, union xmm_reg *dst, uint8_t type);\nvoid vec_single_fcmp32(NO_CPU, const float *src, union xmm_reg *dst, uint8_t type);\nvoid vec_fcmp_p64(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t type);\n\nvoid vec_cvtsi2sd32(NO_CPU, const int32_t *src, double *dst);\nvoid vec_cvttsd2si64(NO_CPU, const double *src, int32_t *dst);\nvoid vec_cvtsd2ss64(NO_CPU, const double *src, float *dst);\nvoid vec_cvtsi2ss32(NO_CPU, const int32_t *src, float *dst);\nvoid vec_cvttss2si32(NO_CPU, const float *src, int32_t *dst);\nvoid vec_cvtss2sd32(NO_CPU, const float *src, double *dst);\n\nvoid vec_cvttpd2dq64(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_cvttps2dq32(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\n// TODO organize\nvoid vec_packss_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_packsu_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_packss_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_unpackl_bw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackl_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackl_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackl_dq64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_unpackl_qdq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackl_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackl_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_bw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_dq128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_unpackh_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_shuffle_w64(NO_CPU, const union mm_reg *src, union mm_reg *dst, uint8_t encoding);\n\nvoid vec_shuffle_lw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding);\nvoid vec_shuffle_hw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding);\n\nvoid vec_shuffle_d128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding);\nvoid vec_shuffle_ps128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding);\nvoid vec_shuffle_pd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst, uint8_t encoding);\n\nvoid vec_compare_eqb64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_compare_eqw64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_compare_eqd64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_compares_gtb64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_compares_gtw64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\nvoid vec_compares_gtd64(NO_CPU, const union mm_reg *src, union mm_reg *dst);\n\nvoid vec_compare_eqb128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_compare_eqw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_compare_eqd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_compares_gtb128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_compares_gtw128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_compares_gtd128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\nvoid vec_movl_p64(NO_CPU, const uint64_t *src, union xmm_reg *dst);\nvoid vec_movl_pm64(NO_CPU, const union xmm_reg *src, uint64_t *dst);\nvoid vec_movh_p64(NO_CPU, const uint64_t *src, union xmm_reg *dst);\nvoid vec_movh_pm64(NO_CPU, const union xmm_reg *src, uint64_t *dst);\n\nvoid vec_movmask_b64(NO_CPU, const union mm_reg *src, uint32_t *dst);\nvoid vec_movmask_b128(NO_CPU, const union xmm_reg *src, uint32_t *dst);\nvoid vec_fmovmask_d128(NO_CPU, const union xmm_reg *src, uint32_t *dst);\n\nvoid vec_insert_w64(NO_CPU, const uint32_t *src, union mm_reg *dst, uint8_t index);\nvoid vec_insert_w128(NO_CPU, const uint32_t *src, union xmm_reg *dst, uint8_t index);\nvoid vec_extract_w128(NO_CPU, const union xmm_reg *src, uint32_t *dst, uint8_t index);\n\nvoid vec_avg_b128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\nvoid vec_avg_w128(NO_CPU, const union xmm_reg *src, union xmm_reg *dst);\n\n#endif\n"
  },
  {
    "path": "fastlane/Appfile",
    "content": "app_identifier \"app.ish.iSH\" # The bundle identifier of your app\napple_id \"tblodt@icloud.com\" # Your Apple email address\n\nteam_id \"CK5SXRTBR7\" # Developer Portal Team ID\nitc_team_name \"Theodore Dubois\"\n\n# you can even provide different app identifiers, Apple IDs and team names per lane:\n# More information: https://docs.fastlane.tools/advanced/#appfile\n"
  },
  {
    "path": "fastlane/Deliverfile",
    "content": "###################### More Options ######################\n# If you want to have even more control, check out the documentation\n# https://docs.fastlane.tools/actions/deliver\n\n\n###################### Automatically generated ######################\n# Feel free to remove the following line if you use fastlane (which you should)\n\napp_identifier \"app.ish.iSH\" # The bundle identifier of your app\nusername \"tblodt@icloud.com\" # your Apple ID user\n"
  },
  {
    "path": "fastlane/Fastfile",
    "content": "before_all do\n    ensure_bundle_exec\nend\n\nlane :build do |options|\n    setup_ci\n    sync_code_signing\n    sync_code_signing(type: 'appstore')\n\n    # do this after committing the version bump but before building the app\n    if is_ci\n      update_code_signing_settings(\n        path: \"iSH.xcodeproj\",\n        targets: \"iSH\",\n        use_automatic_signing: false,\n        profile_uuid: ENV[\"sigh_app.ish.iSH_development\"],\n      )\n      update_code_signing_settings(\n        path: \"iSH.xcodeproj\",\n        targets: \"iSHFileProvider\",\n        use_automatic_signing: false,\n        profile_uuid: ENV[\"sigh_app.ish.iSH.FileProvider_development\"],\n      )\n    end\n\n    config = options[:config]\n    config = \"app/#{config}.xcconfig\" if config\n    build_app(\n      project: \"iSH.xcodeproj\",\n      scheme: \"iSH\",\n      xcconfig: config,\n      xcargs: \"DEVELOPMENT_TEAM=#{CredentialsManager::AppfileConfig.try_fetch_value(:team_id)}\",\n      output_name: options[:output],\n    )\nend\n\nlane :upload_build do\n    last_tag = `git describe --tags --abbrev=0 --match builds/\\*`.chomp\n    shortlog = `git shortlog #{last_tag}..HEAD`\n    if shortlog.empty?\n        UI.error \"No commits since last build\"\n        next\n    end\n    changelog = \"Automated daily build\"\n    testflight_changelog = changelog + \"\\n\" + File.read(\"footer.txt\") + shortlog\n\n    app_store_connect_api_key\n    latest = latest_testflight_build_number.to_s.scan(/^\\d+(?=\\.|$)/).first.to_i\n    build_number = latest + 1\n    Dir.chdir(\"..\") do\n        sh \"agvtool\", \"new-version\", build_number.to_s\n    end\n    commit_version_bump(\n        xcodeproj: \"iSH.xcodeproj\",\n        message: \"Bump version to #{build_number}\",\n        force: true,\n    )\n    tag = \"builds/#{build_number}\"\n    add_git_tag(tag: tag)\n\n    build\n    puts testflight_changelog\n    upload_to_testflight(\n        ipa: \"iSH.ipa\",\n        changelog: testflight_changelog,\n        wait_processing_interval: 300, # the processing is expected to take a total of 5 hours, so don't logspam too much\n        distribute_external: true,\n        groups: [\"People\"],\n    )\n\n    # uploading a build takes about 5 hours, so merge master back in if there have been any commits during that\n    sh \"git pull --no-rebase\"\n    push_to_git_remote\n    set_github_release(\n        repository_name: \"ish-app/ish\",\n        tag_name: tag,\n        commitish: nil, # the tag better exist\n        name: \"Build #{build_number}\",\n        description: changelog,\n        is_prerelease: true,\n        upload_assets: [\"iSH.ipa\", \"iSH.app.dSYM.zip\"],\n        api_token: ENV[\"GH_TOKEN\"],\n    )\nend\n"
  },
  {
    "path": "fastlane/Matchfile",
    "content": "git_url(\"https://github.com/ish-app/certificates\")\napp_identifier([\"app.ish.iSH\", \"app.ish.iSH.FileProvider\"])\n"
  },
  {
    "path": "fastlane/README.md",
    "content": "fastlane documentation\n================\n# Installation\n\nMake sure you have the latest version of the Xcode command line tools installed:\n\n```\nxcode-select --install\n```\n\nInstall _fastlane_ using\n```\n[sudo] gem install fastlane -NV\n```\nor alternatively using `brew install fastlane`\n\n# Available Actions\n### build\n```\nfastlane build\n```\n\n### upload_build\n```\nfastlane upload_build\n```\n\n\n----\n\nThis README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.\nMore information about fastlane can be found on [fastlane.tools](https://fastlane.tools).\nThe documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).\n"
  },
  {
    "path": "fastlane/Snapfile",
    "content": "scheme(\"Screenshots\")\n\ndevices([\n  \"iPhone 11\",\n  \"iPhone Xs Max\",\n  \"iPhone Xʀ\",\n  \"iPhone X\",\n  \"iPhone 8 Plus\",\n  \"iPhone 8\",\n  \"iPhone SE (1st generation)\",\n  \"iPad Pro (12.9-inch) (3rd generation)\",\n  \"iPad Pro (12.9-inch) (2nd generation)\",\n  \"iPad Pro (11-inch) (2nd generation)\",\n  \"iPad Pro (10.5-inch)\",\n  \"iPad Pro (9.7-inch)\",\n])\n\nlanguages([\n  \"en-US\",\n])\n\ndark_mode(true)\n\noverride_status_bar(true)\nxcargs(\"ARCHS=arm64\")\nreinstall_app(true)\nclear_previous_screenshots(true)\nstop_after_first_error(true)\nnumber_of_retries(2) # it's buggy\ndisable_slide_to_type(true)\n"
  },
  {
    "path": "fastlane/SnapshotHelper.swift",
    "content": "//\n//  SnapshotHelper.swift\n//  Example\n//\n//  Created by Felix Krause on 10/8/15.\n//\n\n// -----------------------------------------------------\n// IMPORTANT: When modifying this file, make sure to\n//            increment the version number at the very\n//            bottom of the file to notify users about\n//            the new SnapshotHelper.swift\n// -----------------------------------------------------\n\nimport Foundation\nimport XCTest\n\nvar deviceLanguage = \"\"\nvar locale = \"\"\n\nfunc setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {\n    Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations)\n}\n\nfunc snapshot(_ name: String, waitForLoadingIndicator: Bool) {\n    if waitForLoadingIndicator {\n        Snapshot.snapshot(name)\n    } else {\n        Snapshot.snapshot(name, timeWaitingForIdle: 0)\n    }\n}\n\n/// - Parameters:\n///   - name: The name of the snapshot\n///   - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait.\nfunc snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {\n    Snapshot.snapshot(name, timeWaitingForIdle: timeout)\n}\n\nenum SnapshotError: Error, CustomDebugStringConvertible {\n    case cannotFindSimulatorHomeDirectory\n    case cannotRunOnPhysicalDevice\n\n    var debugDescription: String {\n        switch self {\n        case .cannotFindSimulatorHomeDirectory:\n            return \"Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable.\"\n        case .cannotRunOnPhysicalDevice:\n            return \"Can't use Snapshot on a physical device.\"\n        }\n    }\n}\n\n@objcMembers\nopen class Snapshot: NSObject {\n    static var app: XCUIApplication?\n    static var waitForAnimations = true\n    static var cacheDirectory: URL?\n    static var screenshotsDirectory: URL? {\n        return cacheDirectory?.appendingPathComponent(\"screenshots\", isDirectory: true)\n    }\n\n    open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {\n\n        Snapshot.app = app\n        Snapshot.waitForAnimations = waitForAnimations\n\n        do {\n            let cacheDir = try getCacheDirectory()\n            Snapshot.cacheDirectory = cacheDir\n            setLanguage(app)\n            setLocale(app)\n            setLaunchArguments(app)\n        } catch let error {\n            NSLog(error.localizedDescription)\n        }\n    }\n\n    class func setLanguage(_ app: XCUIApplication) {\n        guard let cacheDirectory = self.cacheDirectory else {\n            NSLog(\"CacheDirectory is not set - probably running on a physical device?\")\n            return\n        }\n\n        let path = cacheDirectory.appendingPathComponent(\"language.txt\")\n\n        do {\n            let trimCharacterSet = CharacterSet.whitespacesAndNewlines\n            deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)\n            app.launchArguments += [\"-AppleLanguages\", \"(\\(deviceLanguage))\"]\n        } catch {\n            NSLog(\"Couldn't detect/set language...\")\n        }\n    }\n\n    class func setLocale(_ app: XCUIApplication) {\n        guard let cacheDirectory = self.cacheDirectory else {\n            NSLog(\"CacheDirectory is not set - probably running on a physical device?\")\n            return\n        }\n\n        let path = cacheDirectory.appendingPathComponent(\"locale.txt\")\n\n        do {\n            let trimCharacterSet = CharacterSet.whitespacesAndNewlines\n            locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)\n        } catch {\n            NSLog(\"Couldn't detect/set locale...\")\n        }\n\n        if locale.isEmpty && !deviceLanguage.isEmpty {\n            locale = Locale(identifier: deviceLanguage).identifier\n        }\n\n        if !locale.isEmpty {\n            app.launchArguments += [\"-AppleLocale\", \"\\\"\\(locale)\\\"\"]\n        }\n    }\n\n    class func setLaunchArguments(_ app: XCUIApplication) {\n        guard let cacheDirectory = self.cacheDirectory else {\n            NSLog(\"CacheDirectory is not set - probably running on a physical device?\")\n            return\n        }\n\n        let path = cacheDirectory.appendingPathComponent(\"snapshot-launch_arguments.txt\")\n        app.launchArguments += [\"-FASTLANE_SNAPSHOT\", \"YES\", \"-ui_testing\"]\n\n        do {\n            let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8)\n            let regex = try NSRegularExpression(pattern: \"(\\\\\\\".+?\\\\\\\"|\\\\S+)\", options: [])\n            let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count))\n            let results = matches.map { result -> String in\n                (launchArguments as NSString).substring(with: result.range)\n            }\n            app.launchArguments += results\n        } catch {\n            NSLog(\"Couldn't detect/set launch_arguments...\")\n        }\n    }\n\n    open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {\n        if timeout > 0 {\n            waitForLoadingIndicatorToDisappear(within: timeout)\n        }\n\n        NSLog(\"snapshot: \\(name)\") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work\n\n        if Snapshot.waitForAnimations {\n            sleep(1) // Waiting for the animation to be finished (kind of)\n        }\n\n        #if os(OSX)\n            guard let app = self.app else {\n                NSLog(\"XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().\")\n                return\n            }\n\n            app.typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: [])\n        #else\n\n            guard self.app != nil else {\n                NSLog(\"XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().\")\n                return\n            }\n\n            let screenshot = XCUIScreen.main.screenshot()\n            #if os(iOS) && !targetEnvironment(macCatalyst)\n            let image = XCUIDevice.shared.orientation.isLandscape ?  fixLandscapeOrientation(image: screenshot.image) : screenshot.image\n            #else\n            let image = screenshot.image\n            #endif\n\n            guard var simulator = ProcessInfo().environment[\"SIMULATOR_DEVICE_NAME\"], let screenshotsDir = screenshotsDirectory else { return }\n\n            do {\n                // The simulator name contains \"Clone X of \" inside the screenshot file when running parallelized UI Tests on concurrent devices\n                let regex = try NSRegularExpression(pattern: \"Clone [0-9]+ of \")\n                let range = NSRange(location: 0, length: simulator.count)\n                simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: \"\")\n\n                let path = screenshotsDir.appendingPathComponent(\"\\(simulator)-\\(name).png\")\n                #if swift(<5.0)\n                    try UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)\n                #else\n                    try image.pngData()?.write(to: path, options: .atomic)\n                #endif\n            } catch let error {\n                NSLog(\"Problem writing screenshot: \\(name) to \\(screenshotsDir)/\\(simulator)-\\(name).png\")\n                NSLog(error.localizedDescription)\n            }\n        #endif\n    }\n\n    class func fixLandscapeOrientation(image: UIImage) -> UIImage {\n        #if os(watchOS)\n            return image\n        #else\n            if #available(iOS 10.0, *) {\n                let format = UIGraphicsImageRendererFormat()\n                format.scale = image.scale\n                let renderer = UIGraphicsImageRenderer(size: image.size, format: format)\n                return renderer.image { context in\n                    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))\n                }\n            } else {\n                return image\n            }\n        #endif\n    }\n\n    class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) {\n        #if os(tvOS)\n            return\n        #endif\n\n        guard let app = self.app else {\n            NSLog(\"XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().\")\n            return\n        }\n\n        let networkLoadingIndicator = app.otherElements.deviceStatusBars.networkLoadingIndicators.element\n        let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: \"exists == false\"), object: networkLoadingIndicator)\n        _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout)\n    }\n\n    class func getCacheDirectory() throws -> URL {\n        let cachePath = \"Library/Caches/tools.fastlane\"\n        // on OSX config is stored in /Users/<username>/Library\n        // and on iOS/tvOS/WatchOS it's in simulator's home dir\n        #if os(OSX)\n            let homeDir = URL(fileURLWithPath: NSHomeDirectory())\n            return homeDir.appendingPathComponent(cachePath)\n        #elseif arch(i386) || arch(x86_64) || arch(arm64)\n            guard let simulatorHostHome = ProcessInfo().environment[\"SIMULATOR_HOST_HOME\"] else {\n                throw SnapshotError.cannotFindSimulatorHomeDirectory\n            }\n            let homeDir = URL(fileURLWithPath: simulatorHostHome)\n            return homeDir.appendingPathComponent(cachePath)\n        #else\n            throw SnapshotError.cannotRunOnPhysicalDevice\n        #endif\n    }\n}\n\nprivate extension XCUIElementAttributes {\n    var isNetworkLoadingIndicator: Bool {\n        if hasAllowListedIdentifier { return false }\n\n        let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20)\n        let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3)\n\n        return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize\n    }\n\n    var hasAllowListedIdentifier: Bool {\n        let allowListedIdentifiers = [\"GeofenceLocationTrackingOn\", \"StandardLocationTrackingOn\"]\n\n        return allowListedIdentifiers.contains(identifier)\n    }\n\n    func isStatusBar(_ deviceWidth: CGFloat) -> Bool {\n        if elementType == .statusBar { return true }\n        guard frame.origin == .zero else { return false }\n\n        let oldStatusBarSize = CGSize(width: deviceWidth, height: 20)\n        let newStatusBarSize = CGSize(width: deviceWidth, height: 44)\n\n        return [oldStatusBarSize, newStatusBarSize].contains(frame.size)\n    }\n}\n\nprivate extension XCUIElementQuery {\n    var networkLoadingIndicators: XCUIElementQuery {\n        let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in\n            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }\n\n            return element.isNetworkLoadingIndicator\n        }\n\n        return self.containing(isNetworkLoadingIndicator)\n    }\n\n    var deviceStatusBars: XCUIElementQuery {\n        guard let app = Snapshot.app else {\n            fatalError(\"XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().\")\n        }\n\n        let deviceWidth = app.windows.firstMatch.frame.width\n\n        let isStatusBar = NSPredicate { (evaluatedObject, _) in\n            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }\n\n            return element.isStatusBar(deviceWidth)\n        }\n\n        return self.containing(isStatusBar)\n    }\n}\n\nprivate extension CGFloat {\n    func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool {\n        return numberA...numberB ~= self\n    }\n}\n\n// Please don't remove the lines below\n// They are used to detect outdated configuration files\n// SnapshotHelperVersion [1.29]\n"
  },
  {
    "path": "fastlane/footer.txt",
    "content": "Send bug reports or feedback here: https://github.com/ish-app/ish/issues (or by email, if you prefer)\nFollow iSH on the Fediverse: @ish@ish.app\nJoin the Discord: https://discord.gg/HFAXj44\nGive me money here: https://patreon.com/tbodt\n\n"
  },
  {
    "path": "fs/adhoc.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"debug.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"kernel/errno.h\"\n\nstatic struct mount adhoc_mount;\n\nstruct fd *adhoc_fd_create(const struct fd_ops *ops) {\n    struct fd *fd = fd_create(ops);\n    if (fd == NULL)\n        return NULL;\n    mount_retain(&adhoc_mount);\n    fd->mount = &adhoc_mount;\n    fd->stat = (struct statbuf) {};\n    return fd;\n}\n\nstatic int adhoc_fstat(struct fd *fd, struct statbuf *stat) {\n    *stat = fd->stat;\n    return 0;\n}\n\nstatic int adhoc_fsetattr(struct fd *fd, struct attr attr) {\n    switch (attr.type) {\n        case attr_uid:\n            fd->stat.uid = attr.uid;\n            break;\n        case attr_gid:\n            fd->stat.gid = attr.gid;\n            break;\n        case attr_mode:\n            fd->stat.mode = (fd->stat.mode & S_IFMT) | (attr.mode & ~S_IFMT);\n            break;\n        case attr_size:\n            return _EINVAL;\n    }\n    return 0;\n}\n\nstatic int adhoc_getpath(struct fd *fd, char *buf) {\n    const char *type = \"unknown\"; // TODO allow this to be customized\n    if (fd->stat.inode == 0)\n        sprintf(buf, \"anon_inode:[%s]\", type);\n    else\n        sprintf(buf, \"%s:[%lu]\", type, (unsigned long) fd->stat.inode);\n    return 0;\n}\n\nbool is_adhoc_fd(struct fd *fd) {\n    return fd->mount == &adhoc_mount;\n}\n\nstatic const struct fs_ops adhoc_fs = {\n    .magic = 0x09041934, // FIXME wrong for pipes and sockets\n    .fstat = adhoc_fstat,\n    .fsetattr = adhoc_fsetattr,\n    .getpath = adhoc_getpath,\n};\n\nstatic struct mount adhoc_mount = {\n    .fs = &adhoc_fs,\n    .point = \"\",\n};\n"
  },
  {
    "path": "fs/dev.c",
    "content": "#include \"kernel/errno.h\"\n#include \"fs/fd.h\"\n#include \"fs/dev.h\"\n#include \"fs/mem.h\"\n#include \"fs/tty.h\"\n#include \"fs/dyndev.h\"\n#include \"fs/devices.h\"\n\nstruct dev_ops *block_devs[256] = {\n    // no block devices yet\n};\nstruct dev_ops *char_devs[256] = {\n    [MEM_MAJOR] = &mem_dev,\n    [TTY_CONSOLE_MAJOR] = &tty_dev,\n    [TTY_ALTERNATE_MAJOR] = &tty_dev,\n    [TTY_PSEUDO_MASTER_MAJOR] = &tty_dev,\n    [TTY_PSEUDO_SLAVE_MAJOR] = &tty_dev,\n    [DYN_DEV_MAJOR] = &dyn_dev_char,\n};\n\nint dev_open(int major, int minor, int type, struct fd *fd) {\n    struct dev_ops *dev = (type == DEV_BLOCK ? block_devs : char_devs)[major];\n    if (dev == NULL)\n        return _ENXIO;\n    fd->ops = &dev->fd;\n    if (!dev->open)\n        return 0;\n    return dev->open(major, minor, fd);\n}\n"
  },
  {
    "path": "fs/dev.h",
    "content": "#ifndef DEV_H\n#define DEV_H\n\n#include <sys/types.h>\n#if __linux__\n#include <sys/sysmacros.h>\n#endif\n#include \"fs/fd.h\"\n\n// a dev_t is encoded like this in hex, where M is major and m is minor:\n// mmmMMMmm\n// (legacy I guess)\n\ntypedef uint32_t dev_t_;\n\nstatic inline dev_t_ dev_make(int major, int minor) {\n    return ((minor & 0xfff00) << 12) | (major << 8) | (minor & 0xff);\n}\nstatic inline int dev_major(dev_t_ dev) {\n    return (dev & 0xfff00) >> 8;\n}\nstatic inline int dev_minor(dev_t_ dev) {\n    return ((dev & 0xfff00000) >> 12) | (dev & 0xff);\n}\n\nstatic inline dev_t dev_real_from_fake(dev_t_ dev) {\n    return makedev(dev_major(dev), dev_minor(dev));\n}\nstatic inline dev_t_ dev_fake_from_real(dev_t dev) {\n    return dev_make(major(dev), minor(dev));\n}\n\n#define DEV_BLOCK 0\n#define DEV_CHAR 1\n\nstruct dev_ops {\n    int (*open)(int major, int minor, struct fd *fd);\n    struct fd_ops fd;\n};\n\nextern struct dev_ops *block_devs[];\nextern struct dev_ops *char_devs[];\n\nint dev_open(int major, int minor, int type, struct fd *fd);\n\nextern struct dev_ops null_dev;\n\n#endif\n"
  },
  {
    "path": "fs/devices.h",
    "content": "#ifndef FS_DEVICES_H\n#define FS_DEVICES_H\n\n// losely based on devices.txt from linux\n\n// --- memory devices ---\n#define MEM_MAJOR 1\n// /dev/null\n#define DEV_NULL_MINOR 3\n// /dev/zero\n#define DEV_ZERO_MINOR 5\n// /dev/full\n#define DEV_FULL_MINOR 7\n// /dev/random\n#define DEV_RANDOM_MINOR 8\n// /dev/urandom\n#define DEV_URANDOM_MINOR 9\n\n// --- tty devices ---\n// /dev/ttyX where X is minor\n#define TTY_CONSOLE_MAJOR 4\n\n// --- alternate tty devices ---\n#define TTY_ALTERNATE_MAJOR 5\n// /dev/tty\n#define DEV_TTY_MINOR 0\n// /dev/console\n#define DEV_CONSOLE_MINOR 1\n// /dev/ptmx\n#define DEV_PTMX_MINOR 2\n\n// --- pseudo tty devices ---\n#define TTY_PSEUDO_MASTER_MAJOR 128\n#define TTY_PSEUDO_SLAVE_MAJOR 136\n\n// --- dynamic devices ---\n#define DYN_DEV_MAJOR 240\n\n// /dev/clipboard\n#define DEV_CLIPBOARD_MINOR 0\n// /dev/gps\n#define DEV_LOCATION_MINOR 1\n\n#endif\n"
  },
  {
    "path": "fs/dir.c",
    "content": "#include <sys/stat.h>\n#include <string.h>\n\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n\nstatic unsigned long fd_telldir(struct fd *fd) {\n    unsigned long off = fd->offset;\n    if (fd->ops->telldir)\n        off = fd->ops->telldir(fd);\n    return off;\n}\n\nstatic void fd_seekdir(struct fd *fd, unsigned long off) {\n    fd->offset = off;\n    if (fd->ops->seekdir)\n        fd->ops->seekdir(fd, off);\n}\n\nstruct linux_dirent_ {\n    dword_t inode;\n    dword_t offset;\n    word_t reclen;\n    char name[];\n} __attribute__((packed));\n\nstruct linux_dirent64_ {\n    qword_t inode;\n    qword_t offset;\n    word_t reclen;\n    byte_t type;\n    char name[];\n} __attribute__((packed));\n\nsize_t fill_dirent_32(void *dirent_data, ino_t inode, off_t_ offset, const char *name, int type) {\n    struct linux_dirent_ *dirent = dirent_data;\n    dirent->inode = inode;\n    dirent->offset = offset;\n    dirent->reclen = offsetof(struct linux_dirent_, name) +\n        strlen(name) + 2; // name, null terminator, type\n    strcpy(dirent->name, name);\n    *((char *) dirent + dirent->reclen - 1) = type;\n    return dirent->reclen;\n}\n\nsize_t fill_dirent_64(void *dirent_data, ino_t inode, off_t_ offset, const char *name, int type) {\n    struct linux_dirent64_ *dirent = dirent_data;\n    dirent->inode = inode;\n    dirent->offset = offset;\n    dirent->reclen = offsetof(struct linux_dirent64_, name) +\n        strlen(name) + 1; // name, null terminator\n    dirent->type = type;\n    strcpy(dirent->name, name);\n    return dirent->reclen;\n}\n\nint_t sys_getdents_common(fd_t f, addr_t dirents, dword_t count,\n        size_t (*fill_dirent)(void *, ino_t, off_t_, const char *, int)) {\n    STRACE(\"getdents(%d, %#x, %#x)\", f, dirents, count);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    if (!S_ISDIR(fd->type) || fd->ops->readdir == NULL)\n        return _ENOTDIR;\n\n    dword_t orig_count = count;\n\n    long ptr;\n    int err;\n    int printed = 0;\n    while (true) {\n        ptr = fd_telldir(fd);\n        struct dir_entry entry;\n        err = fd->ops->readdir(fd, &entry);\n        if (err < 0)\n            return err;\n        if (err == 0)\n            break;\n\n        size_t max_reclen = sizeof(struct linux_dirent64_) + strlen(entry.name) + 4;\n        char dirent_data[max_reclen];\n        ino_t inode = entry.inode;\n        off_t_ offset = fd_telldir(fd);\n        const char *name = entry.name;\n        int type = 0;\n        size_t reclen = fill_dirent(dirent_data, inode, offset, name, type);\n        if (printed < 20) {\n            STRACE(\" {inode=%d, offset=%d, name=%s, type=%d, reclen=%d}\",\n                    inode, offset, name, type, reclen);\n            printed++;\n        }\n\n        if (reclen > count)\n            break;\n        if (user_write(dirents, dirent_data, reclen))\n            return _EFAULT;\n        dirents += reclen;\n        count -= reclen;\n    }\n\n    fd_seekdir(fd, ptr);\n    return orig_count - count;\n}\n\nint_t sys_getdents(fd_t f, addr_t dirents, uint_t count) {\n    return sys_getdents_common(f, dirents, count, fill_dirent_32);\n}\n\nint_t sys_getdents64(fd_t f, addr_t dirents, uint_t count) {\n    return sys_getdents_common(f, dirents, count, fill_dirent_64);\n}\n\n"
  },
  {
    "path": "fs/dyndev.c",
    "content": "#include \"kernel/errno.h\"\n#include \"fs/dev.h\"\n#include \"fs/dyndev.h\"\n#include \"fs/devices.h\"\n\n#define MAX_MINOR 255\n\n// Handles DYNDEV_MAJOR device number\n// XXX(stek29): unregister might be added later\nstruct dyn_dev_info {\n    // devs & next_dev lock\n    lock_t devs_lock;\n    // table of dev_ops registered by minor number\n    struct dev_ops *devs[MAX_MINOR+1];\n};\n\nstruct dyn_dev_info dyn_info_char = {\n    .devs_lock = LOCK_INITIALIZER,\n    .devs = {},\n};\n\nint dyn_dev_register(struct dev_ops *ops, int type, int major, int minor) {\n    // Validate arguments\n    if (minor < 0 || minor > MAX_MINOR) {\n        return _EINVAL;\n    }\n    if (major != DYN_DEV_MAJOR) {\n        return _EINVAL;\n    }\n    if (ops == NULL) {\n        return _EINVAL;\n    }\n    if (type != DEV_CHAR) {\n        return _EINVAL;\n    }\n\n    lock(&dyn_info_char.devs_lock);\n\n    // Make sure minor number isn't taken yet\n    if (dyn_info_char.devs[minor] != NULL) {\n        unlock(&dyn_info_char.devs_lock);\n        return _EEXIST;\n    }\n\n    dyn_info_char.devs[minor] = ops;\n    unlock(&dyn_info_char.devs_lock);\n\n    return 0;\n}\n\nstatic int dyn_open(int type, int major, int minor, struct fd *fd) {\n    assert(type == DEV_CHAR);\n    assert(major == DYN_DEV_MAJOR);\n    // it's safe to access devs without locking (read-only)\n    struct dev_ops *ops = dyn_info_char.devs[minor];\n    if (ops == NULL) {\n        return _ENXIO;\n    }\n    fd->ops = &ops->fd;\n\n    // Succeed if there's no open provided by ops\n    if (!ops->open)\n        return 0;\n    return ops->open(major, minor, fd);\n}\n\nstatic int dyn_open_char(int major, int minor, struct fd *fd) {\n    return dyn_open(DEV_CHAR, major, minor, fd);\n}\nstruct dev_ops dyn_dev_char = {\n    .open = dyn_open_char,\n};\n"
  },
  {
    "path": "fs/dyndev.h",
    "content": "#ifndef FS_DYN_DEV_H\n#define FS_DYN_DEV_H\n\n// dyn_dev's are dynamically added devices (character only for now)\n// with custom dev_ops assigned\n// It's useful to add new device \"drivers\" in runtime (for example,\n// devices only present on some platforms)\n\n// dev_ops handing char device with DYN_DEV_MAJOR major number\nextern struct dev_ops dyn_dev_char;\n\n// Registers new block/character device with provided major and\n// minor numbers, handled by provided ops\n//\n// ops should be valid for \"kernel\" lifetime (should not be freed, but\n// might be static), and should not be null\n//\n// type is DEV_BLOCK or DEV_CHAR\n// (only char is supported for now)\n//\n// major should be DYN_DEV_MAJOR\n//\n// minor should be 0-255\n//\n// Return value:\n//  - 0 on success\n//  - _EEXIST if provided minor number is alredy taken\n//  - _EINVAL if provided arguments are invalid\nextern int dyn_dev_register(struct dev_ops *ops, int type, int major, int minor);\n\n#endif\n"
  },
  {
    "path": "fs/fake-db.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/errno.h\"\n#include \"debug.h\"\n#include \"misc.h\"\n#include \"fs/fake-db.h\"\n\nstatic void db_check_error(struct fakefs_db *fs) {\n    int errcode = sqlite3_errcode(fs->db);\n    switch (errcode) {\n        case SQLITE_OK:\n        case SQLITE_ROW:\n        case SQLITE_DONE:\n            break;\n\n        default:\n            die(\"sqlite error: %d %#x %s\", errcode, sqlite3_extended_errcode(fs->db), sqlite3_errmsg(fs->db));\n    }\n}\n\nstatic sqlite3_stmt *db_prepare(struct fakefs_db *fs, const char *stmt) {\n    sqlite3_stmt *statement;\n    sqlite3_prepare_v2(fs->db, stmt, strlen(stmt) + 1, &statement, NULL);\n    db_check_error(fs);\n    return statement;\n}\n\nbool db_exec(struct fakefs_db *fs, sqlite3_stmt *stmt) {\n    int err = sqlite3_step(stmt);\n    db_check_error(fs);\n    return err == SQLITE_ROW;\n}\nvoid db_reset(struct fakefs_db *fs, sqlite3_stmt *stmt) {\n    sqlite3_reset(stmt);\n    db_check_error(fs);\n}\nvoid db_exec_reset(struct fakefs_db *fs, sqlite3_stmt *stmt) {\n    db_exec(fs, stmt);\n    db_reset(fs, stmt);\n}\n\nvoid db_begin_read(struct fakefs_db *fs) {\n    sqlite3_mutex_enter(fs->lock);\n    db_exec_reset(fs, fs->stmt.begin_deferred);\n}\nvoid db_begin_write(struct fakefs_db *fs) {\n    sqlite3_mutex_enter(fs->lock);\n    db_exec_reset(fs, fs->stmt.begin_immediate);\n}\nvoid db_commit(struct fakefs_db *fs) {\n    db_exec_reset(fs, fs->stmt.commit);\n    sqlite3_mutex_leave(fs->lock);\n}\nvoid db_rollback(struct fakefs_db *fs) {\n    db_exec_reset(fs, fs->stmt.rollback);\n    sqlite3_mutex_leave(fs->lock);\n}\n\nstatic void bind_path(sqlite3_stmt *stmt, int i, const char *path) {\n    sqlite3_bind_blob(stmt, i, path, strlen(path), SQLITE_TRANSIENT);\n}\n\ninode_t path_get_inode(struct fakefs_db *fs, const char *path) {\n    // select inode from paths where path = ?\n    bind_path(fs->stmt.path_get_inode, 1, path);\n    inode_t inode = 0;\n    if (db_exec(fs, fs->stmt.path_get_inode))\n        inode = sqlite3_column_int64(fs->stmt.path_get_inode, 0);\n    db_reset(fs, fs->stmt.path_get_inode);\n    return inode;\n}\nbool path_read_stat(struct fakefs_db *fs, const char *path, struct ish_stat *stat, inode_t *inode) {\n    // select inode, stat from stats natural join paths where path = ?\n    bind_path(fs->stmt.path_read_stat, 1, path);\n    bool exists = db_exec(fs, fs->stmt.path_read_stat);\n    if (exists) {\n        if (inode)\n            *inode = sqlite3_column_int64(fs->stmt.path_read_stat, 0);\n        if (stat)\n            *stat = *(struct ish_stat *) sqlite3_column_blob(fs->stmt.path_read_stat, 1);\n    }\n    db_reset(fs, fs->stmt.path_read_stat);\n    return exists;\n}\ninode_t path_create(struct fakefs_db *fs, const char *path, struct ish_stat *stat) {\n    // insert into stats (stat) values (?)\n    sqlite3_bind_blob(fs->stmt.path_create_stat, 1, stat, sizeof(*stat), SQLITE_TRANSIENT);\n    db_exec_reset(fs, fs->stmt.path_create_stat);\n    inode_t inode = sqlite3_last_insert_rowid(fs->db);\n    // insert or replace into paths values (?, last_insert_rowid())\n    bind_path(fs->stmt.path_create_path, 1, path);\n    db_exec_reset(fs, fs->stmt.path_create_path);\n    return inode;\n}\n\nvoid inode_read_stat_or_die(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat) {\n    if (!inode_read_stat_if_exist(fs, inode, stat))\n        die(\"inode_read_stat(%llu): missing inode\", (unsigned long long) inode);\n}\nbool inode_read_stat_if_exist(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat) {\n    // select stat from stats where inode = ?\n    sqlite3_bind_int64(fs->stmt.inode_read_stat, 1, inode);\n    bool exist = db_exec(fs, fs->stmt.inode_read_stat);\n    if (exist)\n        *stat = *(struct ish_stat *) sqlite3_column_blob(fs->stmt.inode_read_stat, 0);\n    db_reset(fs, fs->stmt.inode_read_stat);\n    return exist;\n}\nvoid inode_write_stat(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat) {\n    // update stats set stat = ? where inode = ?\n    sqlite3_bind_blob(fs->stmt.inode_write_stat, 1, stat, sizeof(*stat), SQLITE_TRANSIENT);\n    sqlite3_bind_int64(fs->stmt.inode_write_stat, 2, inode);\n    db_exec_reset(fs, fs->stmt.inode_write_stat);\n}\n\nvoid path_link(struct fakefs_db *fs, const char *src, const char *dst) {\n    inode_t inode = path_get_inode(fs, src);\n    if (inode == 0)\n        die(\"fakefs link(%s, %s): nonexistent src path\", src, dst);\n    // insert or replace into paths (path, inode) values (?, ?)\n    bind_path(fs->stmt.path_link, 1, dst);\n    sqlite3_bind_int64(fs->stmt.path_link, 2, inode);\n    db_exec_reset(fs, fs->stmt.path_link);\n}\ninode_t path_unlink(struct fakefs_db *fs, const char *path) {\n    inode_t inode = path_get_inode(fs, path);\n    if (inode == 0)\n        die(\"path_unlink(%s): nonexistent path\", path);\n    // delete from paths where path = ?\n    bind_path(fs->stmt.path_unlink, 1, path);\n    db_exec_reset(fs, fs->stmt.path_unlink);\n    return inode;\n}\nvoid path_rename(struct fakefs_db *fs, const char *src, const char *dst) {\n    // update or replace paths set path = change_prefix(path, ? [len(src)], ? [dst])\n    //  where (path >= ? [src plus /] and path < [src plus 0]) or path = ? [src]\n    // arguments:\n    // 1. length of src\n    // 2. dst\n    // 3. src plus /\n    // 4. src plus 0\n    // 5. src\n    size_t src_len = strlen(src);\n    sqlite3_bind_int64(fs->stmt.path_rename, 1, src_len);\n    bind_path(fs->stmt.path_rename, 2, dst);\n    char src_extra[src_len + 1];\n    memcpy(src_extra, src, src_len);\n    src_extra[src_len] = '/';\n    sqlite3_bind_blob(fs->stmt.path_rename, 3, src_extra, src_len + 1, SQLITE_TRANSIENT);\n    src_extra[src_len] = '0';\n    sqlite3_bind_blob(fs->stmt.path_rename, 4, src_extra, src_len + 1, SQLITE_TRANSIENT);\n    sqlite3_bind_blob(fs->stmt.path_rename, 5, src_extra, src_len, SQLITE_TRANSIENT);\n    db_exec_reset(fs, fs->stmt.path_rename);\n}\n\n#if DEBUG_sql\nstatic int trace_callback(unsigned UNUSED(why), void *UNUSED(fuck), void *stmt, void *_sql) {\n    char *sql = _sql;\n    printk(\"%d sql trace: %s %s\\n\", current ? current->pid : -1, sqlite3_expanded_sql(stmt), sql[0] == '-' ? sql : \"\");\n    return 0;\n}\n#endif\n\nstatic void sqlite_func_change_prefix(sqlite3_context *context, int argc, sqlite3_value **args) {\n    assert(argc == 3);\n    const void *in_blob = sqlite3_value_blob(args[0]);\n    size_t in_size = sqlite3_value_bytes(args[0]);\n    size_t start = sqlite3_value_int64(args[1]);\n    const void *replacement = sqlite3_value_blob(args[2]);\n    size_t replacement_size = sqlite3_value_bytes(args[2]);\n    size_t out_size = in_size - start + replacement_size;\n    char *out_blob = sqlite3_malloc(out_size);\n    memcpy(out_blob, replacement, replacement_size);\n    memcpy(out_blob + replacement_size, in_blob + start, in_size - start);\n    sqlite3_result_blob(context, out_blob, out_size, sqlite3_free);\n}\n\nextern int fakefs_rebuild(struct fakefs_db *fs, int root_fd);\nextern int fakefs_migrate(struct fakefs_db *fs, int root_fd);\n\nint fake_db_init(struct fakefs_db *fs, const char *db_path, int root_fd) {\n    int err = sqlite3_open_v2(db_path, &fs->db, SQLITE_OPEN_READWRITE, NULL);\n    if (err != SQLITE_OK) {\n        printk(\"error opening database: %s\\n\", sqlite3_errmsg(fs->db));\n        sqlite3_close(fs->db);\n        return _EINVAL;\n    }\n    sqlite3_busy_timeout(fs->db, 1000);\n    sqlite3_create_function(fs->db, \"change_prefix\", 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sqlite_func_change_prefix, NULL, NULL);\n    db_check_error(fs);\n\n    // let's do WAL mode\n    sqlite3_stmt *statement = db_prepare(fs, \"pragma journal_mode=wal\");\n    db_check_error(fs);\n    sqlite3_step(statement);\n    db_check_error(fs);\n    sqlite3_finalize(statement);\n\n    statement = db_prepare(fs, \"pragma foreign_keys=true\");\n    db_check_error(fs);\n    sqlite3_step(statement);\n    db_check_error(fs);\n    sqlite3_finalize(statement);\n\n#if DEBUG_sql\n    sqlite3_trace_v2(mount->db, SQLITE_TRACE_STMT, trace_callback, NULL);\n#endif\n\n    err = fakefs_migrate(fs, root_fd);\n    if (err < 0)\n        return err;\n\n    // after the filesystem is compressed, transmitted, and uncompressed, the\n    // inode numbers will be different. to detect this, the inode of the\n    // database file is stored inside the database and compared with the actual\n    // database file inode, and if they're different we rebuild the database.\n    struct stat statbuf;\n    if (stat(db_path, &statbuf) < 0) ERRNO_DIE(\"stat database\");\n    ino_t db_inode = statbuf.st_ino;\n    statement = db_prepare(fs, \"select db_inode from meta\");\n    if (sqlite3_step(statement) == SQLITE_ROW) {\n        if ((uint64_t) sqlite3_column_int64(statement, 0) != db_inode) {\n            sqlite3_finalize(statement);\n            statement = NULL;\n            int err = fakefs_rebuild(fs, root_fd);\n            if (err < 0) {\n                return err;\n            }\n        }\n    }\n    if (statement != NULL)\n        sqlite3_finalize(statement);\n\n    // save current inode\n    statement = db_prepare(fs, \"update meta set db_inode = ?\");\n    sqlite3_bind_int64(statement, 1, (int64_t) db_inode);\n    db_check_error(fs);\n    sqlite3_step(statement);\n    db_check_error(fs);\n    sqlite3_finalize(statement);\n\n    // delete orphaned stats\n    statement = db_prepare(fs, \"delete from stats where not exists (select 1 from paths where inode = stats.inode)\");\n    db_check_error(fs);\n    sqlite3_step(statement);\n    db_check_error(fs);\n    sqlite3_finalize(statement);\n\n    fs->lock = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);\n    fs->stmt.begin_deferred = db_prepare(fs, \"begin deferred\");\n    fs->stmt.begin_immediate = db_prepare(fs, \"begin immediate\");\n    fs->stmt.commit = db_prepare(fs, \"commit\");\n    fs->stmt.rollback = db_prepare(fs, \"rollback\");\n    fs->stmt.path_get_inode = db_prepare(fs, \"select inode from paths where path = ?\");\n    fs->stmt.path_read_stat = db_prepare(fs, \"select inode, stat from stats natural join paths where path = ?\");\n    fs->stmt.path_create_stat = db_prepare(fs, \"insert into stats (stat) values (?)\");\n    fs->stmt.path_create_path = db_prepare(fs, \"insert or replace into paths values (?, last_insert_rowid())\");\n    fs->stmt.inode_read_stat = db_prepare(fs, \"select stat from stats where inode = ?\");\n    fs->stmt.inode_write_stat = db_prepare(fs, \"update stats set stat = ? where inode = ?\");\n    fs->stmt.path_link = db_prepare(fs, \"insert or replace into paths (path, inode) values (?, ?)\");\n    fs->stmt.path_unlink = db_prepare(fs, \"delete from paths where path = ?\");\n    fs->stmt.path_rename = db_prepare(fs, \"update or replace paths set path = change_prefix(path, ?, ?) \"\n            \"where (path >= ? and path < ?) or path = ?\");\n    fs->stmt.path_from_inode = db_prepare(fs, \"select path from paths where inode = ?\");\n    fs->stmt.try_cleanup_inode = db_prepare(fs, \"delete from stats where inode = ? and not exists (select 1 from paths where inode = stats.inode)\");\n    return 0;\n}\n\nint fake_db_deinit(struct fakefs_db *fs) {\n    if (fs->db) {\n        sqlite3_finalize(fs->stmt.begin_deferred);\n        sqlite3_finalize(fs->stmt.begin_immediate);\n        sqlite3_finalize(fs->stmt.commit);\n        sqlite3_finalize(fs->stmt.rollback);\n        sqlite3_finalize(fs->stmt.path_get_inode);\n        sqlite3_finalize(fs->stmt.path_read_stat);\n        sqlite3_finalize(fs->stmt.path_create_stat);\n        sqlite3_finalize(fs->stmt.path_create_path);\n        sqlite3_finalize(fs->stmt.inode_read_stat);\n        sqlite3_finalize(fs->stmt.inode_write_stat);\n        sqlite3_finalize(fs->stmt.path_link);\n        sqlite3_finalize(fs->stmt.path_unlink);\n        sqlite3_finalize(fs->stmt.path_rename);\n        sqlite3_finalize(fs->stmt.path_from_inode);\n        sqlite3_finalize(fs->stmt.try_cleanup_inode);\n        return sqlite3_close(fs->db);\n    }\n    return SQLITE_OK;\n}\n"
  },
  {
    "path": "fs/fake-db.h",
    "content": "#ifndef FS_FAKEFS_API_H\n#define FS_FAKEFS_API_H\n\n#include <sqlite3.h>\n#include \"fs/fix_path.h\"\n#include \"misc.h\"\n\nstruct fakefs_db {\n    sqlite3 *db;\n    struct {\n        sqlite3_stmt *begin_deferred;\n        sqlite3_stmt *begin_immediate;\n        sqlite3_stmt *commit;\n        sqlite3_stmt *rollback;\n        sqlite3_stmt *path_get_inode;\n        sqlite3_stmt *path_read_stat;\n        sqlite3_stmt *path_create_stat;\n        sqlite3_stmt *path_create_path;\n        sqlite3_stmt *inode_read_stat;\n        sqlite3_stmt *inode_write_stat;\n        sqlite3_stmt *path_link;\n        sqlite3_stmt *path_unlink;\n        sqlite3_stmt *path_rename;\n        sqlite3_stmt *path_from_inode;\n        sqlite3_stmt *try_cleanup_inode;\n    } stmt;\n    sqlite3_mutex *lock;\n};\n\nint fake_db_init(struct fakefs_db *fs, const char *db_path, int root_fd);\nint fake_db_deinit(struct fakefs_db *fs);\n\nvoid db_begin_read(struct fakefs_db *fs);\nvoid db_begin_write(struct fakefs_db *fs);\nvoid db_commit(struct fakefs_db *fs);\nvoid db_rollback(struct fakefs_db *fs);\n\nbool db_exec(struct fakefs_db *fs, sqlite3_stmt *stmt);\nvoid db_reset(struct fakefs_db *fs, sqlite3_stmt *stmt);\nvoid db_exec_reset(struct fakefs_db *fs, sqlite3_stmt *stmt);\n\nstruct ish_stat {\n    uint32_t mode;\n    uint32_t uid;\n    uint32_t gid;\n    uint32_t rdev;\n};\n\ntypedef uint64_t inode_t;\n\ninode_t path_get_inode(struct fakefs_db *fs, const char *path);\nbool path_read_stat(struct fakefs_db *fs, const char *path, struct ish_stat *stat, uint64_t *inode);\ninode_t path_create(struct fakefs_db *fs, const char *path, struct ish_stat *stat);\n\nbool inode_read_stat_if_exist(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat);\nvoid inode_read_stat_or_die(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat);\nvoid inode_write_stat(struct fakefs_db *fs, inode_t inode, struct ish_stat *stat);\n\nvoid path_link(struct fakefs_db *fs, const char *src, const char *dst);\ninode_t path_unlink(struct fakefs_db *fs, const char *path);\nvoid path_rename(struct fakefs_db *fs, const char *src, const char *dst);\n\n#endif\n"
  },
  {
    "path": "fs/fake-migrate.c",
    "content": "#include \"kernel/fs.h\"\n#include \"debug.h\"\n#include \"kernel/errno.h\"\n#include \"fs/fake-db.h\"\n#include \"fs/sqlutil.h\"\n\n// The value of the user_version pragma is used to decide what needs migrating.\n\n#pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\nstatic struct migration {\n    const char *sql;\n    void (*migrate)(struct fakefs_db *fs);\n} migrations[] = {\n    // version 1: add another index\n    {\n        \"create index inode_to_path on paths (inode, path);\"\n    },\n    // version 2: add foreign key constraint on paths, create trigger to automatically cleanup stats\n    {\n        \"create table paths_new (path blob primary key, inode integer references stats(inode));\"\n        \"insert into paths_new select * from paths where exists (select 1 from stats where inode = paths.inode);\"\n        \"drop table paths; alter table paths_new rename to paths;\"\n        \"create index inode_to_path on paths (inode, path);\"\n        \"delete from stats where not exists (select 1 from paths where inode = stats.inode);\"\n        \"create trigger delete_path after delete on paths \"\n        \"when not exists (select 1 from paths where inode = old.inode) \"\n        \"begin \"\n            \"delete from stats where not exists (select 1 from paths where inode = old.inode) and inode = old.inode; \"\n        \"end;\"\n    },\n    // version 3: the trigger was a mistake\n    {\n        \"drop trigger delete_path\"\n    },\n};\n\nint fakefs_migrate(struct fakefs_db *fs, int UNUSED(root_fd)) {\n    sqlite3 *db = fs->db;\n    int err;\n    sqlite3_stmt *user_version = PREPARE(\"pragma user_version\");\n    STEP(user_version);\n    int version = sqlite3_column_int(user_version, 0);\n    FINALIZE(user_version);\n\n    EXEC(\"begin\");\n    int versions = sizeof(migrations)/sizeof(migrations[0]);\n    while (version < versions) {\n        struct migration m = migrations[version];\n        if (m.sql != NULL)\n            EXEC(m.sql);\n        if (m.migrate != NULL)\n            m.migrate(fs);\n        version++;\n    }\n    // for some reason placeholders aren't allowed in pragmas\n    char *pragma_user_version = sqlite3_mprintf(\"pragma user_version = %d\", version);\n    EXEC(pragma_user_version);\n    sqlite3_free(pragma_user_version);\n    EXEC(\"commit\");\n\n    return 0;\n}\n"
  },
  {
    "path": "fs/fake-rebuild.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include \"fs/sqlutil.h\"\n#include \"fs/fake-db.h\"\n#include \"kernel/errno.h\"\n#include \"util/list.h\"\n#include \"debug.h\"\n\n// rebuild process in pseudocode:\n//\n// table = {}\n// for each path, inode:\n//     real_inode = stat(path).st_ino\n//     if inode in table:\n//         unlink(path)\n//         link(table[inode], path)\n//     else:\n//         table[inode] = path\n//     stat = db['stat ' + inode]\n//     new_db['inode ' + path] = real_inode\n//     new_db['stat ' + real_inode] = stat\n\n// ad hoc hashtable\nstruct entry {\n    ino_t inode;\n    char *path;\n    struct list chain;\n};\n\nint fakefs_rebuild(struct fakefs_db *fs, int root_fd) {\n    sqlite3 *db = fs->db;\n    int err;\n\n    EXEC(\"begin\");\n    EXEC(\"create table paths_old (path blob primary key, inode integer)\");\n    EXEC(\"create table stats_old (inode integer primary key, stat blob)\");\n    EXEC(\"insert into paths_old select * from paths\");\n    EXEC(\"insert into stats_old select * from stats\");\n    EXEC(\"delete from paths\");\n    EXEC(\"delete from stats\");\n    sqlite3_stmt *get_paths = PREPARE(\"select path, inode from paths_old\");\n    sqlite3_stmt *read_stat = PREPARE(\"select stat from stats_old where inode = ?\");\n    sqlite3_stmt *write_path = PREPARE(\"insert into paths (path, inode) values (?, ?)\");\n    sqlite3_stmt *write_stat = PREPARE(\"replace into stats (inode, stat) values (?, ?)\");\n\n    struct list hashtable[2000];\n#define HASH_SIZE (sizeof(hashtable)/sizeof(hashtable[0]))\n    for (unsigned i = 0; i < HASH_SIZE; i++)\n        list_init(&hashtable[i]);\n\n    while (STEP(get_paths)) {\n        const char *path = (const char *) sqlite3_column_text(get_paths, 0);\n        ino_t inode = sqlite3_column_int64(get_paths, 1);\n\n        // grab real inode\n        struct stat stat;\n        int err = fstatat(root_fd, fix_path(path), &stat, 0);\n        if (err < 0)\n            continue;\n        ino_t real_inode = stat.st_ino;\n\n        // restore hardlinks\n        struct list *bucket = &hashtable[inode % HASH_SIZE];\n        struct entry *entry;\n        bool found = false;\n        list_for_each_entry(bucket, entry, chain) {\n            if (entry->inode == inode) {\n                unlinkat(root_fd, fix_path(path), 0);\n                linkat(root_fd, fix_path(entry->path), root_fd, fix_path(path), 0);\n                found = true;\n                break;\n            }\n        }\n        if (!found) {\n            entry = malloc(sizeof(struct entry));\n            entry->inode = inode;\n            entry->path = strdup(path);\n            list_add(bucket, &entry->chain);\n        }\n\n        // extract the stat so we can copy it\n        err = sqlite3_bind_int64(read_stat, 1, inode); CHECK_ERR();\n        if (STEP(read_stat) == false) {\n            RESET(read_stat);\n            continue;\n        }\n        const void *stat_data = sqlite3_column_blob(read_stat, 0);\n        size_t stat_data_size = sqlite3_column_bytes(read_stat, 0);\n\n        // store all the information in the new database\n        err = sqlite3_bind_int64(write_stat, 1, real_inode); CHECK_ERR();\n        err = sqlite3_bind_blob(write_stat, 2, stat_data, stat_data_size, SQLITE_TRANSIENT); CHECK_ERR();\n        STEP(write_stat);\n        RESET(write_stat);\n        err = sqlite3_bind_blob(write_path, 1, path, strlen(path), SQLITE_TRANSIENT); CHECK_ERR();\n        err = sqlite3_bind_int64(write_path, 2, real_inode); CHECK_ERR();\n        STEP(write_path);\n        RESET(write_path);\n\n        RESET(read_stat);\n    }\n\n    for (unsigned i = 0; i < HASH_SIZE; i++) {\n        struct entry *entry, *tmp;\n        list_for_each_entry_safe(&hashtable[i], entry, tmp, chain) {\n            list_remove(&entry->chain);\n            free(entry->path);\n            free(entry);\n        }\n    }\n\n    EXEC(\"drop table paths_old\");\n    EXEC(\"drop table stats_old\");\n    EXEC(\"commit\");\n    FINALIZE(get_paths);\n    FINALIZE(read_stat);\n    FINALIZE(write_path);\n    FINALIZE(write_stat);\n    return 0;\n}\n"
  },
  {
    "path": "fs/fake.c",
    "content": "#include <stdarg.h>\n#include <limits.h>\n#include <string.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/file.h>\n#include <sqlite3.h>\n\n#include \"debug.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/task.h\"\n#include \"fs/fd.h\"\n#include \"fs/dev.h\"\n#include \"fs/inode.h\"\n#include \"fs/real.h\"\n#define ISH_INTERNAL\n#include \"fs/fake.h\"\n\n// TODO document database\n\n// this exists only to override readdir to fix the returned inode numbers\nstatic struct fd_ops fakefs_fdops;\n\nstatic struct fd *fakefs_open(struct mount *mount, const char *path, int flags, int mode) {\n    struct fakefs_db *fs = &mount->fakefs;\n    struct fd *fd = realfs.open(mount, path, flags, 0666);\n    if (IS_ERR(fd))\n        return fd;\n    db_begin_write(fs);\n    fd->fake_inode = path_get_inode(fs, path);\n    if (flags & O_CREAT_) {\n        struct ish_stat ishstat;\n        ishstat.mode = mode | S_IFREG;\n        ishstat.uid = current->euid;\n        ishstat.gid = current->egid;\n        ishstat.rdev = 0;\n        if (fd->fake_inode == 0) {\n            path_create(fs, path, &ishstat);\n            fd->fake_inode = path_get_inode(fs, path);\n        }\n    }\n    db_commit(fs);\n    if (fd->fake_inode == 0) {\n        // metadata for this file is missing\n        // TODO unlink the real file\n        fd_close(fd);\n        return ERR_PTR(_ENOENT);\n    }\n    fd->ops = &fakefs_fdops;\n    return fd;\n}\n\n// WARNING: giant hack, just for file providerws\nstruct fd *fakefs_open_inode(struct mount *mount, ino_t inode) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_read(fs);\n    sqlite3_stmt *stmt = fs->stmt.path_from_inode;\n    sqlite3_bind_int64(stmt, 1, inode);\nstep:\n    if (!db_exec(fs, stmt)) {\n        db_reset(fs, stmt);\n        db_rollback(fs);\n        return ERR_PTR(_ENOENT);\n    }\n    const char *path = (const char *) sqlite3_column_text(stmt, 0);\n    struct fd *fd = realfs.open(mount, path, O_RDWR_, 0);\n    if (PTR_ERR(fd) == _EISDIR)\n        fd = realfs.open(mount, path, O_RDONLY_, 0);\n    if (PTR_ERR(fd) == _ENOENT)\n        goto step;\n    db_reset(fs, stmt);\n    db_commit(fs);\n    fd->fake_inode = inode;\n    fd->ops = &fakefs_fdops;\n    return fd;\n}\n\nstatic int fakefs_link(struct mount *mount, const char *src, const char *dst) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    int err = realfs.link(mount, src, dst);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    path_link(fs, src, dst);\n    db_commit(fs);\n    return 0;\n}\n\nstatic int fakefs_unlink(struct mount *mount, const char *path) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    int err = realfs.unlink(mount, path);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    ino_t ino = path_unlink(fs, path);\n    db_commit(fs);\n    inode_check_orphaned(mount, ino);\n    return 0;\n}\n\nstatic int fakefs_rmdir(struct mount *mount, const char *path) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    int err = realfs.rmdir(mount, path);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    ino_t ino = path_unlink(fs, path);\n    db_commit(fs);\n    inode_check_orphaned(mount, ino);\n    return 0;\n}\n\nstatic int fakefs_rename(struct mount *mount, const char *src, const char *dst) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    path_rename(fs, src, dst);\n    int err = realfs.rename(mount, src, dst);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    db_commit(fs);\n    return 0;\n}\n\nstatic int fakefs_symlink(struct mount *mount, const char *target, const char *link) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    // create a file containing the target\n    int fd = openat(mount->root_fd, fix_path(link), O_WRONLY | O_CREAT | O_EXCL, 0666);\n    if (fd < 0) {\n        db_rollback(fs);\n        return errno_map();\n    }\n    ssize_t res = write(fd, target, strlen(target));\n    close(fd);\n    if (res < 0) {\n        int saved_errno = errno;\n        unlinkat(mount->root_fd, fix_path(link), 0);\n        db_rollback(fs);\n        errno = saved_errno;\n        return errno_map();\n    }\n\n    // customize the stat info so it looks like a link\n    struct ish_stat ishstat;\n    ishstat.mode = S_IFLNK | 0777; // symlinks always have full permissions\n    ishstat.uid = current->euid;\n    ishstat.gid = current->egid;\n    ishstat.rdev = 0;\n    path_create(fs, link, &ishstat);\n    db_commit(fs);\n    return 0;\n}\n\nstatic int fakefs_mknod(struct mount *mount, const char *path, mode_t_ mode, dev_t_ dev) {\n    struct fakefs_db *fs = &mount->fakefs;\n    mode_t_ real_mode = 0666;\n    if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode))\n        real_mode |= S_IFREG;\n    else\n        real_mode |= mode & S_IFMT;\n    db_begin_write(fs);\n    int err = realfs.mknod(mount, path, real_mode, 0);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    struct ish_stat stat;\n    stat.mode = mode;\n    stat.uid = current->euid;\n    stat.gid = current->egid;\n    stat.rdev = 0;\n    if (S_ISBLK(mode) || S_ISCHR(mode))\n        stat.rdev = dev;\n    path_create(fs, path, &stat);\n    db_commit(fs);\n    return err;\n}\n\nstatic int fakefs_stat(struct mount *mount, const char *path, struct statbuf *fake_stat) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_read(fs);\n    struct ish_stat ishstat;\n    ino_t inode;\n    if (!path_read_stat(fs, path, &ishstat, &inode)) {\n        db_rollback(fs);\n        return _ENOENT;\n    }\n    int err = realfs.stat(mount, path, fake_stat);\n    db_commit(fs);\n    if (err < 0)\n        return err;\n    fake_stat->inode = inode;\n    fake_stat->mode = ishstat.mode;\n    fake_stat->uid = ishstat.uid;\n    fake_stat->gid = ishstat.gid;\n    fake_stat->rdev = ishstat.rdev;\n    return 0;\n}\n\nstatic int fakefs_fstat(struct fd *fd, struct statbuf *fake_stat) {\n    struct fakefs_db *fs = &fd->mount->fakefs;\n    int err = realfs.fstat(fd, fake_stat);\n    if (err < 0)\n        return err;\n    db_begin_read(fs);\n    struct ish_stat ishstat;\n    if (!inode_read_stat_if_exist(fs, fd->fake_inode, &ishstat)) {\n        db_rollback(fs);\n        return _ENOENT;\n    }\n    db_commit(fs);\n    fake_stat->inode = fd->fake_inode;\n    fake_stat->mode = ishstat.mode;\n    fake_stat->uid = ishstat.uid;\n    fake_stat->gid = ishstat.gid;\n    fake_stat->rdev = ishstat.rdev;\n    return 0;\n}\n\nstatic void fake_stat_setattr(struct ish_stat *ishstat, struct attr attr) {\n    switch (attr.type) {\n        case attr_uid:\n            ishstat->uid = attr.uid;\n            break;\n        case attr_gid:\n            ishstat->gid = attr.gid;\n            break;\n        case attr_mode:\n            ishstat->mode = (ishstat->mode & S_IFMT) | (attr.mode & ~S_IFMT);\n            break;\n        case attr_size:\n            die(\"attr_size should be handled by realfs\");\n    }\n}\n\nstatic int fakefs_setattr(struct mount *mount, const char *path, struct attr attr) {\n    struct fakefs_db *fs = &mount->fakefs;\n    if (attr.type == attr_size)\n        return realfs.setattr(mount, path, attr);\n    db_begin_read(fs);\n    struct ish_stat ishstat;\n    ino_t inode;\n    if (!path_read_stat(fs, path, &ishstat, &inode)) {\n        db_rollback(fs);\n        return _ENOENT;\n    }\n    fake_stat_setattr(&ishstat, attr);\n    inode_write_stat(fs, inode, &ishstat);\n    db_commit(fs);\n    return 0;\n}\n\nstatic int fakefs_fsetattr(struct fd *fd, struct attr attr) {\n    struct fakefs_db *fs = &fd->mount->fakefs;\n    if (attr.type == attr_size)\n        return realfs.fsetattr(fd, attr);\n    db_begin_write(fs);\n    struct ish_stat ishstat;\n    inode_read_stat_or_die(fs, fd->fake_inode, &ishstat);\n    fake_stat_setattr(&ishstat, attr);\n    inode_write_stat(fs, fd->fake_inode, &ishstat);\n    db_commit(fs);\n    return 0;\n}\n\nstatic int fakefs_mkdir(struct mount *mount, const char *path, mode_t_ mode) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    int err = realfs.mkdir(mount, path, 0777);\n    if (err < 0) {\n        db_rollback(fs);\n        return err;\n    }\n    struct ish_stat ishstat;\n    ishstat.mode = mode | S_IFDIR;\n    ishstat.uid = current->euid;\n    ishstat.gid = current->egid;\n    ishstat.rdev = 0;\n    path_create(fs, path, &ishstat);\n    db_commit(fs);\n    return 0;\n}\n\nstatic ssize_t file_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize) {\n    // broken symlinks can't be included in an iOS app or else Xcode craps out\n    int fd = openat(mount->root_fd, fix_path(path), O_RDONLY);\n    if (fd < 0)\n        return errno_map();\n    int err = read(fd, buf, bufsize);\n    close(fd);\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nstatic ssize_t fakefs_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_read(fs);\n    struct ish_stat ishstat;\n    if (!path_read_stat(fs, path, &ishstat, NULL)) {\n        db_rollback(fs);\n        return _ENOENT;\n    }\n    if (!S_ISLNK(ishstat.mode)) {\n        db_rollback(fs);\n        return _EINVAL;\n    }\n\n    ssize_t err = realfs.readlink(mount, path, buf, bufsize);\n    if (err == _EINVAL)\n        err = file_readlink(mount, path, buf, bufsize);\n    db_commit(fs);\n    return err;\n}\n\nstatic int fakefs_readdir(struct fd *fd, struct dir_entry *entry) {\n    assert(fd->ops == &fakefs_fdops);\n    int res;\nretry:\n    res = realfs_fdops.readdir(fd, entry);\n    if (res <= 0)\n        return res;\n\n    // this is annoying\n    char entry_path[MAX_PATH + 1];\n    realfs_getpath(fd, entry_path);\n    if (strcmp(entry->name, \"..\") == 0) {\n        if (strcmp(entry_path, \"\") != 0) {\n            *strrchr(entry_path, '/') = '\\0';\n        }\n    } else if (strcmp(entry->name, \".\") != 0) {\n        // god I don't know what to do if this would overflow\n        strcat(entry_path, \"/\");\n        strcat(entry_path, entry->name);\n    }\n\n    struct fakefs_db *fs = &fd->mount->fakefs;\n    db_begin_read(fs);\n    entry->inode = path_get_inode(fs, entry_path);\n    db_commit(fs);\n    // it's quite possible that due to some mishap there's no metadata for this file\n    // so just skip this entry, instead of crashing the program, so there's hope for recovery\n    if (entry->inode == 0)\n        goto retry;\n    return res;\n}\n\nstatic struct fd_ops fakefs_fdops;\nstatic void __attribute__((constructor)) init_fake_fdops() {\n    fakefs_fdops = realfs_fdops;\n    fakefs_fdops.readdir = fakefs_readdir;\n}\n\nstatic int fakefs_mount(struct mount *mount) {\n    char db_path[PATH_MAX];\n    strcpy(db_path, mount->source);\n    char *basename = strrchr(db_path, '/') + 1;\n    assert(strcmp(basename, \"data\") == 0);\n    strcpy(basename, \"meta.db\");\n\n    // do this now so rebuilding can use root_fd\n    int err = realfs.mount(mount);\n    if (err < 0)\n        return err;\n\n    err = fake_db_init(&mount->fakefs, db_path, mount->root_fd);\n    if (err < 0)\n        return err;\n\n    return 0;\n}\n\nstatic int fakefs_umount(struct mount *mount) {\n    int err = fake_db_deinit(&mount->fakefs);\n    if (err != SQLITE_OK) {\n        printk(\"sqlite failed to close: %d\\n\", err);\n    }\n    /* return realfs.umount(mount); */\n    return 0;\n}\n\nstatic void fakefs_inode_orphaned(struct mount *mount, ino_t inode) {\n    struct fakefs_db *fs = &mount->fakefs;\n    db_begin_write(fs);\n    sqlite3_bind_int64(fs->stmt.try_cleanup_inode, 1, inode);\n    db_exec_reset(fs, fs->stmt.try_cleanup_inode);\n    db_commit(fs);\n}\n\nconst struct fs_ops fakefs = {\n    .name = \"fake\", .magic = 0x66616b65,\n    .mount = fakefs_mount,\n    .umount = fakefs_umount,\n    .statfs = realfs_statfs,\n    .open = fakefs_open,\n    .readlink = fakefs_readlink,\n    .link = fakefs_link,\n    .unlink = fakefs_unlink,\n    .rename = fakefs_rename,\n    .symlink = fakefs_symlink,\n    .mknod = fakefs_mknod,\n\n    .close = realfs_close,\n    .stat = fakefs_stat,\n    .fstat = fakefs_fstat,\n    .flock = realfs_flock,\n    .setattr = fakefs_setattr,\n    .fsetattr = fakefs_fsetattr,\n    .getpath = realfs_getpath,\n    .utime = realfs_utime,\n\n    .mkdir = fakefs_mkdir,\n    .rmdir = fakefs_rmdir,\n\n    .inode_orphaned = fakefs_inode_orphaned,\n};\n"
  },
  {
    "path": "fs/fake.h",
    "content": "#ifndef ISH_INTERNAL\n#error \"for internal use only\"\n#endif\n\n#ifndef FS_FAKE_H\n#define FS_FAKE_H\n\n#include \"kernel/fs.h\"\n#include \"fs/fake-db.h\"\n#include \"misc.h\"\n\nstruct fd *fakefs_open_inode(struct mount *mount, ino_t inode);\n\n#endif\n"
  },
  {
    "path": "fs/fd.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"debug.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/resource.h\"\n#include \"kernel/fs.h\"\n#include \"fs/poll.h\"\n#include \"fs/fd.h\"\n#include \"fs/inode.h\"\n\nstruct fd *fd_create(const struct fd_ops *ops) {\n    struct fd *fd = malloc(sizeof(struct fd));\n    if (fd == NULL)\n        return NULL;\n    *fd = (struct fd) {};\n    fd->ops = ops;\n    fd->refcount = 1;\n    fd->flags = 0;\n    fd->mount = NULL;\n    fd->offset = 0;\n    list_init(&fd->poll_fds);\n    lock_init(&fd->poll_lock);\n    lock_init(&fd->lock);\n    cond_init(&fd->cond);\n    return fd;\n}\n\nstruct fd *fd_retain(struct fd *fd) {\n    fd->refcount++;\n    return fd;\n}\n\nint fd_close(struct fd *fd) {\n    int err = 0;\n    if (--fd->refcount == 0) {\n        poll_cleanup_fd(fd);\n        if (fd->ops->close)\n            err = fd->ops->close(fd);\n        // see comment in close in kernel/fs.h\n        if (fd->mount && fd->mount->fs->close && fd->mount->fs->close != fd->ops->close) {\n            int new_err = fd->mount->fs->close(fd);\n            if (new_err < 0)\n                err = new_err;\n        }\n\n        if (fd->inode)\n            inode_release(fd->inode);\n        if (fd->mount)\n            mount_release(fd->mount);\n        free(fd);\n    }\n    return err;\n}\n\nstatic int fdtable_resize(struct fdtable *table, unsigned size);\n\nstruct fdtable *fdtable_new(int size) {\n    struct fdtable *fdt = malloc(sizeof(struct fdtable));\n    if (fdt == NULL)\n        return ERR_PTR(_ENOMEM);\n    fdt->refcount = 1;\n    fdt->size = 0;\n    fdt->files = NULL;\n    fdt->cloexec = NULL;\n    lock_init(&fdt->lock);\n    int err = fdtable_resize(fdt, size);\n    if (err < 0) {\n        free(fdt);\n        return ERR_PTR(err);\n    }\n    return fdt;\n}\n\nstatic int fdtable_close(struct fdtable *table, fd_t f);\n\n// FIXME this looks like it has the classic refcount UAF\nvoid fdtable_release(struct fdtable *table) {\n    lock(&table->lock);\n    if (--table->refcount == 0) {\n        for (fd_t f = 0; (unsigned) f < table->size; f++)\n            fdtable_close(table, f);\n        free(table->files);\n        free(table->cloexec);\n        unlock(&table->lock);\n        free(table);\n    } else {\n        unlock(&table->lock);\n    }\n}\n\nstatic int fdtable_resize(struct fdtable *table, unsigned size) {\n    // currently the only legitimate use of this is to expand the table\n    assert(size > table->size);\n\n    struct fd **files = malloc(sizeof(struct fd *) * size);\n    if (files == NULL)\n        return _ENOMEM;\n    memset(files, 0, sizeof(struct fd *) * size);\n    if (table->files)\n        memcpy(files, table->files, sizeof(struct fd *) * table->size);\n\n    bits_t *cloexec = malloc(BITS_SIZE(size));\n    if (cloexec == NULL) {\n        free(files);\n        return _ENOMEM;\n    }\n    memset(cloexec, 0, BITS_SIZE(size));\n    if (table->cloexec)\n        memcpy(cloexec, table->cloexec, BITS_SIZE(table->size));\n\n    free(table->files);\n    table->files = files;\n    free(table->cloexec);\n    table->cloexec = cloexec;\n    table->size = size;\n    return 0;\n}\n\nstruct fdtable *fdtable_copy(struct fdtable *table) {\n    lock(&table->lock);\n    int size = table->size;\n    struct fdtable *new_table = fdtable_new(size);\n    if (IS_ERR(new_table)) {\n        unlock(&table->lock);\n        return new_table;\n    }\n    memcpy(new_table->files, table->files, sizeof(struct fd *) * size);\n    for (fd_t f = 0; f < size; f++)\n        if (new_table->files[f])\n            new_table->files[f]->refcount++;\n    memcpy(new_table->cloexec, table->cloexec, BITS_SIZE(size));\n    unlock(&table->lock);\n    return new_table;\n}\n\nstatic int fdtable_expand(struct fdtable *table, fd_t max) {\n    unsigned size = max + 1;\n    if (size > rlimit(RLIMIT_NOFILE_))\n        return _EMFILE;\n    if (table->size >= size)\n        return 0;\n    return fdtable_resize(table, max + 1);\n}\n\nstruct fd *fdtable_get(struct fdtable *table, fd_t f) {\n    if (f < 0 || (unsigned) f >= current->files->size)\n        return NULL;\n    return table->files[f];\n}\n\nstruct fd *f_get(fd_t f) {\n    lock(&current->files->lock);\n    struct fd *fd = fdtable_get(current->files, f);\n    unlock(&current->files->lock);\n    return fd;\n}\n\nstatic fd_t f_install_start(struct fd *fd, fd_t start) {\n    assert(start >= 0);\n    struct fdtable *table = current->files;\n    unsigned size = rlimit(RLIMIT_NOFILE_);\n    if (size > table->size)\n        size = table->size;\n\n    fd_t f;\n    for (f = start; (unsigned) f < size; f++)\n        if (table->files[f] == NULL)\n            break;\n    if ((unsigned) f >= size) {\n        int err = fdtable_expand(table, f);\n        if (err < 0)\n            f = err;\n    }\n\n    if (f >= 0) {\n        table->files[f] = fd;\n        bit_clear(f, table->cloexec);\n    } else {\n        fd_close(fd);\n    }\n    return f;\n}\n\nfd_t f_install(struct fd *fd, int flags) {\n    lock(&current->files->lock);\n    fd_t f = f_install_start(fd, 0);\n    if (f >= 0) {\n        if (flags & O_CLOEXEC_)\n            bit_set(f, current->files->cloexec);\n        if (flags & O_NONBLOCK_)\n            fd_setflags(fd, O_NONBLOCK_);\n    }\n    unlock(&current->files->lock);\n    return f;\n}\n\nstatic int fdtable_close(struct fdtable *table, fd_t f) {\n    struct fd *fd = fdtable_get(table, f);\n    if (fd == NULL)\n        return _EBADF;\n    if (fd->inode != NULL) // temporary hack for files like sockets that right now don't have inodes but will eventually\n        file_lock_remove_owned_by(fd, table);\n    int err = fd_close(fd);\n    table->files[f] = NULL;\n    bit_clear(f, table->cloexec);\n    return err;\n}\n\nint f_close(fd_t f) {\n    lock(&current->files->lock);\n    int err = fdtable_close(current->files, f);\n    unlock(&current->files->lock);\n    return err;\n}\n\ndword_t sys_close(fd_t f) {\n    STRACE(\"close(%d)\", f);\n    return f_close(f);\n}\n\nvoid fdtable_do_cloexec(struct fdtable *table) {\n    lock(&table->lock);\n    for (fd_t f = 0; (unsigned) f < table->size; f++)\n        if (bit_test(f, table->cloexec))\n            fdtable_close(table, f);\n    unlock(&table->lock);\n}\n\n#define F_DUPFD_ 0\n#define F_GETFD_ 1\n#define F_SETFD_ 2\n#define F_GETFL_ 3\n#define F_SETFL_ 4\n\n#define F_GETLK_ 5\n#define F_SETLK_ 6\n#define F_SETLKW_ 7\n#define F_GETLK64_ 12\n#define F_SETLK64_ 13\n#define F_SETLKW64_ 14\n\n#define F_DUPFD_CLOEXEC_ 1030\n\ndword_t sys_dup(fd_t f) {\n    STRACE(\"dup(%d)\", f);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    fd->refcount++;\n    return f_install(fd, 0);\n}\n\ndword_t sys_dup3(fd_t f, fd_t new_f, int_t flags) {\n    STRACE(\"dup3(%d, %d, %d)\", f, new_f, flags);\n    struct fdtable *table = current->files;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    int err = fdtable_expand(table, new_f);\n    if (err < 0)\n        return err;\n    fd_retain(fd);\n    f_close(new_f);\n    table->files[new_f] = fd;\n    if (flags & O_CLOEXEC_)\n        bit_set(new_f, table->cloexec);\n    return new_f;\n}\n\ndword_t sys_dup2(fd_t f, fd_t new_f) {\n    return sys_dup3(f, new_f, 0);\n}\n\nint fd_getflags(struct fd *fd) {\n    if (fd->ops->getflags)\n        return fd->ops->getflags(fd);\n    return fd->flags;\n}\n\n#define FD_ALLOWED_FLAGS (O_APPEND_ | O_NONBLOCK_)\nint fd_setflags(struct fd *fd, int flags) {\n    if (fd->ops->setflags)\n        return fd->ops->setflags(fd, flags);\n    fd->flags = (fd->flags & ~FD_ALLOWED_FLAGS) | (flags & FD_ALLOWED_FLAGS);\n    return 0;\n}\n\ndword_t sys_fcntl(fd_t f, dword_t cmd, dword_t arg) {\n    struct fdtable *table = current->files;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    struct flock32_ flock32;\n    struct flock_ flock;\n    fd_t new_f;\n    int err;\n    switch (cmd) {\n        case F_DUPFD_:\n            STRACE(\"fcntl(%d, F_DUPFD, %d)\", f, arg);\n            fd->refcount++;\n            return f_install_start(fd, arg);\n\n        case F_DUPFD_CLOEXEC_:\n            STRACE(\"fcntl(%d, F_DUPFD_CLOEXEC, %d)\", f, arg);\n            fd->refcount++;\n            new_f = f_install_start(fd, arg);\n            bit_set(new_f, table->cloexec);\n            return new_f;\n\n        case F_GETFD_:\n            STRACE(\"fcntl(%d, F_GETFD)\", f);\n            return bit_test(f, table->cloexec);\n        case F_SETFD_:\n            STRACE(\"fcntl(%d, F_SETFD, 0x%x)\", f, arg);\n            if (arg & 1)\n                bit_set(f, table->cloexec);\n            else\n                bit_clear(f, table->cloexec);\n            return 0;\n\n        case F_GETFL_:\n            STRACE(\"fcntl(%d, F_GETFL)\", f);\n            return fd_getflags(fd);\n        case F_SETFL_:\n            STRACE(\"fcntl(%d, F_SETFL, %#x)\", f, arg);\n            return fd_setflags(fd, arg);\n\n        case F_GETLK_:\n            STRACE(\"fcntl(%d, F_GETLK, %#x)\", f, arg);\n            if (user_read(arg, &flock32, sizeof(flock32)))\n                return _EFAULT;\n            flock.type = flock32.type;\n            flock.whence = flock32.whence;\n            flock.start = flock32.start;\n            flock.len = flock32.len;\n            flock.pid = flock32.pid;\n            err = fcntl_getlk(fd, &flock);\n            if (err >= 0) {\n                flock32.type = flock.type;\n                flock32.whence = flock.whence;\n                flock32.start = flock.start;\n                flock32.len = flock.len;\n                flock32.pid = flock.pid;\n                if (user_write(arg, &flock32, sizeof(flock32)))\n                    return _EFAULT;\n            }\n            return err;\n\n        case F_GETLK64_:\n            STRACE(\"fcntl(%d, F_GETLK64, %#x)\", f, arg);\n            if (user_read(arg, &flock, sizeof(flock)))\n                return _EFAULT;\n            err = fcntl_getlk(fd, &flock);\n            if (err >= 0)\n                if (user_write(arg, &flock, sizeof(flock)))\n                    return _EFAULT;\n            return err;\n\n        case F_SETLK_:\n        case F_SETLKW_:\n            STRACE(\"fcntl(%d, F_SETLK%*s, %#x)\", f, cmd == F_SETLKW_, \"W\", arg);\n            if (user_read(arg, &flock32, sizeof(flock32)))\n                return _EFAULT;\n            flock.type = flock32.type;\n            flock.whence = flock32.whence;\n            flock.start = flock32.start;\n            flock.len = flock32.len;\n            flock.pid = flock32.pid;\n            return fcntl_setlk(fd, &flock, cmd == F_SETLKW64_);\n\n        case F_SETLK64_:\n        case F_SETLKW64_:\n            STRACE(\"fcntl(%d, F_SETLK%*s64, %#x)\", f, cmd == F_SETLKW_, \"W\", arg);\n            if (user_read(arg, &flock, sizeof(flock)))\n                return _EFAULT;\n            return fcntl_setlk(fd, &flock, cmd == F_SETLKW_);\n\n        default:\n            STRACE(\"fcntl(%d, %d)\", f, cmd);\n            return _EINVAL;\n    }\n}\n\ndword_t sys_fcntl32(fd_t fd, dword_t cmd, dword_t arg) {\n    switch (cmd) {\n        case F_GETLK64_:\n        case F_SETLK64_:\n        case F_SETLKW64_:\n            return _EINVAL;\n    }\n    return sys_fcntl(fd, cmd, arg);\n}\n"
  },
  {
    "path": "fs/fd.h",
    "content": "#ifndef FD_H\n#define FD_H\n#include <dirent.h>\n#include \"kernel/memory.h\"\n#include \"util/list.h\"\n#include \"util/sync.h\"\n#include \"util/bits.h\"\n#include \"fs/stat.h\"\n#include \"fs/proc.h\"\n#include \"fs/sockrestart.h\"\n\n// FIXME almost everything that uses the structs in this file does so without any kind of sane locking\n\nstruct fd {\n    atomic_uint refcount;\n    unsigned flags;\n    mode_t_ type; // just the S_IFMT part, it can't change\n    const struct fd_ops *ops;\n    struct list poll_fds;\n    lock_t poll_lock;\n    unsigned long offset;\n\n    // fd data\n    union {\n        // tty\n        struct {\n            struct tty *tty;\n            // links together fds pointing to the same tty\n            // locked by the tty\n            struct list tty_other_fds;\n        };\n        struct {\n            struct poll *poll;\n        } epollfd;\n        struct {\n            uint64_t val;\n        } eventfd;\n        struct {\n            struct timer *timer;\n            uint64_t expirations;\n        } timerfd;\n        struct {\n            int domain;\n            int type;\n            int protocol;\n\n            // These are only used as strong references, to keep the inode\n            // alive while there is a listener.\n            struct inode_data *unix_name_inode;\n            struct unix_abstract *unix_name_abstract;\n            uint8_t unix_name_len;\n            char unix_name[108];\n            struct fd *unix_peer; // locked by peer_lock, for simplicity\n            cond_t unix_got_peer;\n            // Queue of struct scm for sending file descriptors\n            // locked by fd->lock\n            struct list unix_scm;\n            struct ucred_ {\n                pid_t_ pid;\n                uid_t_ uid;\n                uid_t_ gid;\n            } unix_cred;\n        } socket;\n\n        // See app/Pasteboard.m\n        struct {\n            // UIPasteboard.changeCount\n            uint64_t generation;\n            // Buffer for written data\n            void* buffer;\n            // its capacity\n            size_t buffer_cap;\n            // length of actual data stored in the buffer\n            size_t buffer_len;\n        } clipboard;\n\n        // can fit anything in here\n        void *data;\n    };\n    // fs data\n    union {\n        struct {\n            struct proc_entry entry;\n            unsigned dir_index;\n            struct proc_data data;\n        } proc;\n        struct {\n            int num;\n        } devpts;\n        struct {\n            struct tmp_dirent *dirent;\n            struct tmp_dirent *dir_pos;\n        } tmpfs;\n        void *fs_data;\n    };\n\n    // fs/inode data\n    struct mount *mount;\n    int real_fd; // seeks on this fd require the lock TODO think about making a special lock just for that\n    DIR *dir;\n    struct inode_data *inode;\n    ino_t fake_inode;\n    struct statbuf stat; // for adhoc fs\n    struct fd_sockrestart sockrestart; // argh\n\n    // these are used for a variety of things related to the fd\n    lock_t lock;\n    cond_t cond;\n};\n\ntypedef sdword_t fd_t;\n#define AT_FDCWD_ -100\n\nstruct fd *fd_create(const struct fd_ops *ops);\nstruct fd *fd_retain(struct fd *fd);\nint fd_close(struct fd *fd);\n\nint fd_getflags(struct fd *fd);\nint fd_setflags(struct fd *fd, int flags);\n\n#define NAME_MAX 255\nstruct dir_entry {\n    qword_t inode;\n    char name[NAME_MAX + 1];\n};\n\n#define LSEEK_SET 0\n#define LSEEK_CUR 1\n#define LSEEK_END 2\n\nstruct fd_ops {\n    // required for files\n    // TODO make optional for non-files\n    ssize_t (*read)(struct fd *fd, void *buf, size_t bufsize);\n    ssize_t (*write)(struct fd *fd, const void *buf, size_t bufsize);\n    ssize_t (*pread)(struct fd *fd, void *buf, size_t bufsize, off_t off);\n    ssize_t (*pwrite)(struct fd *fd, const void *buf, size_t bufsize, off_t off);\n    off_t_ (*lseek)(struct fd *fd, off_t_ off, int whence);\n\n    // Reads a directory entry from the stream\n    // required for directories\n    int (*readdir)(struct fd *fd, struct dir_entry *entry);\n    // Return an opaque value representing the current point in the directory stream\n    // optional, fd->offset will be used instead\n    unsigned long (*telldir)(struct fd *fd);\n    // Seek to the location represented by a pointer returned from telldir\n    // optional, fd->offset will be used instead\n    void (*seekdir)(struct fd *fd, unsigned long ptr);\n\n    // map the file\n    int (*mmap)(struct fd *fd, struct mem *mem, page_t start, pages_t pages, off_t offset, int prot, int flags);\n\n    // returns a bitmask of operations that won't block\n    int (*poll)(struct fd *fd);\n\n    // returns the size needed for the output of ioctl, 0 if the arg is not a\n    // pointer, -1 for invalid command\n    ssize_t (*ioctl_size)(int cmd);\n    // if ioctl_size returns non-zero, arg must point to ioctl_size valid bytes\n    int (*ioctl)(struct fd *fd, int cmd, void *arg);\n\n    int (*fsync)(struct fd *fd);\n    int (*close)(struct fd *fd);\n\n    // handle F_GETFL, i.e. return open flags for this fd\n    int (*getflags)(struct fd *fd);\n    // handle F_SETFL, i.e. set O_NONBLOCK\n    int (*setflags)(struct fd *fd, dword_t arg);\n};\n\nstruct fdtable {\n    atomic_uint refcount;\n    unsigned size;\n    struct fd **files;\n    bits_t *cloexec;\n    lock_t lock;\n};\n\nstruct fdtable *fdtable_new(int size);\nvoid fdtable_release(struct fdtable *table);\nstruct fdtable *fdtable_copy(struct fdtable *table);\nvoid fdtable_free(struct fdtable *table);\nvoid fdtable_do_cloexec(struct fdtable *table);\nstruct fd *fdtable_get(struct fdtable *table, fd_t f);\n\nstruct fd *f_get(fd_t f);\n// steals a reference to the fd, gives it to the table on success and destroys it on error\n// flags is checked for O_CLOEXEC and O_NONBLOCK\nfd_t f_install(struct fd *fd, int flags);\nint f_close(fd_t f);\n\n#endif\n"
  },
  {
    "path": "fs/fix_path.h",
    "content": "#ifndef FS_FIX_PATH_H\n#define FS_FIX_PATH_H\n\nstatic inline const char *fix_path(const char *path) {\n    if (path[0] == '\\0')\n        return \".\";\n    if (path[0] == '/')\n        path++;\n    return path;\n}\n\n#endif\n"
  },
  {
    "path": "fs/generic.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"fs/inode.h\"\n#include \"fs/path.h\"\n#include \"fs/dev.h\"\n#include \"kernel/task.h\"\n#include \"kernel/errno.h\"\n\nstruct mount *find_mount_and_trim_path(char *path) {\n    struct mount *mount = mount_find(path);\n    char *dst = path;\n    const char *src = path + strlen(mount->point);\n    while (*src != '\\0')\n        *dst++ = *src++;\n    *dst = '\\0';\n    return mount;\n}\n\nbool contains_mount_point(const char *path) {\n    struct mount *mount;\n    list_for_each_entry(&mounts, mount, mounts) {\n        int n = strlen(path);\n        if (strncmp(path, mount->point, n) == 0 &&\n                (mount->point[n] == '\\0' || mount->point[n] == '/'))\n            return true;\n    }\n    return false;\n}\n\nstruct fd *generic_openat(struct fd *at, const char *path_raw, int flags, int mode) {\n    if (flags & O_RDWR_ && flags & O_WRONLY_)\n        return ERR_PTR(_EINVAL);\n\n    // TODO really, really, seriously reconsider what I'm doing with the strings\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_FOLLOW |\n            (flags & O_CREAT_ ? N_PARENT_DIR_WRITE : 0));\n    if (err < 0)\n        return ERR_PTR(err);\n    struct mount *mount = find_mount_and_trim_path(path);\n    struct fd *fd = mount->fs->open(mount, path, flags, mode);\n    if (IS_ERR(fd)) {\n        // if an error happens after this point, fd_close will release the\n        // mount, but right now we need to do it manually\n        mount_release(mount);\n        return fd;\n    }\n    fd->mount = mount;\n\n    lock(&inodes_lock); // TODO: don't do this\n    struct statbuf stat;\n    err = fd->mount->fs->fstat(fd, &stat);\n    if (err < 0) {\n        unlock(&inodes_lock);\n        goto error;\n    }\n    fd->inode = inode_get_unlocked(mount, stat.inode);\n    unlock(&inodes_lock);\n    fd->type = stat.mode & S_IFMT;\n    fd->flags = flags;\n\n    int accmode;\n    if (flags & O_RDWR_) accmode = AC_R | AC_W;\n    else if (flags & O_WRONLY_) accmode = AC_W;\n    else accmode = AC_R;\n    err = access_check(&stat, accmode);\n    if (err < 0)\n        goto error;\n\n    assert(!S_ISLNK(fd->type)); // would mean path_normalize didn't do its job\n    if (S_ISBLK(fd->type) || S_ISCHR(fd->type)) {\n        int type;\n        if (S_ISBLK(fd->type))\n            type = DEV_BLOCK;\n        else\n            type = DEV_CHAR;\n        err = dev_open(dev_major(stat.rdev), dev_minor(stat.rdev), type, fd);\n        if (err < 0)\n            goto error;\n    }\n    err = _ENXIO;\n    if (S_ISSOCK(fd->type))\n        goto error;\n    err = _EISDIR;\n    if (S_ISDIR(fd->type) && flags & (O_RDWR_ | O_WRONLY_))\n        goto error;\n    err = _ENOTDIR;\n    if (!S_ISDIR(fd->type) && flags & O_DIRECTORY_)\n        goto error;\n    return fd;\n\nerror:\n    fd_close(fd);\n    return ERR_PTR(err);\n}\n\nstruct fd *generic_open(const char *path, int flags, int mode) {\n    return generic_openat(AT_PWD, path, flags, mode);\n}\n\nint generic_getpath(struct fd *fd, char *buf) {\n    int err = fd->mount->fs->getpath(fd, buf);\n    if (err < 0)\n        return err;\n    if (strlen(buf) + strlen(fd->mount->point) >= MAX_PATH)\n        return _ENAMETOOLONG;\n    memmove(buf + strlen(fd->mount->point), buf, strlen(buf) + 1);\n    memcpy(buf, fd->mount->point, strlen(fd->mount->point));\n    if (buf[0] == '\\0')\n        strcpy(buf, \"/\");\n    return 0;\n}\n\nint generic_accessat(struct fd *dirfd, const char *path_raw, int mode) {\n    char path[MAX_PATH];\n    int err = path_normalize(dirfd, path_raw, path, N_SYMLINK_FOLLOW);\n    if (err < 0)\n        return err;\n\n    struct mount *mount = find_mount_and_trim_path(path);\n    struct statbuf stat = {};\n    err = mount->fs->stat(mount, path, &stat);\n    mount_release(mount);\n    if (err < 0)\n        return err;\n    return access_check(&stat, mode);\n}\n\nint generic_linkat(struct fd *src_at, const char *src_raw, struct fd *dst_at, const char *dst_raw) {\n    char src[MAX_PATH];\n    int err = path_normalize(src_at, src_raw, src, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    char dst[MAX_PATH];\n    err = path_normalize(dst_at, dst_raw, dst, N_SYMLINK_NOFOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(src);\n    struct mount *dst_mount = find_mount_and_trim_path(dst);\n    if (mount != dst_mount)\n        err = _EXDEV;\n    else if (mount->fs->link == NULL)\n        err = _EPERM;\n    else\n        err = mount->fs->link(mount, src, dst);\n    mount_release(mount);\n    mount_release(dst_mount);\n    return err;\n}\n\nint generic_unlinkat(struct fd *at, const char *path_raw) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->unlink)\n        err = mount->fs->unlink(mount, path);\n    mount_release(mount);\n    return err;\n}\n\nint generic_renameat(struct fd *src_at, const char *src_raw, struct fd *dst_at, const char *dst_raw) {\n    char src[MAX_PATH];\n    int err = path_normalize(src_at, src_raw, src, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    char dst[MAX_PATH];\n    err = path_normalize(dst_at, dst_raw, dst, N_SYMLINK_NOFOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    if (contains_mount_point(src))\n        return _EBUSY;\n    struct mount *mount = find_mount_and_trim_path(src);\n    struct mount *dst_mount = find_mount_and_trim_path(dst);\n    if (mount != dst_mount)\n        err = _EXDEV;\n    else if (mount->fs->rename == NULL)\n        err = _EPERM;\n    else\n        err = mount->fs->rename(mount, src, dst);\n    mount_release(mount);\n    mount_release(dst_mount);\n    return err;\n}\n\nint generic_symlinkat(const char *target, struct fd *at, const char *link_raw) {\n    char link[MAX_PATH];\n    int err = path_normalize(at, link_raw, link, N_SYMLINK_NOFOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(link);\n    err = _EPERM;\n    if (mount->fs->symlink)\n        err = mount->fs->symlink(mount, target, link);\n    mount_release(mount);\n    return err;\n}\n\nint generic_mknodat(struct fd *at, const char *path_raw, mode_t_ mode, dev_t_ dev) {\n    if (S_ISDIR(mode) || S_ISLNK(mode))\n        return _EINVAL;\n    if (!superuser() && (S_ISBLK(mode) || S_ISCHR(mode)))\n        return _EPERM;\n\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_NOFOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->mknod)\n        err = mount->fs->mknod(mount, path, mode, dev);\n    mount_release(mount);\n    return err;\n}\n\nint generic_setattrat(struct fd *at, const char *path_raw, struct attr attr, bool follow_links) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, follow_links ? N_SYMLINK_FOLLOW : N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->setattr)\n        err = mount->fs->setattr(mount, path, attr);\n    mount_release(mount);\n    return err;\n}\n\nint generic_utime(struct fd *at, const char *path_raw, struct timespec atime, struct timespec mtime, bool follow_links) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, follow_links ? N_SYMLINK_FOLLOW : N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->utime)\n        err = mount->fs->utime(mount, path, atime, mtime);\n    mount_release(mount);\n    return err;\n}\n\nssize_t generic_readlinkat(struct fd *at, const char *path_raw, char *buf, size_t bufsize) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EINVAL;\n    if (mount->fs->readlink)\n        err = mount->fs->readlink(mount, path, buf, bufsize);\n    mount_release(mount);\n    return err;\n}\n\nint generic_mkdirat(struct fd *at, const char *path_raw, mode_t_ mode) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_FOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->mkdir)\n        err = mount->fs->mkdir(mount, path, mode);\n    mount_release(mount);\n    return err;\n}\n\nint generic_rmdirat(struct fd *at, const char *path_raw) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, N_SYMLINK_FOLLOW | N_PARENT_DIR_WRITE);\n    if (err < 0)\n        return err;\n    if (contains_mount_point(path))\n        return _EBUSY;\n    struct mount *mount = find_mount_and_trim_path(path);\n    err = _EPERM;\n    if (mount->fs->rmdir)\n        err = mount->fs->rmdir(mount, path);\n    mount_release(mount);\n    return err;\n}\n\nint generic_seek(struct fd *fd, off_t_ off, int whence, size_t size) {\n    off_t_ new_off = fd->offset;\n    if (whence == LSEEK_SET) {\n        fd->offset = off;\n    } else if (whence == LSEEK_CUR) {\n        if (__builtin_add_overflow(new_off, off, &new_off) || new_off < 0)\n            return _EINVAL;\n        fd->offset = new_off;\n    } else if (whence == LSEEK_END) {\n        new_off = size + off;\n        if (new_off < 0)\n            return _EINVAL;\n        fd->offset = new_off;\n    } else {\n        return _EINVAL;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "fs/inode.c",
    "content": "#include <stdlib.h>\n#include \"util/list.h\"\n#include \"kernel/fs.h\"\n#include \"fs/inode.h\"\n#include \"debug.h\"\n\nlock_t inodes_lock = LOCK_INITIALIZER;\n#define INODES_HASH_SIZE (1 << 10)\nstatic struct list inodes_hash[INODES_HASH_SIZE];\n\nint current_pid(void);\n\nstatic struct inode_data *inode_get_data(struct mount *mount, ino_t ino) {\n    int index = ino % INODES_HASH_SIZE;\n    if (list_null(&inodes_hash[index]))\n        list_init(&inodes_hash[index]);\n    struct inode_data *inode;\n    list_for_each_entry(&inodes_hash[index], inode, chain) {\n        if (inode->mount == mount && inode->number == ino)\n            return inode;\n    }\n    return NULL;\n}\n\nstruct inode_data *inode_get_unlocked(struct mount *mount, ino_t ino) {\n    struct inode_data *inode = inode_get_data(mount, ino);\n    if (inode == NULL) {\n        inode = malloc(sizeof(struct inode_data));\n        inode->refcount = 0;\n        inode->number = ino;\n        mount_retain(mount);\n        inode->mount = mount;\n        inode->socket_id = 0;\n        cond_init(&inode->posix_unlock);\n        list_init(&inode->posix_locks);\n        list_init(&inode->chain);\n        lock_init(&inode->lock);\n        list_add(&inodes_hash[ino % INODES_HASH_SIZE], &inode->chain);\n    }\n\n    inode_retain(inode);\n    return inode;\n}\n\nstruct inode_data *inode_get(struct mount *mount, ino_t ino) {\n    lock(&inodes_lock);\n    struct inode_data *data = inode_get_unlocked(mount, ino);\n    unlock(&inodes_lock);\n    return data;\n}\n\nvoid inode_check_orphaned(struct mount *mount, ino_t ino) {\n    lock(&inodes_lock);\n    struct inode_data *inode = inode_get_data(mount, ino);\n    if (inode == NULL)\n        mount->fs->inode_orphaned(mount, ino);\n    unlock(&inodes_lock);\n}\n\nvoid inode_retain(struct inode_data *inode) {\n    lock(&inode->lock);\n    inode->refcount++;\n    unlock(&inode->lock);\n}\n\nvoid inode_release(struct inode_data *inode) {\n    lock(&inodes_lock);\n    lock(&inode->lock);\n    if (--inode->refcount == 0) {\n        unlock(&inode->lock);\n        list_remove(&inode->chain);\n        if (inode->mount->fs->inode_orphaned)\n            inode->mount->fs->inode_orphaned(inode->mount, inode->number);\n        unlock(&inodes_lock);\n        mount_release(inode->mount);\n        free(inode);\n    } else {\n        unlock(&inode->lock);\n        unlock(&inodes_lock);\n    }\n}\n"
  },
  {
    "path": "fs/inode.h",
    "content": "#ifndef FS_INODE_H\n#define FS_INODE_H\n#include <sys/types.h>\n#include \"misc.h\"\n#include \"util/list.h\"\n#include \"util/sync.h\"\nstruct mount;\nstruct fd;\n\nstruct inode_data {\n    unsigned refcount;\n    ino_t number;\n    struct mount *mount;\n    struct list chain;\n\n    struct list posix_locks;\n    cond_t posix_unlock;\n\n    uint32_t socket_id;\n\n    lock_t lock;\n};\n\nstruct inode_data *inode_get(struct mount *mount, ino_t inode);\nvoid inode_retain(struct inode_data *inode);\nvoid inode_release(struct inode_data *inode);\n\n// generic_open must lock out anything trying to destroy an inode between\n// opening the file and acquiring a reference to its inode. For this purpose\n// only, the inodes_lock and inode_get_unlocked are made available. Think\n// carefully before using them for anything else.\n// mount->lock nests inside this.\n// To quote @dril: i despise this lock. id love nothing more than to kick it\n// through the wall and shatter it into 100 deadlocks. But i need it\nextern lock_t inodes_lock;\nstruct inode_data *inode_get_unlocked(struct mount *mount, ino_t inode);\n\n// calls mount->fs->inode_orphaned if this inode is orphaned, while holding indoes_lock\nvoid inode_check_orphaned(struct mount *mount, ino_t ino);\n\n// file locking stuff (maybe should go in kernel/calls.h?)\n\n#define F_RDLCK_ 0\n#define F_WRLCK_ 1\n#define F_UNLCK_ 2\n\nstruct file_lock {\n    off_t_ start;\n    off_t_ end;\n    int type;\n    pid_t_ pid;\n    void *owner;\n    struct list locks;\n};\n\nstruct flock_ {\n    word_t type;\n    word_t whence;\n    off_t_ start;\n    off_t_ len;\n    pid_t_ pid;\n} __attribute__((packed));\nstruct flock32_ {\n    word_t type;\n    word_t whence;\n    dword_t start;\n    dword_t len;\n    pid_t_ pid;\n} __attribute__((packed));\n\nint fcntl_getlk(struct fd *fd, struct flock_ *flock);\n// cmd should be either F_SETLK or F_SETLKW\nint fcntl_setlk(struct fd *fd, struct flock_ *flock, bool block);\n\n// locks the inode internally\nvoid file_lock_remove_owned_by(struct fd *fd, void *owner);\n\n#endif\n"
  },
  {
    "path": "fs/lock.c",
    "content": "#include <limits.h>\n#include \"kernel/calls.h\"\n#include \"kernel/fs.h\"\n#include \"fs/inode.h\"\n\nstatic bool file_locks_overlap(struct file_lock *a, struct file_lock *b) {\n    return a->end >= b->start && b->end >= a->start;\n}\n\nstatic bool file_locks_conflict(struct file_lock *a, struct file_lock *b) {\n    if (a->owner == b->owner)\n        return false;\n    if (!file_locks_overlap(a, b))\n        return false;\n    // write locks are incompatible with other types of locks\n    if (a->type == F_WRLCK_ || b->type == F_WRLCK_)\n        return true;\n    return false;\n}\n\nstatic bool file_locks_adjacent(struct file_lock *a, struct file_lock *b) {\n    return a->end == b->start - 1 || b->end == a->start - 1;\n}\n\nstatic struct file_lock *file_lock_test(struct inode_data *inode, struct file_lock *request) {\n    struct file_lock *lock;\n    list_for_each_entry(&inode->posix_locks, lock, locks) {\n        if (file_locks_conflict(lock, request))\n            return lock;\n    }\n    return NULL;\n}\n\nstatic struct file_lock *file_lock_copy(struct file_lock *request) {\n    struct file_lock *lock = malloc(sizeof(struct file_lock));\n    lock->start = request->start;\n    lock->end = request->end;\n    lock->type = request->type;\n    lock->owner = request->owner;\n    lock->pid = request->pid;\n    list_init(&lock->locks);\n    return lock;\n}\n\nstatic void file_lock_delete(struct file_lock *lock) {\n    list_remove(&lock->locks);\n    free(lock);\n}\n\nstatic int file_lock_acquire(struct inode_data *inode, struct file_lock *request) {\n    struct file_lock *lock;\n\n    if (request->type != F_UNLCK_) {\n        list_for_each_entry(&inode->posix_locks, lock, locks) {\n            if (file_locks_conflict(lock, request))\n                return _EAGAIN;\n            // TODO check for deadlocks\n        }\n    }\n\n    // If the loop above succeeded, the lock can be placed. Now we just need to\n    // add it into our existing set of locks. This is complicated because it\n    // might need to:\n    // - merge with an adjacent or overlapping lock of the same type\n    // - override an existing overlapping lock of a different type\n    // - split an existing lock into two, if it overlaps just the middle\n    // - do any or all of the above at the same time\n\n    bool found_our_locks = false;\n    struct file_lock *tmp;\n    list_for_each_entry_safe(&inode->posix_locks, lock, tmp, locks) {\n        // To speed up looping over all of our locks, the locks are grouped by owner.\n        if (!found_our_locks) {\n            if (lock->owner != request->owner)\n                continue;\n            found_our_locks = true;\n        } else {\n            if (lock->owner != request->owner)\n                break;\n        }\n        assert(lock->owner == request->owner);\n\n        if (request->type == lock->type) {\n            if (!file_locks_overlap(lock, request) && !file_locks_adjacent(request, lock))\n                continue;\n            // merge request with lock\n            // extend request until it covers lock, then delete lock\n            if (lock->start < request->start)\n                request->start = lock->start;\n            if (lock->end > request->end)\n                request->end = lock->end;\n            file_lock_delete(lock);\n        } else {\n            if (!file_locks_overlap(lock, request))\n                continue;\n            // request must subtract from the lock\n            // the main thing to worry about here is if the lock is larger than\n            // the request on both ends, in which case it needs to be split\n            // into two locks\n            //\n            // test cases to think about: request on the top, lock on the bottom\n            // ..::'' ''::.. ..::.. ''::'' :::... ...::: '''::: :::''' ::::::\n\n            // every case here has the possibility of reducing the locking on some region\n            notify(&inode->posix_unlock);\n\n            if (request->start > lock->start && request->end < lock->end) {\n                // lock sticks out on both ends, split\n                struct file_lock *lock2 = file_lock_copy(lock);\n                // see below for why these can't overflow\n                lock->end = request->start - 1;\n                lock2->start = request->end + 1;\n                list_add_after(&lock->locks, &lock2->locks);\n            } else if (request->start <= lock->start && request->end >= lock->end) {\n                // lock doesn't stick out at all, so just remove it\n                file_lock_delete(lock);\n            } else if (lock->start < request->start) {\n                // lock sticks out on the start, so move the end down\n                assert(lock->end >= request->start);\n                // subtract can't overflow since the comparison above would fail if request->start is 0\n                lock->end = request->start - 1;\n            } else if (lock->end > request->end) {\n                // lock sticks out on the end, so move the start up\n                assert(lock->start <= request->end);\n                // add can't overflow since the comparison above would fail if request->start is OFF_T_MAX\n                lock->start = request->end + 1;\n            }\n        }\n    }\n\n    if (request->type != F_UNLCK_) {\n        struct file_lock *new_lock = file_lock_copy(request);\n        list_add_before(&lock->locks, &new_lock->locks);\n    }\n    return 0;\n}\n\n#define OFF_T_MAX ~(1l << (sizeof(off_t) * 8 - 1))\n\nstatic int file_lock_from_flock(struct fd *fd, struct flock_ *flock, struct file_lock *lock) {\n    off_t_ offset;\n    switch (flock->whence) {\n        case LSEEK_SET:\n            offset = 0;\n            break;\n        case LSEEK_CUR:\n            if (!fd->ops->lseek) {\n                offset = 0;\n            } else {\n                lock(&fd->lock);\n                offset = fd->ops->lseek(fd, 0, LSEEK_CUR);\n                unlock(&fd->lock);\n                if (offset < 0)\n                    return offset;\n            }\n            break;\n        case LSEEK_END: {\n            struct statbuf stat;\n            int err = fd->mount->fs->fstat(fd, &stat);\n            if (err < 0)\n                return err;\n            offset = stat.size;\n            break;\n        }\n        default:\n            return _EINVAL;\n    }\n\n    lock->start = flock->start + offset;\n    if (flock->len > 0) {\n        lock->end = lock->start + flock->len - 1;\n    } else if (flock->len < 0) {\n        lock->end = lock->start - 1;\n        lock->start = lock->end + flock->len + 1;\n    } else {\n        lock->end = OFF_T_MAX;\n    }\n    lock->type = flock->type;\n    lock->owner = current->files;\n    lock->pid = current->pid;\n    return 0;\n}\n\nstatic int flock_from_file_lock(struct file_lock *lock, struct flock_ *flock) {\n    flock->type = lock->type;\n    flock->whence = LSEEK_SET;\n    flock->start = lock->start;\n    if (lock->end != OFF_T_MAX)\n        flock->len = lock->end - lock->start + 1;\n    else\n        flock->len = 0;\n    flock->pid = lock->pid;\n    return 0;\n}\n\nint fcntl_getlk(struct fd *fd, struct flock_ *flock) {\n    if (flock->type != F_RDLCK_ && flock->type != F_WRLCK_)\n        return _EINVAL;\n    struct inode_data *inode = fd->inode;\n    lock(&inode->lock);\n\n    struct file_lock request;\n    int err = file_lock_from_flock(fd, flock, &request);\n    if (err < 0)\n        goto out;\n    struct file_lock *lock = file_lock_test(inode, &request);\n    err = 0;\n    if (lock != NULL)\n        err = flock_from_file_lock(lock, flock);\n    else\n        flock->type = F_UNLCK_;\nout:\n    unlock(&inode->lock);\n    return err;\n}\n\nint fcntl_setlk(struct fd *fd, struct flock_ *flock, bool blocking) {\n    if (flock->type != F_RDLCK_ && flock->type != F_WRLCK_ && flock->type != F_UNLCK_)\n        return _EINVAL;\n    int fd_mode = fd_getflags(fd) & O_ACCMODE_;\n    if (flock->type == F_RDLCK_ && fd_mode == O_WRONLY_)\n        return _EBADF;\n    if (flock->type == F_WRLCK_ && fd_mode == O_RDONLY_)\n        return _EBADF;\n\n    struct inode_data *inode = fd->inode;\n    lock(&inode->lock);\n\n    struct file_lock request;\n    int err = file_lock_from_flock(fd, flock, &request);\n    if (err < 0)\n        goto out;\n    while ((err = file_lock_acquire(inode, &request)) == _EAGAIN) {\n        if (!blocking)\n            break;\n        err = wait_for(&inode->posix_unlock, &inode->lock, NULL);\n        if (err < 0)\n            break;\n    }\nout:\n    unlock(&inode->lock);\n    return err;\n}\n\nvoid file_lock_remove_owned_by(struct fd *fd, void *owner) {\n    struct inode_data *inode = fd->inode;\n    lock(&inode->lock);\n    struct file_lock *lock, *tmp;\n    list_for_each_entry_safe(&inode->posix_locks, lock, tmp, locks) {\n        if (lock->owner == owner)\n            file_lock_delete(lock);\n    }\n    unlock(&inode->lock);\n}\n"
  },
  {
    "path": "fs/mem.c",
    "content": "#include <string.h>\n#include \"kernel/errno.h\"\n#include \"kernel/random.h\"\n#include \"fs/poll.h\"\n#include \"fs/mem.h\"\n#include \"fs/dev.h\"\n#include \"fs/devices.h\"\n\nextern struct dev_ops\n    null_dev,\n    zero_dev,\n    full_dev,\n    random_dev;\n\n// this file handles major device number MEM_MAJOR, minor device numbers are mapped in table below\nstruct dev_ops *mem_devs[256] = {\n    // [1] = &prog_mem_dev,\n    // [2] = &kmem_dev, // (not really applicable)\n    [DEV_NULL_MINOR] = &null_dev,\n    // [4] = &port_dev,\n    [DEV_ZERO_MINOR] = &zero_dev,\n    [DEV_FULL_MINOR] = &full_dev,\n    [DEV_RANDOM_MINOR] = &random_dev,\n    [DEV_URANDOM_MINOR] = &random_dev,\n    // [10] = &aio_dev,\n    // [11] = &kmsg_dev,\n    // [12] = &oldmem_dev, // replaced by /proc/vmcore\n};\n\n// dispatch device for major device 1\nstatic int mem_open(int major, int minor, struct fd *fd) {\n    struct dev_ops *dev = mem_devs[minor];\n    if (dev == NULL) {\n        return _ENXIO;\n    }\n    fd->ops = &dev->fd;\n    if (!dev->open)\n        return 0;\n    return dev->open(major, minor, fd);\n}\n\nstruct dev_ops mem_dev = {\n    .open = mem_open,\n};\n\nstatic int ready_poll(struct fd *UNUSED(fd)) {\n    return POLL_READ | POLL_WRITE;\n}\n\n// begin inline devices\nstatic int null_open(int UNUSED(major), int UNUSED(minor), struct fd *UNUSED(fd)) {\n    return 0;\n}\nstatic ssize_t null_read(struct fd *UNUSED(fd), void *UNUSED(buf), size_t UNUSED(bufsize)) {\n    return 0;\n}\nstatic ssize_t null_write(struct fd *UNUSED(fd), const void *UNUSED(buf), size_t bufsize) {\n    return bufsize;\n}\nstatic off_t_ null_lseek(struct fd *UNUSED(fd), off_t_ UNUSED(off), int UNUSED(whence)) {\n    return 0;\n}\nstruct dev_ops null_dev = {\n    .open = null_open,\n    .fd.read = null_read,\n    .fd.write = null_write,\n    .fd.lseek = null_lseek,\n    .fd.poll = ready_poll,\n};\n\nstatic ssize_t zero_read(struct fd *UNUSED(fd), void *buf, size_t bufsize) {\n    memset(buf, 0, bufsize);\n    return bufsize;\n}\nstatic ssize_t zero_write(struct fd *UNUSED(fd), const void *UNUSED(buf), size_t bufsize) {\n    return bufsize;\n}\nstruct dev_ops zero_dev = {\n    .open = null_open,\n    .fd.read = zero_read,\n    .fd.write = zero_write,\n    .fd.lseek = null_lseek,\n    .fd.poll = ready_poll,\n};\n\nstatic ssize_t full_write(struct fd *UNUSED(fd), const void *UNUSED(buf), size_t UNUSED(bufsize)) {\n    return _ENOSPC;\n}\nstruct dev_ops full_dev = {\n    .open = null_open,\n    .fd.read = zero_read,\n    .fd.write = full_write,\n    .fd.lseek = null_lseek,\n    .fd.poll = ready_poll,\n};\n\nstatic ssize_t random_read(struct fd *UNUSED(fd), void *buf, size_t bufsize) {\n    get_random(buf, bufsize);\n    return bufsize;\n}\nstruct dev_ops random_dev = {\n    .open = null_open,\n    .fd.read = random_read,\n    .fd.write = null_write,\n    .fd.lseek = null_lseek,\n    .fd.poll = ready_poll,\n};\n"
  },
  {
    "path": "fs/mem.h",
    "content": "#ifndef FS_NULL_H\n#define FS_NULL_H\n\n#include \"kernel/fs.h\"\n#include \"fs/dev.h\"\n\nextern struct dev_ops mem_dev;\n\n#endif\n"
  },
  {
    "path": "fs/mount.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/calls.h\"\n#include \"kernel/fs.h\"\n#include \"fs/path.h\"\n#include \"fs/real.h\"\n\n#define MAX_FILESYSTEMS 10\nstatic const struct fs_ops *filesystems[MAX_FILESYSTEMS] = {\n    &realfs,\n    &procfs,\n    &devptsfs,\n    &tmpfs,\n};\n\nvoid fs_register(const struct fs_ops *fs) {\n    for (unsigned i = 0; i < MAX_FILESYSTEMS; i++) {\n        if (filesystems[i] == NULL) {\n            filesystems[i] = fs;\n            return;\n        }\n    }\n    assert(!\"reached filesystem limit\");\n}\n\nstruct mount *mount_find(char *path) {\n    assert(path_is_normalized(path));\n    lock(&mounts_lock);\n    struct mount *mount = NULL;\n    assert(!list_empty(&mounts)); // this would mean there's no root FS mounted\n    list_for_each_entry(&mounts, mount, mounts) {\n        size_t n = strlen(mount->point);\n        if (strncmp(path, mount->point, n) == 0 && (path[n] == '/' || path[n] == '\\0'))\n            break;\n    }\n    mount->refcount++;\n    unlock(&mounts_lock);\n    return mount;\n}\n\nvoid mount_retain(struct mount *mount) {\n    lock(&mounts_lock);\n    mount->refcount++;\n    unlock(&mounts_lock);\n}\n\nvoid mount_release(struct mount *mount) {\n    lock(&mounts_lock);\n    mount->refcount--;\n    unlock(&mounts_lock);\n}\n\nint do_mount(const struct fs_ops *fs, const char *source, const char *point, const char *info, int flags) {\n    struct mount *new_mount = malloc(sizeof(struct mount));\n    if (new_mount == NULL)\n        return _ENOMEM;\n    new_mount->point = strdup(point);\n    new_mount->source = strdup(source);\n    new_mount->info = strdup(info);\n    new_mount->flags = flags;\n    new_mount->fs = fs;\n    new_mount->data = NULL;\n    new_mount->refcount = 0;\n    if (fs->mount) {\n        int err = fs->mount(new_mount);\n        if (err < 0) {\n            free((void *) new_mount->point);\n            free((void *) new_mount->source);\n            free(new_mount);\n            return err;\n        }\n    }\n\n    // the list must stay in descending order of mount point length\n    struct mount *mount;\n    list_for_each_entry(&mounts, mount, mounts) {\n        if (strlen(mount->point) <= strlen(new_mount->point))\n            break;\n    }\n    list_add_before(&mount->mounts, &new_mount->mounts);\n    return 0;\n}\n\nint mount_remove(struct mount *mount) {\n    if (mount->refcount != 0)\n        return _EBUSY;\n\n    if (mount->fs->umount)\n        mount->fs->umount(mount);\n    list_remove(&mount->mounts);\n    free((void *) mount->info);\n    free((void *) mount->source);\n    free((void *) mount->point);\n    free(mount);\n    return 0;\n}\n\nint do_umount(const char *point) {\n    struct mount *mount;\n    bool found = false;\n    list_for_each_entry(&mounts, mount, mounts) {\n        if (strcmp(point, mount->point) == 0) {\n            found = true;\n            break;\n        }\n    }\n    if (!found)\n        return _EINVAL;\n    return mount_remove(mount);\n}\n\n// FIXME: this is shit\nbool mount_param_flag(const char *info, const char *flag) {\n    while (*info != '\\0') {\n        if (strncmp(info, flag, strlen(flag)) == 0)\n            return true;\n        info += strcspn(info, \",\");\n    }\n    return false;\n}\n\n#define MS_SUPPORTED (MS_READONLY_|MS_NOSUID_|MS_NODEV_|MS_NOEXEC_|MS_SILENT_)\n#define MS_FLAGS (MS_READONLY_|MS_NOSUID_|MS_NODEV_|MS_NOEXEC_)\n\ndword_t sys_mount(addr_t source_addr, addr_t point_addr, addr_t type_addr, dword_t flags, addr_t data_addr) {\n    char source[MAX_PATH];\n    if (user_read_string(source_addr, source, sizeof(source)))\n        return _EFAULT;\n    char point_raw[MAX_PATH];\n    if (user_read_string(point_addr, point_raw, sizeof(point_raw)))\n        return _EFAULT;\n    char data[MAX_PATH];\n    if (data_addr != 0) {\n        if (user_read_string(data_addr, data, sizeof(data)))\n            return _EFAULT;\n    }\n    char type[100];\n    if (user_read_string(type_addr, type, sizeof(type)))\n        return _EFAULT;\n    STRACE(\"mount(\\\"%s\\\", \\\"%s\\\", \\\"%s\\\", %#x, \\\"%s\\\")\", source, point_raw, type, flags, data_addr != 0 ? data : NULL);\n\n    if (flags & ~MS_SUPPORTED) {\n        FIXME(\"missing mount flags %#x\", flags & ~MS_SUPPORTED);\n        return _EINVAL;\n    }\n\n    const struct fs_ops *fs = NULL;\n    for (size_t i = 0; i < sizeof(filesystems)/sizeof(filesystems[0]); i++) {\n        if (filesystems[i] && (strcmp(filesystems[i]->name, type) == 0)) {\n            fs = filesystems[i];\n            break;\n        }\n    }\n    if (fs == NULL)\n        return _EINVAL;\n\n    struct statbuf stat;\n    int err = generic_statat(AT_PWD, point_raw, &stat, true);\n    if (err < 0)\n        return err;\n    if (!S_ISDIR(stat.mode))\n        return _ENOTDIR;\n\n    char point[MAX_PATH];\n    err = path_normalize(AT_PWD, point_raw, point, N_SYMLINK_FOLLOW);\n    if (err < 0)\n        return err;\n\n    lock(&mounts_lock);\n    err = do_mount(fs, source, point, data, flags & MS_FLAGS);\n    unlock(&mounts_lock);\n    return err;\n}\n\n#define UMOUNT_NOFOLLOW_ 8\n\ndword_t sys_umount2(addr_t target_addr, dword_t flags) {\n    char target_raw[MAX_PATH];\n    if (user_read_string(target_addr, target_raw, sizeof(target_raw)))\n        return _EFAULT;\n    char target[MAX_PATH];\n    int err = path_normalize(AT_PWD, target_raw, target,\n            flags & UMOUNT_NOFOLLOW_ ? N_SYMLINK_NOFOLLOW : N_SYMLINK_FOLLOW);\n    if (err < 0)\n        return err;\n\n    lock(&mounts_lock);\n    err = do_umount(target);\n    unlock(&mounts_lock);\n    return err;\n}\n\nstruct list mounts = {&mounts, &mounts};\nlock_t mounts_lock = LOCK_INITIALIZER;\n"
  },
  {
    "path": "fs/path.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/calls.h\"\n#include \"fs/path.h\"\n\nstatic int __path_normalize(const char *at_path, const char *path, char *out, int flags, int levels) {\n    // you must choose one\n    if (flags & N_SYMLINK_FOLLOW)\n        assert(!(flags & N_SYMLINK_NOFOLLOW));\n    else\n        assert(flags & N_SYMLINK_NOFOLLOW);\n\n    const char *p = path;\n    char *o = out;\n    *o = '\\0';\n    int n = MAX_PATH - 1;\n\n    if (strcmp(path, \"\") == 0)\n        return _ENOENT;\n\n    if (at_path != NULL && strcmp(at_path, \"/\") != 0) {\n        strcpy(o, at_path);\n        n -= strlen(at_path);\n        o += strlen(at_path);\n    }\n\n    while (*p == '/')\n        p++;\n\n    while (*p != '\\0') {\n        if (p[0] == '.') {\n            if (p[1] == '\\0' || p[1] == '/') {\n                // single dot path component, ignore\n                p++;\n                while (*p == '/')\n                    p++;\n                continue;\n            } else if (p[1] == '.' && (p[2] == '\\0' || p[2] == '/')) {\n                // double dot path component, delete the last component\n                if (o != out) {\n                    do {\n                        o--;\n                        n++;\n                    } while (*o != '/');\n                }\n                p += 2;\n                while (*p == '/')\n                    p++;\n                continue;\n            }\n        }\n\n        // output a slash\n        *o++ = '/'; n--;\n        char *c = o;\n        // copy up to a slash or null\n        while (*p != '/' && *p != '\\0' && --n > 0)\n            *o++ = *p++;\n        // eat any slashes\n        while (*p == '/')\n            p++;\n\n        if (n == 0)\n            return _ENAMETOOLONG;\n\n        if ((flags & N_SYMLINK_FOLLOW) || *p != '\\0') {\n            // this buffer is used to store the path that we're readlinking, then\n            // if it turns out to point to a symlink it's reused as the buffer\n            // passed to the next path_normalize call\n            char possible_symlink[MAX_PATH];\n            *o = '\\0';\n            strcpy(possible_symlink, out);\n            struct mount *mount = find_mount_and_trim_path(possible_symlink);\n            assert(path_is_normalized(possible_symlink));\n            int res = _EINVAL;\n            if (mount->fs->readlink)\n                res = mount->fs->readlink(mount, possible_symlink, c, MAX_PATH - (c - out));\n            if (res >= 0) {\n                mount_release(mount);\n                if (levels >= 5)\n                    return _ELOOP;\n                // readlink does not null terminate\n                c[res] = '\\0';\n                // if we should restart from the root, copy down\n                if (*c == '/')\n                    memmove(out, c, strlen(c) + 1);\n                char *expanded_path = possible_symlink;\n                strcpy(expanded_path, out);\n                if (strcmp(p, \"\") != 0) {\n                    strcat(expanded_path, \"/\");\n                    strcat(expanded_path, p);\n                }\n                return __path_normalize(NULL, expanded_path, out, flags, levels + 1);\n            }\n\n            // if there's a slash after this component, ensure that if it\n            // exists, it's a directory and that we have execute perms on it\n            if (*(p - 1) == '/') {\n                struct statbuf stat;\n                int err = mount->fs->stat(mount, possible_symlink, &stat);\n                mount_release(mount);\n                if (err >= 0) {\n                    if (!S_ISDIR(stat.mode))\n                        return _ENOTDIR;\n                    err = access_check(&stat, AC_X);\n                    if (err < 0)\n                        return err;\n                }\n            } else {\n                mount_release(mount);\n            }\n        }\n    }\n\n    *o = '\\0';\n    assert(path_is_normalized(out));\n\n    return 0;\n}\n\nint path_normalize(struct fd *at, const char *path, char *out, int flags) {\n    assert(at != NULL);\n    if (strcmp(path, \"\") == 0)\n        return _ENOENT;\n\n    // start with root or cwd, depending on whether it starts with a slash\n    lock(&current->fs->lock);\n    if (path[0] == '/')\n        at = current->fs->root;\n    else if (at == AT_PWD)\n        at = current->fs->pwd;\n    unlock(&current->fs->lock);\n    char at_path[MAX_PATH];\n    if (at != NULL) {\n        int err = generic_getpath(at, at_path);\n        if (err < 0)\n            return err;\n        assert(path_is_normalized(at_path));\n    }\n\n    return __path_normalize(at != NULL ? at_path : NULL, path, out, flags, 0);\n}\n\n\nbool path_is_normalized(const char *path) {\n    while (*path != '\\0') {\n        if (*path != '/')\n            return false;\n        path++;\n        if (*path == '/')\n            return false;\n        while (*path != '/' && *path != '\\0')\n            path++;\n    }\n    return true;\n}\n\nbool path_next_component(const char **path, char *component, int *err) {\n    const char *p = *path;\n    if (*p == '\\0')\n        return false;\n\n    assert(*p == '/');\n    p++;\n    char *c = component;\n    while (*p != '/' && *p != '\\0') {\n        *c++ = *p++;\n        if (c - component >= MAX_NAME) {\n            *err = _ENAMETOOLONG;\n            return false;\n        }\n    }\n    *c = '\\0';\n    *path = p;\n    return true;\n}\n"
  },
  {
    "path": "fs/path.h",
    "content": "#ifndef PATH_H\n#define PATH_H\n\n#define AT_PWD (struct fd *) -2\n\n#define N_SYMLINK_FOLLOW 1\n#define N_SYMLINK_NOFOLLOW 2\n#define N_PARENT_DIR_WRITE 4\n\n// Normalizes the path specified and writes the result into the out buffer.\n//\n// Normalization means:\n//  - prepending the current or root directory\n//  - converting multiple slashes into one\n//  - resolving . and ..\n//  - resolving symlinks, skipping the last path component if the follow_links\n//    argument is true\n// The result will always begin with a slash or be empty.\n//\n// If the normalized path plus the null terminator would be longer than\n// MAX_PATH, _ENAMETOOLONG is returned. The out buffer is expected to be at\n// least MAX_PATH in size.\n//\n// at is the file descriptor to use as a base to interpret relative paths. If\n// at is AT_PWD, uses current->pwd (with appropriate locking).\nint path_normalize(struct fd *at, const char *path, char *out, int flags);\nbool path_is_normalized(const char *path);\n\n// Helper function for iterating through a normalized path.\n//\n// The *path pointer is advanced to point to the next /, and the next path\n// component is copied to component. component must point to a buffer large\n// enough to hold a string of MAX_NAME characters.\n//\n// If the next path component was successfully copied, returns true; otherwise\n// returns false. If an error occurred, *err is set to the error code.\n// Otherwise, the end of the path has been reached.\nbool path_next_component(const char **path, char *component, int *err);\n\n#endif\n"
  },
  {
    "path": "fs/pipe.c",
    "content": "#include <sys/stat.h>\n#include <unistd.h>\n#include \"kernel/calls.h\"\n#include \"fs/fd.h\"\n#include \"fs/real.h\"\n#include \"debug.h\"\n\nstatic fd_t pipe_f_create(int pipe_fd, int flags) {\n    struct fd *fd = adhoc_fd_create(&realfs_fdops);\n    if (fd == NULL)\n        return _ENOMEM;\n    fd->real_fd = pipe_fd;\n    fd->stat.mode = S_IFIFO | 0660;\n    fd->stat.uid = current->uid;\n    fd->stat.gid = current->gid;\n    return f_install(fd, flags);\n}\n\nint_t sys_pipe2(addr_t pipe_addr, int_t flags) {\n    STRACE(\"pipe2(%#x, %#x)\", pipe_addr, flags);\n    if (flags & ~(O_CLOEXEC_|O_NONBLOCK_)) {\n        FIXME(\"unsupported pipe2 flags\");\n        return _EINVAL;\n    }\n\n    int p[2];\n    int err = pipe(p);\n    if (err < 0)\n        return err;\n\n    int fp[2];\n    err = fp[0] = pipe_f_create(p[0], flags);\n    if (fp[0] < 0)\n        goto close_pipe;\n    err = fp[1] = pipe_f_create(p[1], flags);\n    if (fp[1] < 0)\n        goto close_fake_0;\n\n    err = _EFAULT;\n    if (user_put(pipe_addr, fp))\n        goto close_fake_1;\n    STRACE(\" [%d %d]\", fp[0], fp[1]);\n    return 0;\n\nclose_fake_1:\n    f_close(fp[1]);\nclose_fake_0:\n    f_close(fp[0]);\nclose_pipe:\n    close(p[0]);\n    close(p[1]);\n    return err;\n}\n\nint_t sys_pipe(addr_t pipe_addr) {\n    return sys_pipe2(pipe_addr, 0);\n}\n"
  },
  {
    "path": "fs/poll.c",
    "content": "#include \"kernel/task.h\"\n#include <string.h>\n#include <poll.h>\n#include <fcntl.h>\n#include <limits.h>\n#include \"misc.h\"\n#include \"util/list.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"fs/poll.h\"\n#include \"fs/real.h\"\n\n#include \"fs/sockrestart.h\"\n\n#if defined(__linux__)\n#include <sys/epoll.h>\n#define HAVE_EPOLL 1\n#elif defined(__APPLE__)\n#include <sys/event.h>\n#define HAVE_KQUEUE 1\n#endif\n\nstatic int real_poll_init(struct real_poll *real);\nstatic void real_poll_close(struct real_poll *real);\nstruct real_poll_event {\n#if HAVE_EPOLL\n    struct epoll_event real;\n#elif HAVE_KQUEUE\n    struct kevent real;\n#endif\n};\nstatic void *rpe_data(struct real_poll_event *rpe);\nstatic int rpe_events(struct real_poll_event *rpe);\nstatic int real_poll_wait(struct real_poll *real, struct real_poll_event *events, int max, struct timespec *timeout);\nstatic int real_poll_update(struct real_poll *real, int fd, int types, void *data);\n\n// lock order: fd, then poll\n\nstruct poll *poll_create() {\n    struct poll *poll = malloc(sizeof(struct poll));\n    if (poll == NULL)\n        return ERR_PTR(_ENOMEM);\n    int err = real_poll_init(&poll->real);\n    if (err < 0)\n        return ERR_PTR(errno_map());\n    poll->waiters = 0;\n    poll->notify_pipe[0] = -1;\n    poll->notify_pipe[1] = -1;\n    list_init(&poll->poll_fds);\n    list_init(&poll->pollfd_freelist);\n    lock_init(&poll->lock);\n    return poll;\n}\n\nstatic inline bool poll_fd_is_real(struct poll_fd *pollfd) {\n    return pollfd->fd->ops->poll == realfs_poll;\n}\n\n// does not do its own locking\nstatic struct poll_fd *poll_find_fd(struct poll *poll, struct fd *fd) {\n    struct poll_fd *poll_fd, *tmp;\n    list_for_each_entry_safe(&poll->poll_fds, poll_fd, tmp, fds) {\n        if (poll_fd->fd == fd)\n            return poll_fd;\n    }\n    return NULL;\n}\n\n// See comment on pollfd_freelist for context\nstatic void poll_fd_free(struct poll_fd *poll_fd) {\n    struct poll *poll = poll_fd->poll;\n    memset(poll_fd, 0xba, sizeof(*poll_fd));\n    poll_fd->poll = NULL; // used to mark it as free\n    list_add(&poll->pollfd_freelist, &poll_fd->fds);\n}\n\nbool poll_has_fd(struct poll *poll, struct fd *fd) {\n    return poll_find_fd(poll, fd) != NULL;\n}\n\nint poll_add_fd(struct poll *poll, struct fd *fd, int types, union poll_fd_info info) {\n    int err;\n    lock(&fd->poll_lock);\n    lock(&poll->lock);\n\n    struct poll_fd *poll_fd;\n    if (!list_empty(&poll->pollfd_freelist)) {\n        poll_fd = list_first_entry(&poll->pollfd_freelist, struct poll_fd, fds);\n        list_remove(&poll_fd->fds);\n    } else {\n        poll_fd = malloc(sizeof(struct poll_fd));\n        if (poll_fd == NULL) {\n            err = _ENOMEM;\n            goto out;\n        }\n    }\n    poll_fd->fd = fd;\n    poll_fd->poll = poll;\n    poll_fd->types = types;\n    poll_fd->info = info;\n    poll_fd->triggered_types = 0;\n\n    if (poll_fd_is_real(poll_fd)) {\n        err = real_poll_update(&poll->real, fd->real_fd, types, poll_fd);\n        if (err < 0) {\n            free(poll_fd);\n            err = errno_map();\n            goto out;\n        }\n    }\n\n    list_add(&fd->poll_fds, &poll_fd->polls);\n    list_add(&poll->poll_fds, &poll_fd->fds);\n\n    err = 0;\nout:\n    unlock(&poll->lock);\n    unlock(&fd->poll_lock);\n    return err;\n}\n\nint poll_del_fd(struct poll *poll, struct fd *fd) {\n    int err;\n    lock(&fd->poll_lock);\n    lock(&poll->lock);\n    struct poll_fd *poll_fd = poll_find_fd(poll, fd);\n    if (poll_fd == NULL) {\n        err = _ENOENT;\n        goto out;\n    }\n\n    if (poll_fd_is_real(poll_fd)) {\n        err = real_poll_update(&poll->real, fd->real_fd, 0, poll_fd);\n        if (err < 0) {\n            err = errno_map();\n            goto out;\n        }\n    }\n\n    list_remove(&poll_fd->polls);\n    list_remove(&poll_fd->fds);\n    poll_fd_free(poll_fd);\n\n    err = 0;\nout:\n    unlock(&poll->lock);\n    unlock(&fd->poll_lock);\n    return err;\n}\n\nint poll_mod_fd(struct poll *poll, struct fd *fd, int types, union poll_fd_info info) {\n    int err;\n    lock(&fd->poll_lock);\n    lock(&poll->lock);\n    struct poll_fd *poll_fd = poll_find_fd(poll, fd);\n    if (poll_fd == NULL) {\n        err = _ENOENT;\n        goto out;\n    }\n\n    if (poll_fd_is_real(poll_fd)) {\n        err = real_poll_update(&poll->real, fd->real_fd, types, poll_fd);\n        if (err < 0) {\n            err = errno_map();\n            goto out;\n        }\n    }\n\n    poll_fd->types = types;\n    poll_fd->info = info;\n    poll_fd->triggered_types &= types;\n\n    err = 0;\nout:\n    unlock(&poll->lock);\n    unlock(&fd->poll_lock);\n    return err;\n}\n\nvoid poll_cleanup_fd(struct fd *fd) {\n    lock(&fd->poll_lock);\n    struct poll_fd *poll_fd, *tmp;\n    list_for_each_entry_safe(&fd->poll_fds, poll_fd, tmp, polls) {\n        lock(&poll_fd->poll->lock);\n        if (poll_fd_is_real(poll_fd))\n            real_poll_update(&poll_fd->poll->real, fd->real_fd, 0, poll_fd);\n        list_remove(&poll_fd->polls);\n        list_remove(&poll_fd->fds);\n        unlock(&poll_fd->poll->lock);\n        poll_fd_free(poll_fd);\n    }\n    unlock(&fd->poll_lock);\n}\n\nvoid poll_wakeup(struct fd *fd, int events) {\n    struct poll_fd *poll_fd;\n    lock(&fd->poll_lock);\n    list_for_each_entry(&fd->poll_fds, poll_fd, polls) {\n        struct poll *poll = poll_fd->poll;\n        lock(&poll->lock);\n        if (poll_fd->types & POLL_EDGETRIGGERED)\n            poll_fd->triggered_types &= ~events;\n        if (poll->notify_pipe[1] != -1)\n            write(poll->notify_pipe[1], \"\", 1);\n        unlock(&poll->lock);\n        // oneshot?\n    }\n    unlock(&fd->poll_lock);\n}\n\nint poll_wait(struct poll *poll_, poll_callback_t callback, void *context, struct timespec *timeout) {\n    lock(&poll_->lock);\n\n    // acquire the pipe\n    if (poll_->waiters++ == 0) {\n        assert(poll_->notify_pipe[0] == -1 && poll_->notify_pipe[1] == -1);\n        if (pipe(poll_->notify_pipe) < 0) {\n            unlock(&poll_->lock);\n            return errno_map();\n        }\n        fcntl(poll_->notify_pipe[0], F_SETFL, O_NONBLOCK);\n        fcntl(poll_->notify_pipe[1], F_SETFL, O_NONBLOCK);\n        real_poll_update(&poll_->real, poll_->notify_pipe[0], POLL_READ, NULL);\n    }\n\n    // TODO this is pretty broken with regards to timeouts\n    int res = 0;\n    while (true) {\n        // check if any fds are ready\n        struct poll_fd *poll_fd, *tmp;\n        list_for_each_entry_safe(&poll_->poll_fds, poll_fd, tmp, fds) {\n            struct fd *fd = poll_fd->fd;\n            int poll_types = 0;\n            if (fd->ops->poll)\n                poll_types = fd->ops->poll(fd);\n            poll_types &= poll_fd->types | POLL_HUP | POLL_ERR;\n            if (poll_fd->types & POLL_EDGETRIGGERED) {\n                poll_types &= ~poll_fd->triggered_types;\n            }\n            if (poll_types) {\n                if (callback(context, poll_types, poll_fd->info) == 1)\n                    res++;\n\n                // The real poll does not actually get the FDs set as oneshot.\n                // But this loop is done while holding the lock, so only one\n                // thread can get each oneshot event. This doesn't solve the\n                // thundering herd problem at all, but at least the semantics\n                // are right. I'll just leave that as a TODO.\n                if (poll_fd->types & POLL_ONESHOT) {\n                    list_remove(&poll_fd->polls);\n                    list_remove(&poll_fd->fds);\n                    if (poll_fd_is_real(poll_fd)) {\n                        real_poll_update(&poll_->real, fd->real_fd, 0, NULL);\n                    }\n                    free(poll_fd);\n                }\n\n                if (poll_fd->types & POLL_EDGETRIGGERED) {\n                    poll_fd->triggered_types |= poll_types;\n                }\n            }\n        }\n        if (res > 0)\n            break;\n\n        lock(&current->sighand->lock);\n        bool signal_pending = !!(current->pending & ~current->blocked);\n        unlock(&current->sighand->lock);\n        if (signal_pending) {\n            res = _EINTR;\n            break;\n        }\n\n        // wait for a ready notification\n        list_for_each_entry(&poll_->poll_fds, poll_fd, fds) {\n            sockrestart_begin_listen_wait(poll_fd->fd);\n        }\n        unlock(&poll_->lock);\n        int err;\n        struct real_poll_event e[4];\n        do {\n            err = real_poll_wait(&poll_->real, e, sizeof(e)/sizeof(e[0]), timeout);\n        } while (sockrestart_should_restart_listen_wait() && errno == EINTR);\n        lock(&poll_->lock);\n        list_for_each_entry(&poll_->poll_fds, poll_fd, fds) {\n            sockrestart_end_listen_wait(poll_fd->fd);\n        }\n\n        if (err < 0) {\n            res = errno_map();\n            break;\n        }\n        if (err == 0) {\n            // timed out and still nobody is ready\n            break;\n        }\n\n        // dead with any edge-triggered notifications\n        for (int i = 0; i < err; i++) {\n            struct poll_fd *triggered_poll_fd = rpe_data(&e[i]);\n            if (triggered_poll_fd != NULL && triggered_poll_fd->poll != NULL &&\n                    triggered_poll_fd->types & POLL_EDGETRIGGERED) {\n                triggered_poll_fd->triggered_types &= ~rpe_events(&e[i]);\n            }\n        }\n\n        char fuck;\n        if (read(poll_->notify_pipe[0], &fuck, 1) < 0 && errno != EAGAIN) {\n            res = errno_map();\n            break;\n        }\n    }\n\n    // release the pipe\n    if (--poll_->waiters == 0) {\n        close(poll_->notify_pipe[0]);\n        close(poll_->notify_pipe[1]);\n        poll_->notify_pipe[0] = -1;\n        poll_->notify_pipe[1] = -1;\n    }\n\n    unlock(&poll_->lock);\n    return res;\n}\n\nvoid poll_destroy(struct poll *poll) {\n    struct poll_fd *poll_fd;\n    struct poll_fd *tmp;\n    list_for_each_entry_safe(&poll->poll_fds, poll_fd, tmp, fds) {\n        lock(&poll_fd->fd->poll_lock);\n        list_remove(&poll_fd->polls);\n        list_remove(&poll_fd->fds);\n        unlock(&poll_fd->fd->poll_lock);\n        free(poll_fd);\n    }\n\n    list_for_each_entry_safe(&poll->pollfd_freelist, poll_fd, tmp, fds) {\n        list_remove(&poll_fd->fds);\n        free(poll_fd);\n    }\n\n    real_poll_close(&poll->real);\n    free(poll);\n}\n\n// Platform-specific real_poll implementations\n\n#if HAVE_EPOLL\n\nstatic int real_poll_init(struct real_poll *real) {\n    real->fd = epoll_create1(0);\n    if (real->fd < 0)\n        return -1;\n    return 0;\n}\n\nstatic int real_poll_wait(struct real_poll *real, struct real_poll_event *events, int max, struct timespec *timeout) {\n    int timeout_millis = -1;\n    if (timeout != NULL)\n        timeout_millis = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;\n    return epoll_wait(real->fd, (struct epoll_event *) events, max, timeout_millis);\n}\n\nstatic int real_poll_update(struct real_poll *real, int fd, int types, void *data) {\n    types &= ~EPOLLONESHOT;\n    if (types == 0)\n        return epoll_ctl(real->fd, EPOLL_CTL_DEL, fd, NULL);\n    struct epoll_event epevent = {.events = types, .data.ptr = data};\n    int err = epoll_ctl(real->fd, EPOLL_CTL_MOD, fd, &epevent);\n    if (err < 0 && errno == ENOENT)\n        err = epoll_ctl(real->fd, EPOLL_CTL_ADD, fd, &epevent);\n    return err;\n}\n\nstatic void *rpe_data(struct real_poll_event *rpe) {\n    return rpe->real.data.ptr;\n}\nstatic int rpe_events(struct real_poll_event *rpe) {\n    return rpe->real.events;\n}\n\n#elif HAVE_KQUEUE\n\nstatic int real_poll_init(struct real_poll *real) {\n    real->fd = kqueue();\n    if (real->fd < 0)\n        return -1;\n    return 0;\n}\n\nstatic int real_poll_update(struct real_poll *real, int fd, int types, void *data) {\n    struct kevent e[3] = {\n        {.filter = EVFILT_READ, .flags = types & (POLL_READ | POLL_HUP) ? EV_ADD : EV_DELETE},\n        {.filter = EVFILT_WRITE, .flags = types & POLL_WRITE ? EV_ADD : EV_DELETE},\n        {.filter = EVFILT_EXCEPT, .flags = types & POLL_ERR ? EV_ADD : EV_DELETE},\n    };\n    if (!(types & POLL_READ) && types & POLL_HUP) {\n        // Set the low water mark really high so we'll only get woken up on a hangup\n        e[0].fflags = NOTE_LOWAT;\n        e[0].data = INT_MAX;\n    }\n    for (int i = 0; i < 3; i++) {\n        e[i].ident = fd;\n        e[i].udata = data;\n        e[i].flags |= EV_RECEIPT;\n        if (types & POLL_EDGETRIGGERED)\n            e[i].flags |= EV_CLEAR;\n    }\n\n    return kevent(real->fd, e, 3, e, 3, NULL);\n}\n\nstatic int real_poll_wait(struct real_poll *real, struct real_poll_event *events, int max, struct timespec *timeout) {\n    return kevent(real->fd, NULL, 0, (struct kevent *) events, max, timeout);\n}\n\nstatic void *rpe_data(struct real_poll_event *rpe) {\n    return rpe->real.udata;\n}\nstatic int rpe_events(struct real_poll_event *rpe) {\n    if (rpe->real.filter == EVFILT_READ) {\n        int events = 0;\n        if (rpe->real.data > 0)\n            events |= POLL_READ;\n        if (rpe->real.flags & EV_EOF)\n            events |= POLL_HUP;\n        return events;\n    }\n    if (rpe->real.filter == EVFILT_WRITE) return POLL_WRITE;\n    if (rpe->real.filter == EVFILT_EXCEPT) return POLL_ERR;\n    return 0;\n}\n\n#endif\n\nstatic void real_poll_close(struct real_poll *real) {\n    close(real->fd);\n}\n\n"
  },
  {
    "path": "fs/poll.h",
    "content": "#ifndef FS_POLL_H\n#define FS_POLL_H\n#include \"kernel/fs.h\"\n\nstruct real_poll {\n    int fd;\n};\n\nstruct poll {\n    struct list poll_fds;\n    struct real_poll real;\n    int notify_pipe[2];\n    int waiters; // if nonzero, notify_pipe exists\n\n    // This is used to solve the race/UaF described here: https://lwn.net/Articles/520012/\n    // thread 1: calls poll_wait, real_poll_wait returns an event with a pointer to a poll_fd\n    // thread 2: calls poll_del_fd which frees the same poll_fd\n    //\n    // This can't be solved by adding locks because thread 1 could get\n    // suspended after real_poll_wait returns but before it has a chance to\n    // lock anything.\n    //\n    // An attempt was made to solve this with a Linux kernel patch, which\n    // almost went in 3.7 but was backed out after discussion at\n    // https://lkml.org/lkml/2012/10/16/302, and anyway wouldn't have solved\n    // the problem on Darwin. My solution is to just not free poll_fds, and\n    // instead move them to a freelist where they can be reused.\n    struct list pollfd_freelist;\n\n    lock_t lock;\n};\n\nstruct poll_fd {\n    // locked by containing struct poll\n    struct fd *fd;\n    struct list fds;\n    int types;\n    union poll_fd_info {\n        void *ptr;\n        int fd;\n        uint64_t num;\n    } info;\n    // Used to implement edge-triggered notifications. When an event is\n    // returned its bits are set here, and those bits are ignored on the next\n    // call to poll_wait. The bits are cleared by poll_wakeup.\n    int triggered_types;\n\n    // locked by containing struct fd\n    struct poll *poll;\n    struct list polls;\n};\n\n// these are defined in system headers somewhere\n#undef POLL_IN\n#undef POLL_OUT\n#undef POLL_MSG\n#undef POLL_ERR\n#undef POLL_PRI\n#undef POLL_HUP\n\n#define POLL_READ 1\n#define POLL_PRI 2\n#define POLL_WRITE 4\n#define POLL_ERR 8\n#define POLL_HUP 16\n#define POLL_NVAL 32\n#define POLL_ONESHOT (1 << 30)\n#define POLL_EDGETRIGGERED (1ul << 31)\nstruct poll_event {\n    struct fd *fd;\n    int types;\n};\nstruct poll *poll_create(void);\nbool poll_has_fd(struct poll *poll, struct fd *fd);\nint poll_add_fd(struct poll *poll, struct fd *fd, int types, union poll_fd_info info);\nint poll_mod_fd(struct poll *poll, struct fd *fd, int types, union poll_fd_info info);\nint poll_del_fd(struct poll *poll, struct fd *fd);\n// Indicates that the specified events have been triggered. Each call will\n// generate a new edge-triggered notification.\n// please do not call this while holding any locks you would acquire in your poll operation\nvoid poll_wakeup(struct fd *fd, int events);\n// Waits for events on the fds in this poll, and calls the callback for each one found.\n// Returns the number of times the callback returned 1, or negative for error.\ntypedef int (*poll_callback_t)(void *context, int types, union poll_fd_info info);\nint poll_wait(struct poll *poll, poll_callback_t callback, void *context, struct timespec *timeout);\n// does not lock the poll because lock ordering, you must ensure no other\n// thread will add or remove fds from this poll\nvoid poll_destroy(struct poll *poll);\n\n// for fd_close\nvoid poll_cleanup_fd(struct fd *fd);\n\n#endif\n"
  },
  {
    "path": "fs/proc/entry.c",
    "content": "#include <sys/stat.h>\n#include <string.h>\n#include \"fs/stat.h\"\n#include \"fs/proc.h\"\n#include \"kernel/task.h\"\n\nmode_t_ proc_entry_mode(struct proc_entry *entry) {\n    mode_t_ mode = entry->meta->mode;\n    if ((mode & S_IFMT) == 0)\n        mode |= S_IFREG;\n    if ((mode & 0777) == 0) {\n        if (S_ISREG(mode))\n            mode |= 0444;\n        else if (S_ISDIR(mode))\n            mode |= 0555;\n        else if (S_ISLNK(mode))\n            mode |= 0777;\n    }\n    return mode;\n}\n\nint proc_entry_stat(struct proc_entry *entry, struct statbuf *stat) {\n    memset(stat, 0, sizeof(*stat));\n    stat->mode = proc_entry_mode(entry);\n\n    lock(&pids_lock);\n    struct task *task = pid_get_task(entry->pid);\n\n    if (task != NULL) {\n        stat->uid = task->uid;\n        stat->gid = task->gid;\n    } // else the memset above will have initialized memory to zero, which is the root uid/gid\n\n    unlock(&pids_lock);\n\n    stat->inode = entry->meta->inode | entry->pid << 16 | (uint64_t) entry->fd << 48;\n    return 0;\n}\n\nvoid proc_entry_getname(struct proc_entry *entry, char *buf) {\n    if (entry->meta->getname)\n        entry->meta->getname(entry, buf);\n    else if (entry->meta->name)\n        strcpy(buf, entry->meta->name);\n    else\n        assert(!\"missing name in proc entry\");\n}\n\nbool proc_dir_read(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry) {\n    if (entry->meta->readdir)\n        return entry->meta->readdir(entry, index, next_entry);\n\n    if (entry->meta->children) {\n        if (*index >= entry->meta->children->count)\n            return false;\n        next_entry->meta = &entry->meta->children->entries[*index];\n        next_entry->pid = entry->pid;\n        (*index)++;\n        return true;\n    }\n    assert(!\"read from invalid proc directory\");\n}\n\nvoid free_string_array(char **array) {\n    for (int i = 0; array[i] != NULL; i++)\n        free(array[i]);\n    free(array);\n}\n\nvoid proc_entry_cleanup(struct proc_entry *entry) {\n    if (entry->name != NULL)\n        free(entry->name);\n    if (entry->child_names != NULL)\n        free_string_array(entry->child_names);\n    *entry = (struct proc_entry) {0};\n}\n"
  },
  {
    "path": "fs/proc/ish.c",
    "content": "#include \"fs/proc.h\"\n#include \"fs/proc/ish.h\"\n#include \"kernel/errno.h\"\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\nconst char *proc_ish_version = \"\";\nchar **(*get_all_defaults_keys)(void);\nchar *(*get_friendly_name)(const char *name);\nchar *(*get_underlying_name)(const char *name);\nbool (*get_user_default)(const char *name, char **buffer, size_t *size);\nbool (*set_user_default)(const char *name, char *buffer, size_t size);\nbool (*remove_user_default)(const char *name);\nchar *(*get_documents_directory)(void);\n\nstatic int proc_ish_show_colors(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    proc_printf(buf,\n                \"\\x1B[30m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[31m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[32m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[33m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[34m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[35m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[36m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[37m\" \"iSH\" \"\\x1B[39m\" \"\\n\\x1B[7m\"\n                \"\\x1B[40m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[41m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[42m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[43m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[44m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[45m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[46m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[47m\" \"iSH\" \"\\x1B[39m\" \"\\x1B[0m\\x1B[1m\\n\"\n                \"\\x1B[90m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[91m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[92m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[93m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[94m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[95m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[96m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[97m\" \"iSH\" \"\\x1B[39m\" \"\\n\\x1B[7m\"\n                \"\\x1B[100m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[101m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[102m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[103m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[104m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[105m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[106m\" \"iSH\" \"\\x1B[39m \"\n                \"\\x1B[107m\" \"iSH\" \"\\x1B[39m\" \"\\x1B[0m\\n\"\n                );\n    return 0;\n}\n\nstatic int proc_ish_show_documents(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    if (get_documents_directory == NULL) {\n        proc_printf(buf, \"\\n\");\n        return 0;\n    }\n    char *directory = get_documents_directory();\n    proc_printf(buf, \"%s\\n\", directory);\n    free(directory);\n    return 0;\n}\n\nstatic void proc_ish_defaults_getname(struct proc_entry *entry, char *buf) {\n    strcpy(buf, entry->name);\n}\n\nstatic int proc_ish_defaults_readlink(struct proc_entry *entry, char *buf) {\n    char *name = get_underlying_name(entry->name);\n    sprintf(buf, \"../.defaults/%s\", name);\n    free(name);\n    return 0;\n}\n\nstatic int proc_ish_underlying_defaults_show(struct proc_entry *entry, struct proc_data *data) {\n    size_t size;\n    char *buffer;\n    if (get_user_default == NULL || !get_user_default(entry->name, &buffer, &size))\n        return _EIO;\n    proc_buf_append(data, buffer, size);\n    free(buffer);\n    return 0;\n}\n\nstatic int proc_ish_underlying_defaults_update(struct proc_entry *entry, struct proc_data *data) {\n    if (set_user_default == NULL || !set_user_default(entry->name, data->data, data->size))\n        return _EIO;\n    return 0;\n}\n\nstatic int proc_ish_underlying_defaults_unlink(struct proc_entry *entry) {\n    return (remove_user_default != NULL && remove_user_default(entry->name)) ? 0 : _EIO;\n}\n\nstatic int proc_ish_defaults_unlink(struct proc_entry *entry) {\n    char *name = get_underlying_name(entry->name);\n    int err = remove_user_default(name) ? 0 : _EIO;\n    free(name);\n    return err;\n}\n\nstruct proc_dir_entry proc_ish_underlying_defaults_fd = { NULL,\n    .getname = proc_ish_defaults_getname,\n    .show = proc_ish_underlying_defaults_show,\n    .update = proc_ish_underlying_defaults_update,\n    .unlink = proc_ish_underlying_defaults_unlink,\n};\nstruct proc_dir_entry proc_ish_defaults_fd = { NULL, S_IFLNK,\n    .getname = proc_ish_defaults_getname,\n    .readlink = proc_ish_defaults_readlink,\n    .unlink = proc_ish_defaults_unlink,\n};\n\nstatic void get_child_names(struct proc_entry *entry, unsigned long index) {\n    if (index == 0 || entry->child_names == NULL) {\n        if (entry->child_names != NULL)\n            free_string_array(entry->child_names);\n        if (get_all_defaults_keys == NULL) {\n            entry->child_names = NULL;\n        } else {\n            entry->child_names = get_all_defaults_keys();\n        }\n    }\n}\n\nstatic bool proc_ish_underlying_defaults_readdir(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry) {\n    get_child_names(entry, *index);\n    if (entry->child_names == NULL || entry->child_names[*index] == NULL)\n        return false;\n    next_entry->meta = &proc_ish_underlying_defaults_fd;\n    next_entry->name = strdup(entry->child_names[*index]);\n    (*index)++;\n    return true;\n}\n\nstatic bool proc_ish_defaults_readdir(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry) {\n    get_child_names(entry, *index);\n    char *friendly_name;\n    do {\n        const char *name = NULL;\n        if (entry->child_names != NULL) {\n            name = entry->child_names[*index];\n        }\n        if (name == NULL)\n            return false;\n        friendly_name = get_friendly_name(name);\n        (*index)++;\n    } while (friendly_name == NULL);\n    next_entry->meta = &proc_ish_defaults_fd;\n    next_entry->name = friendly_name;\n    return true;\n}\n\nstatic int proc_ish_show_version(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    proc_printf(buf, \"%s\\n\", proc_ish_version);\n    return 0;\n}\n\nstruct proc_children proc_ish_children = PROC_CHILDREN({\n    {\"colors\", .show = proc_ish_show_colors},\n    {\".defaults\", S_IFDIR, .readdir = proc_ish_underlying_defaults_readdir},\n    {\"defaults\", S_IFDIR, .readdir = proc_ish_defaults_readdir},\n    {\"documents\", .show = proc_ish_show_documents},\n    {\"version\", .show = proc_ish_show_version},\n});\n"
  },
  {
    "path": "fs/proc/ish.h",
    "content": "#include <stddef.h>\n#include <stdbool.h>\n\nstruct user_default_key {\n    char *name;\n    char *underlying_name;\n};\n\nextern char **(*get_all_defaults_keys)(void);\nextern char *(*get_friendly_name)(const char *name);\nextern char *(*get_underlying_name)(const char *name);\nextern bool (*get_user_default)(const char *name, char **buffer, size_t *size);\nextern bool (*set_user_default)(const char *name, char *buffer, size_t size);\nextern bool (*remove_user_default)(const char *name);\nextern char *(*get_documents_directory)(void);\n"
  },
  {
    "path": "fs/proc/pid.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/memory.h\"\n#include \"kernel/calls.h\"\n#include \"fs/proc.h\"\n#include \"fs/fd.h\"\n#include \"fs/tty.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/vdso.h\"\n#include \"util/sync.h\"\n\nstatic void proc_pid_getname(struct proc_entry *entry, char *buf) {\n    sprintf(buf, \"%d\", entry->pid);\n}\n\nstatic struct task *proc_get_task(struct proc_entry *entry) {\n    lock(&pids_lock);\n    struct task *task = pid_get_task(entry->pid);\n    if (task == NULL)\n        unlock(&pids_lock);\n    return task;\n}\nstatic void proc_put_task(struct task *UNUSED(task)) {\n    unlock(&pids_lock);\n}\n\nstatic int proc_pid_stat_show(struct proc_entry *entry, struct proc_data *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    lock(&task->general_lock);\n    lock(&task->group->lock);\n    lock(&task->sighand->lock);\n\n    proc_printf(buf, \"%d \", task->pid);\n    proc_printf(buf, \"(%.16s) \", task->comm);\n    proc_printf(buf, \"%c \",\n            task->zombie ? 'Z' :\n            task->group->stopped ? 'T' :\n            'R'); // I have no visibility into sleep state at the moment\n    proc_printf(buf, \"%d \", task->parent ? task->parent->pid : 0);\n    proc_printf(buf, \"%d \", task->group->pgid);\n    proc_printf(buf, \"%d \", task->group->sid);\n    struct tty *tty = task->group->tty;\n    proc_printf(buf, \"%d \", tty ? dev_make(tty->driver->major, tty->num) : 0);\n    proc_printf(buf, \"%d \", tty ? tty->fg_group : 0);\n    proc_printf(buf, \"%u \", 0); // flags\n\n    // page faults (no data available)\n    proc_printf(buf, \"%lu \", 0l); // minor faults\n    proc_printf(buf, \"%lu \", 0l); // children minor faults\n    proc_printf(buf, \"%lu \", 0l); // major faults\n    proc_printf(buf, \"%lu \", 0l); // children major faults\n\n    // values that would be returned from getrusage\n    // finding these for a given process isn't too easy\n    proc_printf(buf, \"%lu \", 0l); // user time\n    proc_printf(buf, \"%lu \", 0l); // system time\n    proc_printf(buf, \"%ld \", 0l); // children user time\n    proc_printf(buf, \"%ld \", 0l); // children system time\n\n    proc_printf(buf, \"%ld \", 20l); // priority (not adjustable)\n    proc_printf(buf, \"%ld \", 0l); // nice (also not adjustable)\n    proc_printf(buf, \"%ld \", list_size(&task->group->threads));\n    proc_printf(buf, \"%ld \", 0l); // itimer value (deprecated, always 0)\n    proc_printf(buf, \"%lld \", 0ll); // jiffies on process start\n\n    proc_printf(buf, \"%lu \", 0l); // vsize\n    proc_printf(buf, \"%ld \", 0l); // rss\n    proc_printf(buf, \"%lu \", 0l); // rss limit\n\n    // bunch of shit that can only be accessed by a debugger\n    proc_printf(buf, \"%lu \", 0l); // startcode\n    proc_printf(buf, \"%lu \", 0l); // endcode\n    proc_printf(buf, \"%lu \", task->mm ? task->mm->stack_start : 0);\n    proc_printf(buf, \"%lu \", 0l); // kstkesp\n    proc_printf(buf, \"%lu \", 0l); // kstkeip\n\n    proc_printf(buf, \"%lu \", (unsigned long) task->pending & 0xffffffff);\n    proc_printf(buf, \"%lu \", (unsigned long) task->blocked & 0xffffffff);\n    uint32_t ignored = 0;\n    uint32_t caught = 0;\n    for (int i = 0; i < 32; i++) {\n        if (task->sighand->action[i].handler == SIG_IGN_)\n            ignored |= 1l << i;\n        else if (task->sighand->action[i].handler != SIG_DFL_)\n            caught |= 1l << i;\n    }\n    proc_printf(buf, \"%lu \", (unsigned long) ignored);\n    proc_printf(buf, \"%lu \", (unsigned long) caught);\n\n    proc_printf(buf, \"%lu \", 0l); // wchan (wtf)\n    proc_printf(buf, \"%lu \", 0l); // nswap\n    proc_printf(buf, \"%lu \", 0l); // cnswap\n    proc_printf(buf, \"%d \", task->exit_signal);\n    proc_printf(buf, \"%d\", 0); // processor\n    // that's enough for now\n    proc_printf(buf, \"\\n\");\n\n    unlock(&task->sighand->lock);\n    unlock(&task->group->lock);\n    unlock(&task->general_lock);\n    proc_put_task(task);\n    return 0;\n}\n\nstatic int proc_pid_statm_show(struct proc_entry *entry, struct proc_data *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n\n    proc_printf(buf, \"%lu \", 0); // total vm size\n    proc_printf(buf, \"%lu \", 0); // vm resident size\n    proc_printf(buf, \"%lu \", 0); // resident shared\n    proc_printf(buf, \"%lu \", 0); // text\n    proc_printf(buf, \"%lu \", 0); // lib (always 0 since linux 2.6)\n    proc_printf(buf, \"%lu \", 0); // data + stack\n    proc_printf(buf, \"%lu \", 0); // dirty (always 0 since linux 2.6)\n    proc_printf(buf, \"\\n\");\n\n    proc_put_task(task);\n    return 0;\n}\n\nstatic int proc_pid_auxv_show(struct proc_entry *entry, struct proc_data *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    int err = 0;\n    lock(&task->general_lock);\n    if (task->mm == NULL)\n        goto out_free_task;\n\n    size_t size = task->mm->auxv_end - task->mm->auxv_start;\n    char *data = malloc(size);\n    if (data == NULL) {\n        err = _ENOMEM;\n        goto out_free_task;\n    }\n    if (user_read_task(task, task->mm->auxv_start, data, size) == 0)\n        proc_buf_append(buf, data, size);\n    free(data);\n\nout_free_task:\n    unlock(&task->general_lock);\n    proc_put_task(task);\n    return err;\n}\n\nstatic int proc_pid_cmdline_show(struct proc_entry *entry, struct proc_data *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    int err = 0;\n    lock(&task->general_lock);\n    if (task->mm == NULL)\n        goto out_free_task;\n\n    size_t size = task->mm->argv_end - task->mm->argv_start;\n    char *data = malloc(size);\n    if (data == NULL) {\n        err = _ENOMEM;\n        goto out_free_task;\n    }\n    if (user_read_task(task, task->mm->argv_start, data, size) == 0)\n        proc_buf_append(buf, data, size);\n    free(data);\n\nout_free_task:\n    unlock(&task->general_lock);\n    proc_put_task(task);\n    return err;\n}\n\nvoid proc_maps_dump(struct task *task, struct proc_data *buf) {\n    struct mem *mem = task->mem;\n    if (mem == NULL)\n        return;\n\n    read_wrlock(&mem->lock);\n    page_t page = 0;\n    while (page < MEM_PAGES) {\n        // find a region\n        while (page < MEM_PAGES && mem_pt(mem, page) == NULL) {\n            mem_next_page(mem, &page);\n        }\n        if (page >= MEM_PAGES)\n            break;\n        page_t start = page;\n        struct pt_entry *start_pt = mem_pt(mem, start);\n        struct data *data = start_pt->data;\n\n        // find the end of said region\n        while (page < MEM_PAGES) {\n            struct pt_entry *pt = mem_pt(mem, page);\n            if (pt == NULL)\n                break;\n            if ((pt->flags & P_RWX) != (start_pt->flags & P_RWX))\n                break;\n            // region continues if data is the same or both are anonymous\n            if (!(pt->data == data || (pt->flags & P_ANONYMOUS && start_pt->flags & P_ANONYMOUS)))\n                break;\n            mem_next_page(mem, &page);\n        }\n        page_t end = page;\n\n        // output info\n        char path[MAX_PATH] = \"\";\n        if (start_pt->flags & P_GROWSDOWN) {\n            strcpy(path, \"[stack]\");\n        } else if (data->name != NULL) {\n            strcpy(path, data->name);\n        } else if (data->fd != NULL) {\n            generic_getpath(start_pt->data->fd, path);\n        }\n        proc_printf(buf, \"%08x-%08x %c%c%c%c %08lx 00:00 %-10d %s\\n\",\n                start << PAGE_BITS, end << PAGE_BITS,\n                start_pt->flags & P_READ ? 'r' : '-',\n                start_pt->flags & P_WRITE ? 'w' : '-',\n                start_pt->flags & P_EXEC ? 'x' : '-',\n                start_pt->flags & P_SHARED ? '-' : 'p',\n                (unsigned long) data->file_offset, // offset\n                0, // inode\n                path);\n    }\n    read_wrunlock(&mem->lock);\n}\n\nstatic int proc_pid_maps_show(struct proc_entry *entry, struct proc_data *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    proc_maps_dump(task, buf);\n    proc_put_task(task);\n    return 0;\n}\n\nstatic ssize_t proc_pid_mem_pread(struct proc_entry *entry, struct proc_data *buf, off_t offset) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    int result = user_read_task(task, (addr_t)offset, buf->data, buf->size);\n    proc_put_task(task);\n    return result ? -1 : buf->size;\n}\n\nstatic ssize_t proc_pid_mem_pwrite(struct proc_entry *entry, struct proc_data *buf, off_t offset) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    int result = user_write_task_ptrace(task, (addr_t)offset, buf->data, buf->size);\n    proc_put_task(task);\n    return result ? -1 : buf->size;\n}\n\n\nstatic struct proc_dir_entry proc_pid_fd;\n\nstatic bool proc_pid_fd_readdir(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    lock(&task->files->lock);\n    while (*index < task->files->size && task->files->files[*index] == NULL)\n        (*index)++;\n    fd_t f = (*index)++;\n    bool any_left = (unsigned) f < task->files->size;\n    unlock(&task->files->lock);\n    proc_put_task(task);\n    *next_entry = (struct proc_entry) {&proc_pid_fd, .pid = entry->pid, .fd = f};\n    return any_left;\n}\n\nstatic void proc_pid_fd_getname(struct proc_entry *entry, char *buf) {\n    sprintf(buf, \"%d\", entry->fd);\n}\n\nstatic int proc_pid_fd_readlink(struct proc_entry *entry, char *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    lock(&task->files->lock);\n    struct fd *fd = fdtable_get(task->files, entry->fd);\n    int err = generic_getpath(fd, buf);\n    unlock(&task->files->lock);\n    proc_put_task(task);\n    return err;\n}\n\nstatic int proc_pid_exe_readlink(struct proc_entry *entry, char *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    lock(&task->general_lock);\n    int err = generic_getpath(task->mm->exefile, buf);\n    unlock(&task->general_lock);\n    proc_put_task(task);\n    return err;\n}\n\nstatic void proc_pid_task_getname(struct proc_entry *entry, char *buf) {\n    sprintf(buf, \"%d\", entry->pid);\n}\n\nstatic int proc_pid_task_readlink(struct proc_entry *entry, char *buf) {\n    sprintf(buf, \"/proc/%d\", entry->pid);\n    return 0;\n}\n\nstatic struct proc_dir_entry proc_pid_task;\n\nstatic bool proc_pid_task_readdir(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry) {\n    // TODO: Expose all threads\n    *next_entry = (struct proc_entry) {&proc_pid_task, .pid = entry->pid};\n    return !(*index)++;\n}\n\nstatic int proc_pid_cwd_readlink(struct proc_entry *entry, char *buf) {\n    struct task *task = proc_get_task(entry);\n    if (task == NULL)\n        return _ESRCH;\n    lock(&task->fs->lock);\n    int err = generic_getpath(task->fs->pwd, buf);\n    unlock(&task->fs->lock);\n    proc_put_task(task);\n    return err;\n}\n\n\nstruct proc_children proc_pid_children = PROC_CHILDREN({\n    {\"auxv\", .show = proc_pid_auxv_show},\n    {\"cmdline\", .show = proc_pid_cmdline_show},\n    {\"cwd\", S_IFLNK, .readlink = proc_pid_cwd_readlink},\n    {\"exe\", S_IFLNK, .readlink = proc_pid_exe_readlink},\n    {\"fd\", S_IFDIR, .readdir = proc_pid_fd_readdir},\n    {\"maps\", .show = proc_pid_maps_show},\n    {\"mem\", .pread = proc_pid_mem_pread, .pwrite = proc_pid_mem_pwrite},\n    {\"stat\", .show = proc_pid_stat_show},\n    {\"statm\", .show = proc_pid_statm_show},\n    {\"task\", S_IFDIR, .readdir = proc_pid_task_readdir},\n});\n\nstruct proc_dir_entry proc_pid = {NULL, S_IFDIR,\n    .children = &proc_pid_children, .getname = proc_pid_getname};\n\nstatic struct proc_dir_entry proc_pid_fd = {NULL, S_IFLNK,\n    .getname = proc_pid_fd_getname, .readlink = proc_pid_fd_readlink};\n\nstatic struct proc_dir_entry proc_pid_task = {NULL, S_IFLNK,\n    .getname = proc_pid_task_getname, .readlink = proc_pid_task_readlink};\n"
  },
  {
    "path": "fs/proc/root.c",
    "content": "#include <sys/stat.h>\n#include <inttypes.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"fs/proc.h\"\n#include \"platform/platform.h\"\n\nstatic int proc_show_version(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    struct uname uts;\n    do_uname(&uts);\n    proc_printf(buf, \"%s version %s %s\\n\", uts.system, uts.release, uts.version);\n    return 0;\n}\n\nstatic int proc_show_stat(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    struct cpu_usage usage = get_cpu_usage();\n    proc_printf(buf, \"cpu  %\"PRIu64\" %\"PRIu64\" %\"PRIu64\" %\"PRIu64\"\\n\", usage.user_ticks, usage.nice_ticks, usage.system_ticks, usage.idle_ticks);\n\n    // calculate btime (boot time in seconds since epoch) by subtracting uptime from current time\n    struct uptime_info uptime = get_uptime();\n    struct timespec uptime_ts = {.tv_sec = uptime.uptime_ticks / 100, .tv_nsec = uptime.uptime_ticks % 100};\n    struct timespec boot_time = timespec_subtract(timespec_now(CLOCK_REALTIME), uptime_ts);\n    proc_printf(buf, \"btime %ld\\n\", boot_time.tv_sec);\n\n    return 0;\n}\n\nstatic int proc_show_cpuinfo(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    unsigned cpus = sysconf(_SC_NPROCESSORS_ONLN);\n    for (unsigned i = 0; i < cpus; i++) {\n        proc_printf(buf, \"processor\\t: %u\\n\", i);\n        proc_printf(buf, \"vendor_id\\t: iSH\\n\");\n        proc_printf(buf, \"\\n\");\n    }\n    return 0;\n}\n\nstatic void show_kb(struct proc_data *buf, const char *name, uint64_t value) {\n    proc_printf(buf, \"%s%8\"PRIu64\" kB\\n\", name, value / 1000);\n}\n\nstatic int proc_show_meminfo(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    struct mem_usage usage = get_mem_usage();\n    show_kb(buf, \"MemTotal:       \", usage.total);\n    show_kb(buf, \"MemFree:        \", usage.free);\n    show_kb(buf, \"MemShared:      \", usage.free);\n    // a bunch of crap busybox top needs to see or else it gets stack garbage\n    show_kb(buf, \"Shmem:          \", 0);\n    show_kb(buf, \"Buffers:        \", 0);\n    show_kb(buf, \"Cached:         \", 0);\n    show_kb(buf, \"SwapTotal:      \", 0);\n    show_kb(buf, \"SwapFree:       \", 0);\n    show_kb(buf, \"Dirty:          \", 0);\n    show_kb(buf, \"Writeback:      \", 0);\n    show_kb(buf, \"AnonPages:      \", 0);\n    show_kb(buf, \"Mapped:         \", 0);\n    show_kb(buf, \"Slab:           \", 0);\n    return 0;\n}\n\nstatic int proc_show_uptime(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    struct uptime_info uptime_info = get_uptime();\n    unsigned long uptime = uptime_info.uptime_ticks;\n    proc_printf(buf, \"%lu.%lu %lu.%lu\\n\", uptime / 100, uptime % 100, uptime / 100, uptime % 100);\n    return 0;\n}\n\nstatic int proc_readlink_self(struct proc_entry *UNUSED(entry), char *buf) {\n    sprintf(buf, \"%d/\", current->pid);\n    return 0;\n}\n\nstatic void proc_print_escaped(struct proc_data *buf, const char *str) {\n    for (size_t i = 0; str[i]; i++) {\n        switch (str[i]) {\n            case '\\t': case ' ': case '\\\\':\n                proc_printf(buf, \"\\\\%03o\", str[i]);\n                break;\n            default:\n                proc_printf(buf, \"%c\", str[i]);\n        }\n    }\n}\n\n#define proc_printf_comma(buf, at_start, format, ...) do { \\\n    proc_printf((buf), \"%s\" format, *(at_start) ? \"\" : \",\", ##__VA_ARGS__); \\\n    *(at_start) = false; \\\n} while (0)\n\nstatic int proc_show_mounts(struct proc_entry *UNUSED(entry), struct proc_data *buf) {\n    struct mount *mount;\n    list_for_each_entry(&mounts, mount, mounts) {\n        const char *point = mount->point;\n        if (point[0] == '\\0')\n            point = \"/\";\n\n        proc_print_escaped(buf, mount->source);\n        proc_printf(buf, \" \");\n        proc_print_escaped(buf, point);\n        proc_printf(buf, \" %s \", mount->fs->name);\n        bool at_start = true;\n        proc_printf_comma(buf, &at_start, \"%s\", mount->flags & MS_READONLY_ ? \"ro\" : \"rw\");\n        if (mount->flags & MS_NOSUID_)\n            proc_printf_comma(buf, &at_start, \"nosuid\");\n        if (mount->flags & MS_NODEV_)\n            proc_printf_comma(buf, &at_start, \"nodev\");\n        if (mount->flags & MS_NOEXEC_)\n            proc_printf_comma(buf, &at_start, \"noexec\");\n        if (strcmp(mount->info, \"\") != 0)\n            proc_printf_comma(buf, &at_start, \"%s\", mount->info);\n        proc_printf(buf, \" 0 0\\n\");\n    };\n    return 0;\n}\n\n// in alphabetical order\nstruct proc_dir_entry proc_root_entries[] = {\n    {\"cpuinfo\", .show = proc_show_cpuinfo},\n    {\"ish\", S_IFDIR, .children = &proc_ish_children},\n    {\"meminfo\", .show = proc_show_meminfo},\n    {\"mounts\", .show = proc_show_mounts},\n    {\"self\", S_IFLNK, .readlink = proc_readlink_self},\n    {\"stat\", .show = proc_show_stat},\n    {\"uptime\", .show = proc_show_uptime},\n    {\"version\", .show = proc_show_version},\n};\n#define PROC_ROOT_LEN sizeof(proc_root_entries)/sizeof(proc_root_entries[0])\n\nstatic bool proc_root_readdir(struct proc_entry *UNUSED(entry), unsigned long *index, struct proc_entry *next_entry) {\n    if (*index < PROC_ROOT_LEN) {\n        *next_entry = (struct proc_entry) {&proc_root_entries[*index], *index, NULL, NULL, 0, 0};\n        (*index)++;\n        return true;\n    }\n\n    pid_t_ pid = *index - PROC_ROOT_LEN;\n    if (pid <= MAX_PID) {\n        lock(&pids_lock);\n        do {\n            pid++;\n        } while (pid <= MAX_PID && pid_get_task(pid) == NULL);\n        unlock(&pids_lock);\n        if (pid > MAX_PID)\n            return false;\n        *next_entry = (struct proc_entry) {&proc_pid, .pid = pid};\n        *index = pid + PROC_ROOT_LEN;\n        return true;\n    }\n\n    return false;\n}\n\nstruct proc_dir_entry proc_root = {NULL, S_IFDIR, .readdir = proc_root_readdir};\n"
  },
  {
    "path": "fs/proc.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/calls.h\"\n#include \"kernel/fs.h\"\n#include \"fs/proc.h\"\n#include \"fs/path.h\"\n\nstatic int proc_lookup(const char *path, struct proc_entry *entry) {\n    entry->meta = &proc_root;\n    char component[MAX_NAME + 1];\n    int err = 0;\n    while (path_next_component(&path, component, &err)) {\n        if (!S_ISDIR(proc_entry_mode(entry))) {\n            err = _ENOTDIR;\n            break;\n        }\n\n        unsigned long index = 0;\n        struct proc_entry next_entry = {0};\n        char entry_name[MAX_NAME];\n        while (proc_dir_read(entry, &index, &next_entry)) {\n            // tack on some dynamically generated attributes\n            if (next_entry.meta->parent == NULL)\n                next_entry.meta->parent = entry->meta;\n            else\n                // this asserts that an entry has a unique parent\n                assert(next_entry.meta->parent == entry->meta);\n            if (next_entry.meta->inode == 0)\n                next_entry.meta->inode = index + 1;\n\n            proc_entry_getname(&next_entry, entry_name);\n            if (strcmp(entry_name, component) == 0)\n                goto found;\n            proc_entry_cleanup(&next_entry);\n        }\n        err = _ENOENT;\n        break;\nfound:\n        proc_entry_cleanup(entry);\n        *entry = next_entry;\n    }\n    if (err < 0)\n        proc_entry_cleanup(entry);\n    return err;\n}\n\nextern const struct fd_ops procfs_fdops;\n\nstatic struct fd *proc_open(struct mount *UNUSED(mount), const char *path, int UNUSED(flags), int UNUSED(mode)) {\n    struct proc_entry entry = {0};\n    int err = proc_lookup(path, &entry);\n    if (err < 0)\n        return ERR_PTR(err);\n    struct fd *fd = fd_create(&procfs_fdops);\n    fd->proc.entry = entry;\n    fd->proc.data.data = NULL;\n    return fd;\n}\n\nstatic int proc_getpath(struct fd *fd, char *buf) {\n    char *p = buf + MAX_PATH - 1;\n    size_t n = 0;\n    p[0] = '\\0';\n    struct proc_entry entry = fd->proc.entry;\n    while (entry.meta != &proc_root) {\n        char component[MAX_NAME];\n        proc_entry_getname(&entry, component);\n        size_t component_len = strlen(component) + 1; // plus one for the slash\n        p -= component_len;\n        n += component_len;\n        *p = '/';\n        memcpy(p + 1, component, component_len - 1);\n        entry.meta = entry.meta->parent;\n    }\n    memmove(buf, p, n + 1); // plus one for the null\n    return 0;\n}\n\nstatic int proc_stat(struct mount *UNUSED(mount), const char *path, struct statbuf *stat) {\n    struct proc_entry entry = {0};\n    int err = proc_lookup(path, &entry);\n    if (err < 0)\n        return err;\n    int ret = proc_entry_stat(&entry, stat);\n    proc_entry_cleanup(&entry);\n    return ret;\n}\n\nstatic int proc_fstat(struct fd *fd, struct statbuf *stat) {\n    return proc_entry_stat(&fd->proc.entry, stat);\n}\n\nstatic int proc_refresh_data(struct fd *fd) {\n    mode_t_ mode = proc_entry_mode(&fd->proc.entry);\n    if (S_ISDIR(mode))\n        return _EISDIR;\n    assert(S_ISREG(mode));\n\n    if (fd->proc.data.data == NULL) {\n        fd->proc.data.capacity = 4096;\n        fd->proc.data.data = malloc(fd->proc.data.capacity); // default size\n    }\n    fd->proc.data.size = 0;\n    struct proc_entry *entry = &fd->proc.entry;\n    int err = entry->meta->show(entry, &fd->proc.data);\n    if (err < 0)\n        return err;\n    return 0;\n}\n\nstatic off_t_ proc_seek(struct fd *fd, off_t_ off, int whence) {\n    int err = proc_refresh_data(fd);\n    if (err < 0)\n        return err;\n\n    err = generic_seek(fd, off, whence, fd->proc.data.size);\n    if (err < 0)\n        return err;\n\n    return fd->offset;\n}\n\nstatic ssize_t proc_pread(struct fd *fd, void *buf, size_t bufsize, off_t off) {\n    if (fd->proc.entry.meta->pread) {\n        struct proc_data data = {buf, bufsize, bufsize};\n        return fd->proc.entry.meta->pread(&fd->proc.entry, &data, off);\n    }\n    \n    int err = proc_refresh_data(fd);\n    if (err < 0)\n        return err;\n\n    const char *data = fd->proc.data.data;\n    assert(data != NULL);\n\n    size_t remaining = fd->proc.data.size - off;\n    if ((size_t) off > fd->proc.data.size)\n        remaining = 0;\n    size_t n = bufsize;\n    if (n > remaining)\n        n = remaining;\n\n    memcpy(buf, data + off, n);\n    return n;\n}\n\nstatic void proc_buf_write(struct proc_data *buf, const void *data, size_t size, size_t off) {\n    assert(off <= buf->size);\n    size_t tidemark = off + size;\n    if (tidemark > buf->capacity) {\n        size_t new_capacity = buf->capacity + 1;\n        while (tidemark > new_capacity)\n            new_capacity *= 2;\n        char *new_data = realloc(buf->data, new_capacity);\n        if (new_data == NULL) {\n            // just give up, error reporting is a pain\n            return;\n        }\n        buf->data = new_data;\n        buf->capacity = new_capacity;\n    }\n    assert(tidemark <= buf->capacity);\n    memcpy(buf->data + off, data, size);\n    if (tidemark > buf->size) {\n        buf->size = tidemark;\n    }\n}\n\nstatic ssize_t proc_pwrite(struct fd *fd, const void *buf, size_t bufsize, off_t off) {\n    mode_t_ mode = proc_entry_mode(&fd->proc.entry);\n    if (S_ISDIR(mode))\n        return _EISDIR;\n    assert(S_ISREG(mode));\n    \n    if (fd->proc.entry.meta->pwrite) {\n        struct proc_data data = {(char *) buf, bufsize, bufsize};\n        return fd->proc.entry.meta->pwrite(&fd->proc.entry, &data, off);\n    }\n    \n    if (!fd->proc.entry.meta->update) {\n        return _EPERM;\n    }\n    \n    struct proc_data data = {(char *)buf, bufsize, bufsize};\n    fd->proc.entry.meta->update(&fd->proc.entry, &data);\n    \n    return bufsize;\n}\n\nstatic int proc_readdir(struct fd *fd, struct dir_entry *entry) {\n    struct proc_entry proc_entry = {0};\n    bool any_left = proc_dir_read(&fd->proc.entry, &fd->offset, &proc_entry);\n    if (!any_left)\n        return 0;\n    proc_entry_getname(&proc_entry, entry->name);\n    entry->inode = proc_entry.meta->inode;\n    proc_entry_cleanup(&proc_entry);\n    return 1;\n}\n\nstatic int proc_close(struct fd *fd) {\n    if (fd->proc.data.data != NULL)\n        free(fd->proc.data.data);\n    proc_entry_cleanup(&fd->proc.entry);\n    return 0;\n}\n\nconst struct fd_ops procfs_fdops = {\n    .pread = proc_pread,\n    .pwrite = proc_pwrite,\n    .lseek = proc_seek,\n    .readdir = proc_readdir,\n    .close = proc_close,\n};\n\nstatic ssize_t proc_readlink(struct mount *UNUSED(mount), const char *path, char *buf, size_t bufsize) {\n    struct proc_entry entry = {0};\n    int err = proc_lookup(path, &entry);\n    if (err < 0)\n        return err;\n    if (!S_ISLNK(proc_entry_mode(&entry))) {\n        proc_entry_cleanup(&entry);\n        return _EINVAL;\n    }\n\n    char target[MAX_PATH + 1];\n    err = entry.meta->readlink(&entry, target);\n    proc_entry_cleanup(&entry);\n    if (err < 0)\n        return err;\n    if (bufsize > strlen(target))\n        bufsize = strlen(target);\n    memcpy(buf, target, bufsize);\n    return bufsize;\n}\n\nstatic int proc_unlink(struct mount *UNUSED(mount), const char *path) {\n    struct proc_entry entry = {0};\n    int err = proc_lookup(path, &entry);\n    if (err < 0)\n        return err;\n    err = _EPERM;\n    if (entry.meta->unlink)\n        err = entry.meta->unlink(&entry);\n    proc_entry_cleanup(&entry);\n    return err;\n}\n\nvoid proc_buf_append(struct proc_data *buf, const void *data, size_t size) {\n    proc_buf_write(buf, data, size, buf->size);\n}\n\nvoid proc_printf(struct proc_data *buf, const char *format, ...) {\n    char data[4096];\n    va_list args;\n    va_start(args, format);\n    size_t size = vsnprintf(data, sizeof(data), format, args);\n    va_end(args);\n    proc_buf_append(buf, data, size);\n}\n\nconst struct fs_ops procfs = {\n    .name = \"proc\", .magic = 0x9fa0,\n    .open = proc_open,\n    .getpath = proc_getpath,\n    .stat = proc_stat,\n    .fstat = proc_fstat,\n    .readlink = proc_readlink,\n    .unlink = proc_unlink,\n};\n"
  },
  {
    "path": "fs/proc.h",
    "content": "#ifndef FS_PROC_H\n#define FS_PROC_H\n\n#include \"fs/stat.h\"\n#include \"misc.h\"\n\nstruct proc_entry {\n    struct proc_dir_entry *meta;\n    unsigned long index;\n    char **child_names;\n    char *name;\n    pid_t_ pid;\n    sdword_t fd; // typedef might not have been read yet\n};\n\nstruct proc_data {\n    char *data;\n    size_t size;\n    size_t capacity;\n};\n\nstruct proc_dir_entry {\n    const char *name;\n    mode_t_ mode;\n    \n    // file with dynamic name\n    void (*getname)(struct proc_entry *entry, char *buf);\n\n    // file with custom show data function\n    int (*show)(struct proc_entry *entry, struct proc_data *data);\n    \n    // file with a custom write function\n    int (*update)(struct proc_entry *entry, struct proc_data *data);\n    \n    // file with custom pread functionality\n    ssize_t (*pread)(struct proc_entry *entry, struct proc_data *data, off_t off);\n    \n    // file with custom pwrite functionality\n    ssize_t (*pwrite)(struct proc_entry *entry, struct proc_data *data, off_t off);\n\n    // symlink\n    int (*readlink)(struct proc_entry *entry, char *buf);\n    \n    // remove\n    int (*unlink)(struct proc_entry *entry);\n\n    // directory with static list\n    struct proc_children *children;\n\n    // directory with dynamic contents\n    bool (*readdir)(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry);\n\n    struct proc_dir_entry *parent;\n    int inode;\n};\n\nstruct proc_children {\n    size_t count;\n    struct proc_dir_entry entries[];\n};\n\n#define PROC_CHILDREN(...) { .count = sizeof((struct proc_dir_entry[])__VA_ARGS__) / sizeof(struct proc_dir_entry), .entries = __VA_ARGS__ }\n\nextern struct proc_dir_entry proc_root;\nextern struct proc_dir_entry proc_pid;\nextern struct proc_children proc_ish_children;\n\nmode_t_ proc_entry_mode(struct proc_entry *entry);\nvoid proc_entry_getname(struct proc_entry *entry, char *buf);\nint proc_entry_stat(struct proc_entry *entry, struct statbuf *stat);\nvoid proc_entry_cleanup(struct proc_entry *entry);\n\nvoid free_string_array(char **array);\n\nbool proc_dir_read(struct proc_entry *entry, unsigned long *index, struct proc_entry *next_entry);\n\nvoid proc_buf_append(struct proc_data *buf, const void *data, size_t size);\nvoid proc_printf(struct proc_data *buf, const char *format, ...);\n\n#endif\n"
  },
  {
    "path": "fs/pty.c",
    "content": "#include <string.h>\n#include <limits.h>\n#include <sys/stat.h>\n#include <ctype.h>\n#include \"kernel/task.h\"\n#include \"kernel/errno.h\"\n#include \"fs/tty.h\"\n#include \"fs/devices.h\"\n\nextern struct tty_driver pty_slave;\n\n// the master holds a reference to the slave, so the slave will always be cleaned up second\n// when the master cleans up it hangs up the slave, making any operation that references the master unreachable\n\nstatic void pty_slave_init_inode(struct tty *tty) {\n    tty->pty.uid = current->euid;\n    // TODO make these mount options\n    tty->pty.gid = current->egid;\n    tty->pty.perms = 0620;\n}\n\nstatic int pty_master_init(struct tty *tty) {\n    tty->termios.iflags = 0;\n    tty->termios.oflags = 0;\n    tty->termios.lflags = 0;\n\n    struct tty *slave = tty_alloc(&pty_slave, TTY_PSEUDO_SLAVE_MAJOR, tty->num);\n    slave->refcount = 1;\n    pty_slave.ttys[tty->num] = slave;\n    tty->pty.other = slave;\n    slave->pty.other = tty;\n    slave->pty.locked = true;\n    pty_slave_init_inode(slave);\n    return 0;\n}\n\n\nstatic void pty_hangup(struct tty *tty) {\n    if (tty == NULL)\n        return;\n    lock(&tty->lock);\n    tty_hangup(tty);\n    unlock(&tty->lock);\n}\n\nstatic struct tty *pty_hangup_other(struct tty *tty) {\n    struct tty *other = tty->pty.other;\n    if (other == NULL)\n        return NULL;\n    pty_hangup(other);\n    return other;\n}\n\nstatic void pty_slave_cleanup(struct tty *tty) {\n    pty_hangup_other(tty);\n}\n\nstatic void pty_master_cleanup(struct tty *tty) {\n    struct tty *slave = pty_hangup_other(tty);\n    slave->pty.other = NULL;\n    tty_release(slave);\n}\n\nstatic int pty_slave_open(struct tty *tty) {\n    if (tty->pty.other == NULL)\n        return _EIO;\n    if (tty->pty.locked)\n        return _EIO;\n    return 0;\n}\n\nstatic int pty_slave_close(struct tty *tty) {\n    // If userland's reference count on the pty slave will go to 0,\n    // hang up the pty master.  But the session leader may have a\n    // reference, and the pty master always has a reference.\n    if (tty->refcount - 1 == (tty->session ? 2 : 1)) {\n        pty_hangup_other(tty);\n    }\n    return 0;\n}\n\nstatic int pty_master_ioctl(struct tty *tty, int cmd, void *arg) {\n    struct tty *slave = tty->pty.other;\n    switch (cmd) {\n        case TIOCSPTLCK_:\n            slave->pty.locked = !!*(dword_t *) arg;\n            break;\n        case TIOCGPTN_:\n            *(dword_t *) arg = slave->num;\n            break;\n        case TIOCPKT_:\n            tty->pty.packet_mode = !!*(dword_t *) arg;\n            break;\n        case TIOCGPKT_:\n            *(dword_t *) arg = tty->pty.packet_mode;\n            break;\n        default:\n            return _ENOTTY;\n    }\n    return 0;\n}\n\nstatic int pty_write(struct tty *tty, const void *buf, size_t len, bool blocking) {\n    return tty_input(tty->pty.other, buf, len, blocking);\n}\n\nstatic int pty_return_eio(struct tty *UNUSED(tty)) {\n    return _EIO;\n}\n\n#define MAX_PTYS (1 << 12)\n\nconst struct tty_driver_ops pty_master_ops = {\n    .init = pty_master_init,\n    .open = pty_return_eio,\n    .write = pty_write,\n    .ioctl = pty_master_ioctl,\n    .cleanup = pty_master_cleanup,\n};\nDEFINE_TTY_DRIVER(pty_master, &pty_master_ops, TTY_PSEUDO_MASTER_MAJOR, MAX_PTYS);\n\nconst struct tty_driver_ops pty_slave_ops = {\n    .init = pty_return_eio,\n    .open = pty_slave_open,\n    .close = pty_slave_close,\n    .write = pty_write,\n    .cleanup = pty_slave_cleanup,\n};\nDEFINE_TTY_DRIVER(pty_slave, &pty_slave_ops, TTY_PSEUDO_SLAVE_MAJOR, MAX_PTYS);\n\nstatic int pty_reserve_next() {\n    int pty_num;\n    lock(&ttys_lock);\n    for (pty_num = 0; pty_num < MAX_PTYS; pty_num++) {\n        if (pty_slave.ttys[pty_num] == NULL)\n            break;\n    }\n    pty_slave.ttys[pty_num] = (void *) 1; // anything non-null to reserve it\n    unlock(&ttys_lock);\n    return pty_num;\n}\n\nint ptmx_open(struct fd *fd) {\n    int pty_num = pty_reserve_next();\n    if (pty_num == MAX_PTYS)\n        return _ENOSPC;\n    struct tty *master = tty_get(&pty_master, TTY_PSEUDO_MASTER_MAJOR, pty_num);\n    if (IS_ERR(master))\n        return PTR_ERR(master);\n    return tty_open(master, fd);\n}\n\nstruct tty *pty_open_fake(struct tty_driver *driver) {\n    int pty_num = pty_reserve_next();\n    if (pty_num == MAX_PTYS)\n        return ERR_PTR(_ENOSPC);\n    // TODO this is a bit of a hack\n    driver->ttys = pty_slave.ttys;\n    driver->limit = pty_slave.limit;\n    driver->major = TTY_PSEUDO_SLAVE_MAJOR;\n    struct tty *tty = tty_get(driver, TTY_PSEUDO_SLAVE_MAJOR, pty_num);\n    if (IS_ERR(tty))\n        return tty;\n    pty_slave_init_inode(tty);\n    return tty;\n}\n\nstatic bool isdigits(const char *str) {\n    for (int i = 0; str[i] != '\\0'; i++)\n        if (!isdigit(str[i]))\n            return false;\n    return true;\n}\n\nstatic const struct fd_ops devpts_fdops;\n\nstatic bool devpts_pty_exists(int pty_num) {\n    if (pty_num < 0 || pty_num > MAX_PTYS)\n        return false;\n    lock(&ttys_lock);\n    bool exists = pty_slave.ttys[pty_num] != NULL;\n    unlock(&ttys_lock);\n    return exists;\n}\n\n// this has a slightly weird error returning convention\n// I'm lucky that ENOENT is -2 and not -1\nstatic int devpts_get_pty_num(const char *path) {\n    if (strcmp(path, \"\") == 0)\n        return -1; // root\n    if (path[0] != '/' || path[1] == '\\0' || strchr(path + 1, '/') != NULL)\n        return _ENOENT;\n\n    // there's one path component here, which had better be a pty number\n    const char *name = path + 1; // skip the initial /\n    if (!isdigits(name))\n        return _ENOENT;\n    // it's not possible to correctly use atoi\n    long pty_long = atol(name);\n    if (pty_long > INT_MAX)\n        return _ENOENT;\n    int pty_num = (int) pty_long;\n    if (!devpts_pty_exists(pty_num))\n        return _ENOENT;\n    return pty_num;\n}\n\nstatic struct fd *devpts_open(struct mount *UNUSED(mount), const char *path, int UNUSED(flags), int UNUSED(mode)) {\n    int pty_num = devpts_get_pty_num(path);\n    if (pty_num == _ENOENT)\n        return ERR_PTR(_ENOENT);\n    struct fd *fd = fd_create(&devpts_fdops);\n    fd->devpts.num = pty_num;\n    return fd;\n}\n\nstatic int devpts_getpath(struct fd *fd, char *buf) {\n    if (fd->devpts.num == -1)\n        strcpy(buf, \"\");\n    else\n        sprintf(buf, \"/%d\", fd->devpts.num);\n    return 0;\n}\n\nstatic void devpts_stat_num(int pty_num, struct statbuf *stat) {\n    if (pty_num == -1) {\n        // root\n        stat->mode = S_IFDIR | 0755;\n        stat->inode = 1;\n    } else {\n        lock(&ttys_lock);\n        struct tty *tty = pty_slave.ttys[pty_num];\n        assert(tty != NULL);\n        lock(&tty->lock);\n\n        stat->mode = S_IFCHR | tty->pty.perms;\n        stat->uid = tty->pty.uid;\n        stat->gid = tty->pty.gid;\n        stat->inode = pty_num + 3;\n        stat->rdev = dev_make(TTY_PSEUDO_SLAVE_MAJOR, pty_num);\n\n        unlock(&tty->lock);\n        unlock(&ttys_lock);\n    }\n}\n\nstatic int devpts_setattr_num(int pty_num, struct attr attr) {\n    if (pty_num == -1)\n        return _EROFS;\n    if (attr.type == attr_size)\n        return _EINVAL;\n\n    lock(&ttys_lock);\n    struct tty *tty = pty_slave.ttys[pty_num];\n    assert(tty != NULL);\n    lock(&tty->lock);\n\n    switch (attr.type) {\n        case attr_uid:\n            tty->pty.uid = attr.uid;\n            break;\n        case attr_gid:\n            tty->pty.gid = attr.gid;\n            break;\n        case attr_mode:\n            tty->pty.perms = attr.mode;\n            break;\n    }\n\n    unlock(&tty->lock);\n    unlock(&ttys_lock);\n    return 0;\n}\n\nstatic int devpts_fstat(struct fd *fd, struct statbuf *stat) {\n    devpts_stat_num(fd->devpts.num, stat);\n    return 0;\n}\n\nstatic int devpts_stat(struct mount *UNUSED(mount), const char *path, struct statbuf *stat) {\n    int pty_num = devpts_get_pty_num(path);\n    if (pty_num == _ENOENT)\n        return _ENOENT;\n    devpts_stat_num(pty_num, stat);\n    return 0;\n}\n\nstatic int devpts_setattr(struct mount *UNUSED(mount), const char *path, struct attr attr) {\n    int pty_num = devpts_get_pty_num(path);\n    if (pty_num == _ENOENT)\n        return _ENOENT;\n    devpts_setattr_num(pty_num, attr);\n    return 0;\n}\n\nstatic int devpts_fsetattr(struct fd *fd, struct attr attr) {\n    devpts_setattr_num(fd->devpts.num, attr);\n    return 0;\n}\n\nstatic int devpts_readdir(struct fd *fd, struct dir_entry *entry) {\n    assert(fd->devpts.num == -1); // there shouldn't be anything to list but the root\n\n    int pty_num = fd->offset;\n    while (pty_num < MAX_PTYS && !devpts_pty_exists(pty_num))\n        pty_num++;\n    if (pty_num >= MAX_PTYS)\n        return 0;\n    fd->offset = pty_num + 1;\n    sprintf(entry->name, \"%d\", pty_num);\n    entry->inode = pty_num + 3;\n    return 1;\n}\n\nconst struct fs_ops devptsfs = {\n    .name = \"devpts\", .magic = 0x1cd1,\n    .open = devpts_open,\n    .getpath = devpts_getpath,\n    .stat = devpts_stat,\n    .fstat = devpts_fstat,\n    .setattr = devpts_setattr,\n    .fsetattr = devpts_fsetattr,\n};\n\nstatic const struct fd_ops devpts_fdops = {\n    .readdir = devpts_readdir,\n};\n"
  },
  {
    "path": "fs/real.c",
    "content": "#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <dirent.h>\n#include <sys/stat.h>\n#include <sys/ioctl.h>\n#include <termios.h>\n#include <sys/mman.h>\n#include <sys/xattr.h>\n#include <sys/file.h>\n#include <sys/statvfs.h>\n#include <poll.h>\n\n#include \"debug.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/fs.h\"\n#include \"fs/dev.h\"\n#include \"fs/real.h\"\n#include \"fs/tty.h\"\n#include \"util/fchdir.h\"\n\nstatic int getpath(int fd, char *buf) {\n#if defined(__linux__)\n    char proc_fd[20];\n    sprintf(proc_fd, \"/proc/self/fd/%d\", fd);\n    ssize_t size = readlink(proc_fd, buf, MAX_PATH - 1);\n    if (size >= 0)\n        buf[size] = '\\0';\n    return size;\n#elif defined(__APPLE__)\n    return fcntl(fd, F_GETPATH, buf);\n#endif\n}\n\nstatic int open_flags_real_from_fake(int flags) {\n    int real_flags = 0;\n    if (flags & O_RDONLY_) real_flags |= O_RDONLY;\n    if (flags & O_WRONLY_) real_flags |= O_WRONLY;\n    if (flags & O_RDWR_) real_flags |= O_RDWR;\n    if (flags & O_CREAT_) real_flags |= O_CREAT;\n    if (flags & O_EXCL_) real_flags |= O_EXCL;\n    if (flags & O_TRUNC_) real_flags |= O_TRUNC;\n    if (flags & O_APPEND_) real_flags |= O_APPEND;\n    if (flags & O_NONBLOCK_) real_flags |= O_NONBLOCK;\n    return real_flags;\n}\n\nstatic int open_flags_fake_from_real(int flags) {\n    int fake_flags = 0;\n    if (flags & O_RDONLY) fake_flags |= O_RDONLY_;\n    if (flags & O_WRONLY) fake_flags |= O_WRONLY_;\n    if (flags & O_RDWR) fake_flags |= O_RDWR_;\n    if (flags & O_CREAT) fake_flags |= O_CREAT_;\n    if (flags & O_EXCL) fake_flags |= O_EXCL_;\n    if (flags & O_TRUNC) fake_flags |= O_TRUNC_;\n    if (flags & O_APPEND) fake_flags |= O_APPEND_;\n    if (flags & O_NONBLOCK) fake_flags |= O_NONBLOCK_;\n    return fake_flags;\n}\n\nstruct fd *realfs_open(struct mount *mount, const char *path, int flags, int mode) {\n    int real_flags = open_flags_real_from_fake(flags);\n    int fd_no = openat(mount->root_fd, fix_path(path), real_flags, mode);\n    if (fd_no < 0)\n        return ERR_PTR(errno_map());\n    struct fd *fd = fd_create(&realfs_fdops);\n    fd->real_fd = fd_no;\n    fd->dir = NULL;\n    return fd;\n}\n\nint realfs_close(struct fd *fd) {\n    if (fd->dir != NULL)\n        closedir(fd->dir);\n    int err = close(fd->real_fd);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nstatic void copy_stat(struct statbuf *fake_stat, struct stat *real_stat) {\n    fake_stat->dev = dev_fake_from_real(real_stat->st_dev);\n    fake_stat->inode = real_stat->st_ino;\n    fake_stat->mode = real_stat->st_mode;\n    fake_stat->nlink = real_stat->st_nlink;\n    fake_stat->uid = real_stat->st_uid;\n    fake_stat->gid = real_stat->st_gid;\n    fake_stat->rdev = dev_fake_from_real(real_stat->st_rdev);\n    fake_stat->size = real_stat->st_size;\n    fake_stat->blksize = real_stat->st_blksize;\n    fake_stat->blocks = real_stat->st_blocks;\n    fake_stat->atime = real_stat->st_atime;\n    fake_stat->mtime = real_stat->st_mtime;\n    fake_stat->ctime = real_stat->st_ctime;\n#if __APPLE__\n#define TIMESPEC(x) st_##x##timespec\n#elif __linux__\n#define TIMESPEC(x) st_##x##tim\n#endif\n    fake_stat->atime_nsec = real_stat->TIMESPEC(a).tv_nsec;\n    fake_stat->mtime_nsec = real_stat->TIMESPEC(m).tv_nsec;\n    fake_stat->ctime_nsec = real_stat->TIMESPEC(c).tv_nsec;\n#undef TIMESPEC\n}\n\nint realfs_stat(struct mount *mount, const char *path, struct statbuf *fake_stat) {\n    struct stat real_stat;\n    if (fstatat(mount->root_fd, fix_path(path), &real_stat, AT_SYMLINK_NOFOLLOW) < 0)\n        return errno_map();\n    copy_stat(fake_stat, &real_stat);\n    return 0;\n}\n\nint realfs_fstat(struct fd *fd, struct statbuf *fake_stat) {\n    struct stat real_stat;\n    if (fstat(fd->real_fd, &real_stat) < 0)\n        return errno_map();\n    copy_stat(fake_stat, &real_stat);\n    return 0;\n}\n\nssize_t realfs_read(struct fd *fd, void *buf, size_t bufsize) {\n    ssize_t res = read(fd->real_fd, buf, bufsize);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nssize_t realfs_write(struct fd *fd, const void *buf, size_t bufsize) {\n    ssize_t res = write(fd->real_fd, buf, bufsize);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nssize_t realfs_pread(struct fd *fd, void *buf, size_t bufsize, off_t off) {\n    ssize_t res = pread(fd->real_fd, buf, bufsize, off);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nssize_t realfs_pwrite(struct fd *fd, const void *buf, size_t bufsize, off_t off) {\n    ssize_t res = pwrite(fd->real_fd, buf, bufsize, off);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nvoid realfs_opendir(struct fd *fd) {\n    if (fd->dir == NULL) {\n        int dirfd = dup(fd->real_fd);\n        fd->dir = fdopendir(dirfd);\n        // this should never get called on a non-directory\n        assert(fd->dir != NULL);\n    }\n}\n\nint realfs_readdir(struct fd *fd, struct dir_entry *entry) {\n    realfs_opendir(fd);\n    errno = 0;\n    struct dirent *dirent = readdir(fd->dir);\n    if (dirent == NULL) {\n        if (errno != 0)\n            return errno_map();\n        else\n            return 0;\n    }\n    entry->inode = dirent->d_ino;\n    strcpy(entry->name, dirent->d_name);\n    return 1;\n}\n\nunsigned long realfs_telldir(struct fd *fd) {\n    realfs_opendir(fd);\n    return telldir(fd->dir);\n}\n\nvoid realfs_seekdir(struct fd *fd, unsigned long ptr) {\n    realfs_opendir(fd);\n    seekdir(fd->dir, ptr);\n}\n\noff_t realfs_lseek(struct fd *fd, off_t offset, int whence) {\n    if (fd->dir != NULL && whence == LSEEK_SET) {\n        realfs_seekdir(fd, offset);\n        return offset;\n    }\n\n    if (whence == LSEEK_SET)\n        whence = SEEK_SET;\n    else if (whence == LSEEK_CUR)\n        whence = SEEK_CUR;\n    else if (whence == LSEEK_END)\n        whence = SEEK_END;\n    else\n        return _EINVAL;\n    off_t res = lseek(fd->real_fd, offset, whence);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nint realfs_poll(struct fd *fd) {\n    struct pollfd p = {.fd = fd->real_fd, .events = POLLPRI};\n    // prevent POLLNVAL\n    int flags = fcntl(fd->real_fd, F_GETFL, 0);\n    if ((flags & O_ACCMODE) != O_WRONLY)\n        p.events |= POLLIN;\n    if ((flags & O_ACCMODE) != O_RDONLY)\n        p.events |= POLLOUT;\n    if (poll(&p, 1, 0) <= 0)\n        return 0;\n\n#if defined(__APPLE__)\n    // this is the \"WTF is apple smoking\" section\n\n    // https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/kern/sys_generic.c#L1856\n    if (p.revents & POLLHUP)\n        p.revents |= POLLOUT;\n    // apparently you can sometimes get POLLPRI on a pipe??? please ignore how much of a mess this condition is\n    if (is_adhoc_fd(fd) && S_ISFIFO(fd->stat.mode))\n        p.revents &= ~POLLPRI;\n\n    if (p.revents & POLLNVAL) {\n        printk(\"pollnval %d flags %d events %d revents %d\\n\", fd->real_fd, flags, p.events, p.revents);\n        // Seriously, fuck Darwin. I just want to poll on POLLIN|POLLOUT|POLLPRI.\n        // But if there's almost any kind of error, you just get POLLNVAL back,\n        // and no information about the bits that are in fact set. So ask for each\n        // separately and ignore a POLLNVAL.\n        // This is no longer atomic but I don't really know what to do about that.\n        int events = 0;\n        static const int pollbits[] = {POLLIN, POLLOUT, POLLPRI};\n        for (unsigned i = 0; i < sizeof(pollbits)/sizeof(pollbits[0]); i++) {\n            p.events = pollbits[i];\n            if (poll(&p, 1, 0) > 0 && !(p.revents & POLLNVAL))\n                events |= p.revents;\n        }\n        assert(!(events & POLLNVAL));\n        return events;\n    }\n#endif\n\n    assert(!(p.revents & POLLNVAL));\n    return p.revents;\n}\n\nint realfs_mmap(struct fd *fd, struct mem *mem, page_t start, pages_t pages, off_t offset, int prot, int flags) {\n    int mmap_flags = 0;\n    if (flags & MMAP_PRIVATE) mmap_flags |= MAP_PRIVATE;\n    if (flags & MMAP_SHARED) mmap_flags |= MAP_SHARED;\n    int mmap_prot = PROT_READ;\n    if (prot & P_WRITE) mmap_prot |= PROT_WRITE;\n\n    off_t real_offset = (offset / real_page_size) * real_page_size;\n    off_t correction = offset - real_offset;\n    char *memory = mmap(NULL, (pages * PAGE_SIZE) + correction,\n            mmap_prot, mmap_flags, fd->real_fd, real_offset);\n    return pt_map(mem, start, pages, memory, correction, prot);\n}\n\nssize_t realfs_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize) {\n    ssize_t size = readlinkat(mount->root_fd, fix_path(path), buf, bufsize);\n    if (size < 0)\n        return errno_map();\n    return size;\n}\n\nint realfs_getpath(struct fd *fd, char *buf) {\n    int err = getpath(fd->real_fd, buf);\n    if (err < 0)\n        return err;\n    if (strcmp(fd->mount->source, \"/\") != 0 || strcmp(buf, \"/\") == 0) {\n        size_t source_len = strlen(fd->mount->source);\n        memmove(buf, buf + source_len, MAX_PATH - source_len);\n    }\n    return 0;\n}\n\nint realfs_link(struct mount *mount, const char *src, const char *dst) {\n    int res = linkat(mount->root_fd, fix_path(src), mount->root_fd, fix_path(dst), 0);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nint realfs_unlink(struct mount *mount, const char *path) {\n    int res = unlinkat(mount->root_fd, fix_path(path), 0);\n    if (res < 0)\n        return errno_map();\n    return res;\n}\n\nint realfs_rmdir(struct mount *mount, const char *path) {\n    int err = unlinkat(mount->root_fd, fix_path(path), AT_REMOVEDIR);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nint realfs_rename(struct mount *mount, const char *src, const char *dst) {\n    int err = renameat(mount->root_fd, fix_path(src), mount->root_fd, fix_path(dst));\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nint realfs_symlink(struct mount *mount, const char *target, const char *link) {\n    int err = symlinkat(target, mount->root_fd, link);\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nint realfs_mknod(struct mount *mount, const char *path, mode_t_ mode, dev_t_ UNUSED(dev)) {\n    int err;\n    if (S_ISFIFO(mode)) {\n        lock_fchdir(mount->root_fd);\n        err = mkfifo(fix_path(path), mode & ~S_IFMT);\n        unlock_fchdir();\n    } else if (S_ISREG(mode)) {\n        err = openat(mount->root_fd, fix_path(path), O_CREAT|O_EXCL|O_RDONLY, mode & ~S_IFMT);\n        if (err >= 0)\n            err = close(err);\n    } else {\n        return _EPERM;\n    }\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nint realfs_truncate(struct mount *mount, const char *path, off_t_ size) {\n    int fd = openat(mount->root_fd, fix_path(path), O_RDWR);\n    if (fd < 0)\n        return errno_map();\n    int err = 0;\n    if (ftruncate(fd, size) < 0)\n        err = errno_map();\n    close(fd);\n    return err;\n}\n\nint realfs_setattr(struct mount *mount, const char *path, struct attr attr) {\n    path = fix_path(path);\n    int root = mount->root_fd;\n    int err;\n    switch (attr.type) {\n        case attr_uid:\n            err = fchownat(root, path, attr.uid, -1, 0);\n            break;\n        case attr_gid:\n            err = fchownat(root, path, attr.gid, -1, 0);\n            break;\n        case attr_mode:\n            err = fchmodat(root, path, attr.mode, 0);\n            break;\n        case attr_size:\n            return realfs_truncate(mount, path, attr.size);\n        default:\n            TODO(\"other attrs\");\n    }\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nint realfs_fsetattr(struct fd *fd, struct attr attr) {\n    int real_fd = fd->real_fd;\n    int err;\n    switch (attr.type) {\n        case attr_uid:\n            err = fchown(real_fd, attr.uid, -1);\n            break;\n        case attr_gid:\n            err = fchown(real_fd, attr.gid, -1);\n            break;\n        case attr_mode:\n            err = fchmod(real_fd, attr.mode);\n            break;\n        case attr_size:\n            err = ftruncate(real_fd, attr.size);\n            break;\n        default: abort();\n    }\n    if (err < 0)\n        return errno_map();\n    return err;\n}\n\nint realfs_utime(struct mount *mount, const char *path, struct timespec atime, struct timespec mtime) {\n    struct timespec times[2] = {atime, mtime};\n    int err = utimensat(mount->root_fd, fix_path(path), times, 0);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nint realfs_mkdir(struct mount *mount, const char *path, mode_t_ mode) {\n    int err = mkdirat(mount->root_fd, fix_path(path), mode);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nint realfs_flock(struct fd *fd, int operation) {\n    int real_op = 0;\n    if (operation & LOCK_SH_) real_op |= LOCK_SH;\n    if (operation & LOCK_EX_) real_op |= LOCK_EX;\n    if (operation & LOCK_UN_) real_op |= LOCK_UN;\n    if (operation & LOCK_NB_) real_op |= LOCK_NB;\n    return flock(fd->real_fd, real_op);\n}\n\nint realfs_statfs(struct mount *mount, struct statfsbuf *stat) {\n    struct statvfs vfs = {};\n    fstatvfs(mount->root_fd, &vfs);\n    stat->bsize = vfs.f_bsize;\n    stat->blocks = vfs.f_blocks;\n    stat->bfree = vfs.f_bfree;\n    stat->bavail = vfs.f_bavail;\n    stat->files = vfs.f_files;\n    stat->ffree = vfs.f_ffree;\n    stat->namelen = vfs.f_namemax;\n    stat->frsize = vfs.f_frsize;\n    return 0;\n}\n\nint realfs_mount(struct mount *mount) {\n    char *source_realpath = realpath(mount->source, NULL);\n    if (source_realpath == NULL)\n        return errno_map();\n    free((void *) mount->source);\n    mount->source = source_realpath;\n\n    mount->root_fd = open(mount->source, O_DIRECTORY);\n    if (mount->root_fd < 0)\n        return errno_map();\n    return 0;\n}\n\nint realfs_fsync(struct fd *fd) {\n    int err = fsync(fd->real_fd);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nint realfs_getflags(struct fd *fd) {\n    int flags = fcntl(fd->real_fd, F_GETFL);\n    if (flags < 0)\n        return errno_map();\n    return open_flags_fake_from_real(flags);\n}\n\nint realfs_setflags(struct fd *fd, dword_t flags) {\n    int ret = fcntl(fd->real_fd, F_SETFL, open_flags_real_from_fake(flags));\n    if (ret < 0)\n        return errno_map();\n    return 0;\n}\n\nssize_t realfs_ioctl_size(int cmd) {\n    if (cmd == FIONREAD_)\n        return sizeof(dword_t);\n    return -1;\n}\n\nint realfs_ioctl(struct fd *fd, int cmd, void *arg) {\n    int err;\n    size_t nread;\n    switch (cmd) {\n        case FIONREAD_:\n            err = ioctl(fd->real_fd, FIONREAD, &nread);\n            if (err < 0)\n                return errno_map();\n            *(dword_t *) arg = nread;\n            return 0;\n    }\n    return _ENOTTY;\n}\n\nconst struct fs_ops realfs = {\n    .name = \"real\", .magic = 0x7265616c,\n    .mount = realfs_mount,\n    .statfs = realfs_statfs,\n\n    .open = realfs_open,\n    .readlink = realfs_readlink,\n    .link = realfs_link,\n    .unlink = realfs_unlink,\n    .rmdir = realfs_rmdir,\n    .rename = realfs_rename,\n    .symlink = realfs_symlink,\n    .mknod = realfs_mknod,\n\n    .close = realfs_close,\n    .stat = realfs_stat,\n    .fstat = realfs_fstat,\n    .setattr = realfs_setattr,\n    .fsetattr = realfs_fsetattr,\n    .utime = realfs_utime,\n    .getpath = realfs_getpath,\n    .flock = realfs_flock,\n\n    .mkdir = realfs_mkdir,\n};\n\nconst struct fd_ops realfs_fdops = {\n    .read = realfs_read,\n    .write = realfs_write,\n    .pread = realfs_pread,\n    .pwrite = realfs_pwrite,\n    .readdir = realfs_readdir,\n    .telldir = realfs_telldir,\n    .seekdir = realfs_seekdir,\n    .lseek = realfs_lseek,\n    .mmap = realfs_mmap,\n    .poll = realfs_poll,\n    .ioctl_size = realfs_ioctl_size,\n    .ioctl = realfs_ioctl,\n    .fsync = realfs_fsync,\n    .close = realfs_close,\n    .getflags = realfs_getflags,\n    .setflags = realfs_setflags,\n};\n"
  },
  {
    "path": "fs/real.h",
    "content": "#ifndef FS_REAL_H\n#define FS_REAL_H\n\n#include \"kernel/fs.h\"\n\nextern const struct fd_ops realfs_fdops;\nextern const struct fs_ops realfs;\n\nstruct fd *realfs_open(struct mount *mount, const char *path, int flags, int mode);\n\nssize_t realfs_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize);\nint realfs_link(struct mount *mount, const char *src, const char *dst);\nint realfs_unlink(struct mount *mount, const char *path);\nint realfs_rmdir(struct mount *mount, const char *path);\nint realfs_rename(struct mount *mount, const char *src, const char *dst);\nint realfs_symlink(struct mount *mount, const char *target, const char *link);\nint realfs_mknod(struct mount *mount, const char *path, mode_t_ mode, dev_t_ UNUSED(dev));\n\nint realfs_stat(struct mount *mount, const char *path, struct statbuf *fake_stat);\nint realfs_statfs(struct mount *mount, struct statfsbuf *stat);\nint realfs_fstat(struct fd *fd, struct statbuf *fake_stat);\nint realfs_setattr(struct mount *mount, const char *path, struct attr attr);\nint realfs_fsetattr(struct fd *fd, struct attr attr);\n\nint realfs_mkdir(struct mount *mount, const char *path, mode_t_ mode);\n\nint realfs_truncate(struct mount *mount, const char *path, off_t_ size);\nint realfs_utime(struct mount *mount, const char *path, struct timespec atime, struct timespec mtime);\n\nint realfs_statfs(struct mount *mount, struct statfsbuf *stat);\nint realfs_flock(struct fd *fd, int operation);\nint realfs_getpath(struct fd *fd, char *buf);\nssize_t realfs_read(struct fd *fd, void *buf, size_t bufsize);\nssize_t realfs_write(struct fd *fd, const void *buf, size_t bufsize);\n\nint realfs_readdir(struct fd *fd, struct dir_entry *entry);\nunsigned long realfs_telldir(struct fd *fd);\nvoid realfs_seekdir(struct fd *fd, unsigned long ptr);\n\noff_t realfs_lseek(struct fd *fd, off_t offset, int whence);\n\nint realfs_poll(struct fd *fd);\nint realfs_mmap(struct fd *fd, struct mem *mem, page_t start, pages_t pages, off_t offset, int prot, int flags);\nint realfs_fsync(struct fd *fd);\nint realfs_getflags(struct fd *fd);\nint realfs_setflags(struct fd *fd, dword_t arg);\nssize_t realfs_ioctl_size(int cmd);\nint realfs_ioctl(struct fd *fd, int cmd, void *arg);\nint realfs_close(struct fd *fd);\n\n#endif\n"
  },
  {
    "path": "fs/sock.c",
    "content": "#include <fcntl.h>\n#include <netinet/tcp.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/un.h>\n#include \"kernel/calls.h\"\n#include \"fs/fd.h\"\n#include \"fs/inode.h\"\n#include \"fs/path.h\"\n#include \"fs/real.h\"\n#include \"fs/sock.h\"\n#include \"debug.h\"\n\n#define SOCKET_TYPE_MASK 0xf\n\nconst struct fd_ops socket_fdops;\n\nstatic lock_t peer_lock = LOCK_INITIALIZER;\n\nstatic fd_t sock_fd_create(int sock_fd, int domain, int type, int protocol) {\n    struct fd *fd = adhoc_fd_create(&socket_fdops);\n    if (fd == NULL)\n        return _ENOMEM;\n    fd->stat.mode = S_IFSOCK | 0666;\n    fd->real_fd = sock_fd;\n    fd->socket.domain = domain;\n    fd->socket.type = type & SOCKET_TYPE_MASK;\n    fd->socket.protocol = protocol;\n    if (domain == AF_LOCAL_) {\n        cond_init(&fd->socket.unix_got_peer);\n        list_init(&fd->socket.unix_scm);\n    }\n    return f_install(fd, type & ~SOCKET_TYPE_MASK);\n}\n\nint_t sys_socket(dword_t domain, dword_t type, dword_t protocol) {\n    STRACE(\"socket(%d, %d, %d)\", domain, type, protocol);\n    int real_domain = sock_family_to_real(domain);\n    if (real_domain < 0)\n        return _EINVAL;\n    int real_type = sock_type_to_real(type, protocol);\n    if (real_type < 0)\n        return _EINVAL;\n\n    // this hack makes mtr work\n    if (type == SOCK_RAW_ && protocol == IPPROTO_RAW)\n        protocol = IPPROTO_ICMP;\n\n    int sock = socket(real_domain, real_type, protocol);\n    if (sock < 0)\n        return errno_map();\n\n#ifdef __APPLE__\n    if (domain == AF_INET_ && type == SOCK_DGRAM_) {\n        // in some cases, such as ICMP, datagram sockets on mac can default to\n        // including the IP header like raw sockets\n        int one = 1;\n        setsockopt(sock, IPPROTO_IP, IP_STRIPHDR, &one, sizeof(one));\n    }\n#endif\n\n    fd_t f = sock_fd_create(sock, domain, type, protocol);\n    if (f < 0)\n        close(sock);\n    return f;\n}\n\nstatic void inode_release_if_exist(struct inode_data *inode) {\n    if (inode != NULL)\n        inode_release(inode);\n}\n\nstatic struct fd *sock_getfd(fd_t sock_fd) {\n    struct fd *sock = f_get(sock_fd);\n    if (sock == NULL || sock->ops != &socket_fdops)\n        return NULL;\n    return sock;\n}\n\nstatic uint32_t unix_socket_next_id() {\n    static uint32_t next_id = 0;\n    static lock_t next_id_lock = LOCK_INITIALIZER;\n    lock(&next_id_lock);\n    uint32_t id = ++next_id;\n    unlock(&next_id_lock);\n    return id;\n}\n\nstatic int unix_socket_get(const char *path_raw, struct fd *bind_fd, uint32_t *socket_id) {\n    char path[MAX_PATH];\n    int err = path_normalize(AT_PWD, path_raw, path, N_SYMLINK_FOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    struct statbuf stat;\n    err = mount->fs->stat(mount, path, &stat);\n\n    // If bind was called, there are some funny semantics.\n    if (bind_fd != NULL) {\n        // If the file exists, fail.\n        if (err == 0) {\n            err = _EADDRINUSE;\n            goto out;\n        }\n        // If the file can't be found, try to create it as a socket.\n        if (err < 0) {\n            mode_t_ mode = 0777;\n            struct fs_info *fs = current->fs;\n            lock(&fs->lock);\n            mode &= ~fs->umask;\n            unlock(&fs->lock);\n            err = mount->fs->mknod(mount, path, S_IFSOCK | mode, 0);\n            if (err < 0)\n                goto out;\n            err = mount->fs->stat(mount, path, &stat);\n            if (err < 0)\n                goto out;\n        }\n    }\n\n    // If something other than bind was called, just do the obvious thing and\n    // fail if stat failed.\n    if (bind_fd == NULL && err < 0)\n        goto out;\n\n    if (!S_ISSOCK(stat.mode)) {\n        err = _ENOTSOCK;\n        goto out;\n    }\n\n    // Look up the socket ID for the inode number.\n    struct inode_data *inode = inode_get(mount, stat.inode);\n    lock(&inode->lock);\n    if (inode->socket_id == 0)\n        inode->socket_id = unix_socket_next_id();\n    unlock(&inode->lock);\n    *socket_id = inode->socket_id;\n\n    mount_release(mount);\n    if (bind_fd != NULL)\n        bind_fd->socket.unix_name_inode = inode;\n    else\n        inode_release(inode);\n    return 0;\n\nout:\n    mount_release(mount);\n    return err;\n}\n\n// Dan Bernstein's simple and decently effective hash function\nstatic uint32_t str_hash(const char *str) {\n    uint32_t hash = 5381;\n    for (int i = 0; str[i] != '\\0'; i++) {\n        hash = 33 * hash ^ str[i];\n    }\n    return hash;\n}\n\n// The abstract socket namespace is a lot simpler than it sounds: if the first\n// byte of the path is a null byte, then it gets looked up in this hashtable\n// instead of the filesystem.\n\nstruct unix_abstract {\n    unsigned refcount;\n    uint32_t hash;\n    uint32_t socket_id;\n    struct list links;\n};\n#define ABSTRACT_HASH_SIZE 1024\nstatic struct list abstract_hash[ABSTRACT_HASH_SIZE];\nstatic lock_t unix_abstract_lock = LOCK_INITIALIZER;\n\nstatic int unix_abstract_get(const char *name, struct fd *bind_fd, uint32_t *socket_id) {\n    uint32_t hash = str_hash(name);\n    lock(&unix_abstract_lock);\n    struct unix_abstract *sock_tmp;\n    struct unix_abstract *sock = NULL;\n    struct list *bucket = &abstract_hash[hash % ABSTRACT_HASH_SIZE];\n    if (list_null(bucket))\n        list_init(bucket);\n    list_for_each_entry(bucket, sock_tmp, links) {\n        if (sock_tmp->hash == hash) {\n            sock = sock_tmp;\n            break;\n        }\n    }\n\n    if (bind_fd != NULL && sock != NULL) {\n        unlock(&unix_abstract_lock);\n        return _EEXIST;\n    }\n    if (bind_fd == NULL && sock == NULL) {\n        unlock(&unix_abstract_lock);\n        return _ENOENT;\n    }\n\n    if (sock == NULL) {\n        sock = malloc(sizeof(struct unix_abstract));\n        sock->refcount = 0;\n        sock->hash = hash;\n        sock->socket_id = unix_socket_next_id();\n        list_add(bucket, &sock->links);\n    }\n\n    sock->refcount++;\n    unlock(&unix_abstract_lock);\n    *socket_id = sock->socket_id;\n    if (bind_fd != NULL)\n        bind_fd->socket.unix_name_abstract = sock;\n    return 0;\n}\n\nstatic void unix_abstract_release(struct unix_abstract *name) {\n    lock(&unix_abstract_lock);\n    if (--name->refcount == 0) {\n        list_remove(&name->links);\n        free(name);\n    }\n    unlock(&unix_abstract_lock);\n}\n\nconst char *sock_tmp_prefix = \"/tmp/ishsock\";\n\nstatic int sockaddr_read_bind(addr_t sockaddr_addr, void *sockaddr, uint_t *sockaddr_len, struct fd *bind_fd) {\n    // Make sure we can read things without overflowing buffers\n    if (*sockaddr_len < 2)\n        return _EINVAL;\n    if (*sockaddr_len > sizeof(struct sockaddr_max_))\n        return _EINVAL;\n\n    if (user_read(sockaddr_addr, sockaddr, *sockaddr_len))\n        return _EFAULT;\n    struct sockaddr *real_addr = sockaddr;\n    struct sockaddr_ *fake_addr = sockaddr;\n    real_addr->sa_family = sock_family_to_real(fake_addr->family);\n\n    switch (real_addr->sa_family) {\n        case PF_INET:\n            if (*sockaddr_len < sizeof(struct sockaddr_in))\n                return _EINVAL;\n            break;\n        case PF_INET6:\n            if (*sockaddr_len < sizeof(struct sockaddr_in6))\n                return _EINVAL;\n            break;\n\n        case PF_LOCAL: {\n            // First pull out the path, being careful to not overflow anything.\n            char path[SOCKADDR_DATA_MAX + 1];\n            size_t path_size = *sockaddr_len - offsetof(struct sockaddr_, data);\n            memcpy(path, fake_addr->data, path_size);\n            path[path_size] = '\\0';\n\n            uint32_t socket_id;\n            int err;\n            if (path_size == 0) {\n                return _ENOENT;\n            } else if (path[0] != '\\0') {\n                STRACE(\" unix socket %s\", path);\n                err = unix_socket_get(path, bind_fd, &socket_id);\n            } else {\n                STRACE(\" unix abstract socket %s\", path + 1);\n                err = unix_abstract_get(path + 1, bind_fd, &socket_id);\n            }\n            if (err < 0)\n                return err;\n            if (bind_fd != NULL) {\n                bind_fd->socket.unix_name_len = path_size;\n                memcpy(bind_fd->socket.unix_name, path, path_size);\n            }\n\n            struct sockaddr_un *real_addr_un = sockaddr;\n            size_t path_len = sprintf(real_addr_un->sun_path, \"%s%d.%u\", sock_tmp_prefix, getpid(), socket_id);\n            // The call to real bind will fail if the backing socket already\n            // exists from a previous run or something. We already checked that\n            // the fake file doesn't exist in unix_socket_get, so try a simple\n            // solution.\n            if (bind_fd != NULL)\n                unlink(real_addr_un->sun_path);\n            *sockaddr_len = offsetof(struct sockaddr_un, sun_path) + path_len;\n            break;\n        }\n        default:\n            return _EINVAL;\n    }\n    return 0;\n}\n\nstatic int sockaddr_read(addr_t sockaddr_addr, void *sockaddr, uint_t *sockaddr_len) {\n    struct inode_data *inode = NULL;\n    int err = sockaddr_read_bind(sockaddr_addr, sockaddr, sockaddr_len, NULL);\n    inode_release_if_exist(inode);\n    return err;\n}\n\nstatic int sockaddr_write(addr_t sockaddr_addr, void *sockaddr, uint_t buffer_len, uint_t *sockaddr_len) {\n    struct sockaddr *real_addr = sockaddr;\n    struct sockaddr_ *fake_addr = sockaddr;\n    fake_addr->family = sock_family_from_real(real_addr->sa_family);\n    switch (fake_addr->family) {\n        case PF_LOCAL_: {\n            // Most callers of sockaddr_write use it to return a peer name, and\n            // since we don't know the peer name in this case, just return the\n            // default peer name, which is the null address.\n            static struct sockaddr_ unix_domain_null = {.family = PF_LOCAL_};\n            sockaddr = &unix_domain_null;\n            *sockaddr_len = sizeof(unix_domain_null);\n            break;\n        }\n        case PF_INET_:\n        case PF_INET6_:\n            break;\n        default:\n            return _EINVAL;\n    }\n\n    if (buffer_len > *sockaddr_len)\n        buffer_len = *sockaddr_len;\n    // The address is supposed to be truncated if the specified length is too\n    // short, instead of returning an error.\n    if (user_write(sockaddr_addr, sockaddr, buffer_len))\n        return _EFAULT;\n    return 0;\n}\n\nint_t sys_bind(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len) {\n    STRACE(\"bind(%d, 0x%x, %d)\", sock_fd, sockaddr_addr, sockaddr_len);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    struct sockaddr_max_ sockaddr;\n    struct inode_data *inode = NULL;\n    int err = sockaddr_read_bind(sockaddr_addr, &sockaddr, &sockaddr_len, sock);\n    if (err < 0)\n        return err;\n\n    err = bind(sock->real_fd, (void *) &sockaddr, sockaddr_len);\n    if (err < 0) {\n        inode_release_if_exist(sock->socket.unix_name_inode);\n        if (sock->socket.unix_name_abstract != NULL)\n            unix_abstract_release(sock->socket.unix_name_abstract);\n        return errno_map();\n    }\n    sock->socket.unix_name_inode = inode;\n    return 0;\n}\n\nstatic void fill_cred(struct ucred_ *cred) {\n    cred->pid = current->pid;\n    cred->uid = current->euid;\n    cred->gid = current->egid;\n}\n\nint_t sys_connect(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len) {\n    STRACE(\"connect(%d, 0x%x, %d)\", sock_fd, sockaddr_addr, sockaddr_len);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    struct sockaddr_max_ sockaddr;\n    int err = sockaddr_read(sockaddr_addr, &sockaddr, &sockaddr_len);\n    if (err < 0)\n        return err;\n\n    err = connect(sock->real_fd, (void *) &sockaddr, sockaddr_len);\n    if (err < 0)\n        return errno_map();\n\n    if (sock->socket.domain == AF_LOCAL_) {\n        fill_cred(&sock->socket.unix_cred);\n        assert(sock->socket.unix_peer == NULL);\n        // Send a pointer to ourselves to the other end so they can set up the peer pointers.\n        ssize_t res = write(sock->real_fd, &sock, sizeof(struct fd *));\n        if (res == sizeof(struct fd *)) {\n            // Wait for acknowledgement that it happened.\n            lock(&peer_lock);\n            while (sock->socket.unix_peer == NULL)\n                wait_for_ignore_signals(&sock->socket.unix_got_peer, &peer_lock, NULL);\n            unlock(&peer_lock);\n        }\n    }\n\n    return err;\n}\n\nint_t sys_listen(fd_t sock_fd, int_t backlog) {\n    STRACE(\"listen(%d, %d)\", sock_fd, backlog);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    int err = listen(sock->real_fd, backlog);\n    if (err < 0)\n        return errno_map();\n    sockrestart_begin_listen(sock);\n    return err;\n}\n\nint_t sys_accept(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr) {\n    STRACE(\"accept(%d, 0x%x, 0x%x)\", sock_fd, sockaddr_addr, sockaddr_len_addr);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    dword_t sockaddr_len = 0;\n    if (sockaddr_addr != 0) {\n        if (user_get(sockaddr_len_addr, sockaddr_len))\n            return _EFAULT;\n    }\n\n    char sockaddr[sockaddr_len];\n    int client;\n    do {\n        sockrestart_begin_listen_wait(sock);\n        errno = 0;\n        client = accept(sock->real_fd,\n                sockaddr_addr != 0 ? (void *) sockaddr : NULL,\n                sockaddr_addr != 0 ? &sockaddr_len : NULL);\n        sockrestart_end_listen_wait(sock);\n    } while (sockrestart_should_restart_listen_wait() && errno == EINTR);\n    if (client < 0)\n        return errno_map();\n\n    if (sockaddr_addr != 0) {\n        int err = sockaddr_write(sockaddr_addr, sockaddr, sizeof(sockaddr), &sockaddr_len);\n        if (err < 0)\n            return client;\n        if (user_put(sockaddr_len_addr, sockaddr_len))\n            return _EFAULT;\n    }\n\n    fd_t client_f = sock_fd_create(client,\n            sock->socket.domain, sock->socket.type, sock->socket.protocol);\n    if (client_f < 0)\n        close(client);\n\n    if (sock->socket.domain == AF_LOCAL_) {\n        lock(&peer_lock);\n        struct fd *client_fd = f_get(client_f);\n        fill_cred(&client_fd->socket.unix_cred);\n        struct fd *peer;\n        ssize_t res = read(client, &peer, sizeof(peer));\n        if (res == sizeof(peer)) {\n            client_fd->socket.unix_peer = peer;\n            peer->socket.unix_peer = client_fd;\n            notify(&peer->socket.unix_got_peer);\n        }\n        unlock(&peer_lock);\n    }\n\n    return client_f;\n}\n\nstatic void copy_unix_name(char *sockaddr, dword_t *sockaddr_len, struct fd *sock) {\n    struct sockaddr_ *fake_addr = (void *) sockaddr;\n    fake_addr->family = PF_LOCAL_;\n\n    size_t data_len = *sockaddr_len - offsetof(struct sockaddr_, data);\n    size_t name_len = sock->socket.unix_name_len;\n    if (name_len > data_len)\n        name_len = data_len;\n    memset(fake_addr->data, 0, data_len);\n    memcpy(fake_addr->data, sock->socket.unix_name, name_len);\n    *sockaddr_len = offsetof(struct sockaddr_, data) + name_len;\n}\n\nint_t sys_getsockname(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr) {\n    STRACE(\"getsockname(%d, 0x%x, 0x%x)\", sock_fd, sockaddr_addr, sockaddr_len_addr);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    dword_t sockaddr_len;\n    if (user_get(sockaddr_len_addr, sockaddr_len))\n        return _EFAULT;\n    char sockaddr[sockaddr_len];\n\n    // if this is a unix socket, return the same string passed to bind\n    if (sock->socket.domain == PF_LOCAL_) {\n        copy_unix_name(sockaddr, &sockaddr_len, sock);\n        if (user_write(sockaddr_addr, sockaddr, sizeof(sockaddr)))\n            return _EFAULT;\n        if (user_put(sockaddr_len_addr, sockaddr_len))\n            return _EFAULT;\n        return 0;\n    }\n\n    int res = getsockname(sock->real_fd, (void *) sockaddr, &sockaddr_len);\n    if (res < 0)\n        return errno_map();\n\n    int err = sockaddr_write(sockaddr_addr, sockaddr, sizeof(sockaddr), &sockaddr_len);\n    if (err < 0)\n        return err;\n    if (user_put(sockaddr_len_addr, sockaddr_len))\n        return _EFAULT;\n    return res;\n}\n\nint_t sys_getpeername(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr) {\n    STRACE(\"getpeername(%d, 0x%x, 0x%x)\", sock_fd, sockaddr_addr, sockaddr_len_addr);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    dword_t sockaddr_len;\n    if (user_get(sockaddr_len_addr, sockaddr_len))\n        return _EFAULT;\n\n    // TODO if this is a unix socket, return the same string the peer passed to\n    // bind once the peer pointer is available\n\n    char sockaddr[sockaddr_len];\n    int res = getpeername(sock->real_fd, (void *) sockaddr, &sockaddr_len);\n    if (res < 0)\n        return errno_map();\n\n    int err = sockaddr_write(sockaddr_addr, sockaddr, sizeof(sockaddr), &sockaddr_len);\n    if (err < 0)\n        return err;\n    if (user_put(sockaddr_len_addr, sockaddr_len))\n        return _EFAULT;\n    return res;\n}\n\nint_t sys_socketpair(dword_t domain, dword_t type, dword_t protocol, addr_t sockets_addr) {\n    STRACE(\"socketpair(%d, %d, %d, 0x%x)\", domain, type, protocol, sockets_addr);\n    int real_domain = sock_family_to_real(domain);\n    if (real_domain < 0)\n        return _EINVAL;\n    int real_type = sock_type_to_real(type, protocol);\n    if (real_type < 0)\n        return _EINVAL;\n\n    int sockets[2];\n    int err = socketpair(domain, type, protocol, sockets);\n    if (err < 0)\n        return errno_map();\n\n    lock(&peer_lock);\n    int fake_sockets[2];\n    err = fake_sockets[0] = sock_fd_create(sockets[0], domain, type, protocol);\n    if (fake_sockets[0] < 0) {\n        unlock(&peer_lock);\n        goto close_sockets;\n    }\n    err = fake_sockets[1] = sock_fd_create(sockets[1], domain, type, protocol);\n    if (fake_sockets[1] < 0) {\n        unlock(&peer_lock);\n        goto close_fake_0;\n    }\n    struct fd *sock1 = f_get(fake_sockets[0]);\n    struct fd *sock2 = f_get(fake_sockets[1]);\n    sock1->socket.unix_peer = sock2;\n    sock2->socket.unix_peer = sock1;\n    unlock(&peer_lock);\n\n    err = _EFAULT;\n    if (user_put(sockets_addr, fake_sockets))\n        goto close_fake_1;\n\n    STRACE(\" [%d, %d]\", fake_sockets[0], fake_sockets[1]);\n    return 0;\n\nclose_fake_1:\n    sys_close(fake_sockets[1]);\nclose_fake_0:\n    sys_close(fake_sockets[0]);\nclose_sockets:\n    close(sockets[0]);\n    close(sockets[1]);\n    return err;\n}\n\nint_t sys_sendto(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, addr_t sockaddr_addr, dword_t sockaddr_len) {\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    char *buffer = malloc(len + 1);\n    if (user_read(buffer_addr, buffer, len))\n        return _EFAULT;\n    buffer[len] = '\\0';\n    STRACE(\"sendto(%d, \\\"%.100s\\\", %d, %d, 0x%x, %d)\", sock_fd, buffer, len, flags, sockaddr_addr, sockaddr_len);\n    int real_flags = sock_flags_to_real(flags);\n    int err = _EINVAL;\n    if (real_flags < 0)\n        goto error;\n    struct sockaddr_max_ sockaddr;\n    if (sockaddr_addr) {\n        err = sockaddr_read(sockaddr_addr, &sockaddr, &sockaddr_len);\n        if (err < 0)\n            goto error;\n    }\n\n    ssize_t res = sendto(sock->real_fd, buffer, len, real_flags,\n            sockaddr_addr ? (void *) &sockaddr : NULL, sockaddr_len);\n    free(buffer);\n    if (res < 0)\n        return errno_map();\n    return res;\n\nerror:\n    free(buffer);\n    return err;\n}\n\nint_t sys_recvfrom(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, addr_t sockaddr_addr, addr_t sockaddr_len_addr) {\n    STRACE(\"recvfrom(%d, 0x%x, %d, %d, 0x%x, 0x%x)\", sock_fd, buffer_addr, len, flags, sockaddr_addr, sockaddr_len_addr);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    int real_flags = sock_flags_to_real(flags);\n    if (real_flags < 0)\n        return _EINVAL;\n    uint_t sockaddr_len = 0;\n    if (sockaddr_len_addr != 0)\n        if (user_get(sockaddr_len_addr, sockaddr_len))\n            return _EFAULT;\n\n    char *buffer = malloc(len);\n    char sockaddr[sockaddr_len];\n    ssize_t res = recvfrom(sock->real_fd, buffer, len, real_flags,\n            sockaddr_addr != 0 ? (void *) sockaddr : NULL,\n            sockaddr_len_addr != 0 ? &sockaddr_len : NULL);\n    if (res < 0) {\n        free(buffer);\n        return errno_map();\n    }\n\n    if (user_write(buffer_addr, buffer, len)) {\n        free(buffer);\n        return _EFAULT;\n    }\n    free(buffer);\n    if (sockaddr_addr != 0) {\n        int err = sockaddr_write(sockaddr_addr, sockaddr, sizeof(sockaddr), &sockaddr_len);\n        if (err < 0)\n            return err;\n    }\n    if (sockaddr_len_addr != 0)\n        if (user_put(sockaddr_len_addr, sockaddr_len))\n            return _EFAULT;\n    return res;\n}\n\nint_t sys_send(fd_t sock_fd, addr_t buf, dword_t len, int_t flags) {\n    return sys_sendto(sock_fd, buf, len, flags, 0, 0);\n}\n\nint_t sys_recv(fd_t sock_fd, addr_t buf, dword_t len, int_t flags) {\n    return sys_recvfrom(sock_fd, buf, len, flags, 0, 0);\n}\n\nint_t sys_shutdown(fd_t sock_fd, dword_t how) {\n    STRACE(\"shutdown(%d, %d)\", sock_fd, how);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    int err = shutdown(sock->real_fd, how);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\n#define DEFAULT_TCP_CONGESTION \"cubic\"\n\nint_t sys_setsockopt(fd_t sock_fd, dword_t level, dword_t option, addr_t value_addr, dword_t value_len) {\n    STRACE(\"setsockopt(%d, %d, %d, 0x%x, %d)\", sock_fd, level, option, value_addr, value_len);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    char value[value_len];\n    if (user_read(value_addr, value, value_len))\n        return _EFAULT;\n\n    // ICMP6_FILTER can only be set on real SOCK_RAW\n    if (level == IPPROTO_ICMPV6 && option == ICMP6_FILTER_)\n        return 0;\n    // IP_MTU_DISCOVER has no equivalent on Darwin\n    if (level == IPPROTO_IP && option == IP_MTU_DISCOVER_)\n        return 0;\n    // TCP_CONGESTION also has no equivalent on Darwin\n#if defined(__APPLE__)\n    if (level == IPPROTO_TCP && option == TCP_CONGESTION_) {\n        if (strncmp(value, DEFAULT_TCP_CONGESTION, sizeof(value)) == 0)\n            return 0;\n        return _ENOENT;\n    }\n#endif\n\n    int real_opt = sock_opt_to_real(option, level);\n    if (real_opt < 0)\n        return _EINVAL;\n    int real_level = sock_level_to_real(level);\n    if (real_level < 0)\n        return _EINVAL;\n\n    // 0 means the option is not implemented, but things rely on it, so we\n    // should just ignore attempts to set it.\n    if (real_opt == 0)\n        return 0;\n\n    int err = setsockopt(sock->real_fd, real_level, real_opt, value, value_len);\n    if (err < 0)\n        return errno_map();\n    return 0;\n}\n\nint_t sys_getsockopt(fd_t sock_fd, dword_t level, dword_t option, addr_t value_addr, dword_t len_addr) {\n    STRACE(\"getsockopt(%d, %d, %d, %#x, %#x)\", sock_fd, level, option, value_addr, len_addr);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n    dword_t value_len;\n    if (user_get(len_addr, value_len))\n        return _EFAULT;\n    char value[value_len];\n    if (user_read(value_addr, value, value_len))\n        return _EFAULT;\n\n    if (level == SOL_SOCKET_ && (option == SO_DOMAIN_ || option == SO_TYPE_ || option == SO_PROTOCOL_)) {\n        dword_t *value_p = (dword_t *) value;\n        if (value_len != sizeof(*value_p))\n            return _EINVAL;\n        if (option == SO_DOMAIN_)\n            *value_p = sock->socket.domain;\n        else if (option == SO_TYPE_)\n            *value_p = sock->socket.type;\n        else if (option == SO_PROTOCOL_)\n            *value_p = sock->socket.protocol;\n    } else if (level == SOL_SOCKET_ && option == SO_PEERCRED_) {\n        struct ucred_ *cred = (struct ucred_ *) value;\n        if (value_len != sizeof(*cred))\n            return _EINVAL;\n        lock(&peer_lock);\n        if (sock->socket.domain != AF_LOCAL_ || sock->socket.unix_peer == NULL) {\n            cred->pid = 0;\n            cred->uid = cred->gid = -1;\n        } else {\n            *cred = sock->socket.unix_peer->socket.unix_cred;\n        }\n        unlock(&peer_lock);\n    } else if (level == SOL_SOCKET_ && option == SO_ERROR_) {\n        if (value_len != sizeof(dword_t))\n            return _EINVAL;\n        int real_error;\n        socklen_t real_error_len = sizeof(real_error);\n        int err = getsockopt(sock->real_fd, SOL_SOCKET, SO_ERROR, &real_error, &real_error_len);\n        if (err < 0)\n            return errno_map();\n        *(dword_t *) value = real_error == 0 ? 0 : -err_map(real_error);\n    } else if (level == IPPROTO_TCP && option == TCP_CONGESTION_) {\n        value_len = strlen(DEFAULT_TCP_CONGESTION);\n        memcpy(value, DEFAULT_TCP_CONGESTION, value_len);\n#if defined(__APPLE__)\n    } else if (level == IPPROTO_TCP && option == TCP_INFO_) {\n        // This one's fun. On Linux, the struct is not ABI dependent, so no\n        // special handling is needed. On Darwin, the struct is completely\n        // different and has a different sockopt name.\n        struct tcp_connection_info conn_info;\n        socklen_t conn_info_size = sizeof(conn_info);\n        int err = getsockopt(sock->real_fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &conn_info, &conn_info_size);\n        if (err < 0)\n            return errno_map();\n\n        // The possible keys for this table are in netinet/tcp_fsm.h, but that\n        // header isn't available on iOS, only macOS.\n        static const uint8_t tcp_state_table[] = {\n            7, // TCPS_CLOSED\n            10, // TCPS_LISTEN\n            2, // TCPS_SYN_SENT\n            3, // TCPS_SYN_RECEIVED\n            1, // TCPS_ESTABLISHED\n            8, // TCPS_CLOSE_WAIT\n            4, // TCPS_FIN_WAIT_1\n            11, // TCPS_CLOSING\n            9, // TCPS_LAST_ACK\n            5, // TCPS_FIN_WAIT_2\n            6, // TCPS_TIME_WAIT\n        };\n        struct tcp_info_ info = {\n            .state = tcp_state_table[conn_info.tcpi_state],\n            .options = conn_info.tcpi_options,\n            .snd_wscale = conn_info.tcpi_snd_wscale,\n            .rcv_wscale = conn_info.tcpi_rcv_wscale,\n\n            .rto = conn_info.tcpi_rto * 1000,\n            .snd_mss = conn_info.tcpi_maxseg,\n\n            .rtt = conn_info.tcpi_srtt * 1000,\n            .rttvar = conn_info.tcpi_rttvar * 1000,\n            .snd_ssthresh = conn_info.tcpi_snd_ssthresh,\n            .snd_cwnd = conn_info.tcpi_snd_cwnd / conn_info.tcpi_maxseg,\n\n            // https://lkml.org/lkml/2017/4/24/923\n            .total_retrans = conn_info.tcpi_txretransmitpackets,\n        };\n        if (value_len > sizeof(struct tcp_info_))\n            value_len = sizeof(struct tcp_info_);\n        memcpy(value, &info, value_len);\n#endif\n    } else {\n        int real_opt = sock_opt_to_real(option, level);\n        if (real_opt < 0)\n            return _EINVAL;\n        int real_level = sock_level_to_real(level);\n        if (real_level < 0)\n            return _EINVAL;\n\n        int err = getsockopt(sock->real_fd, real_level, real_opt, value, &value_len);\n        if (err < 0)\n            return errno_map();\n    }\n\n    if (user_put(len_addr, value_len))\n        return _EFAULT;\n    if (user_put(value_addr, value))\n        return _EFAULT;\n    return 0;\n}\n\nstatic void scm_free(struct scm *scm) {\n    for (unsigned i = 0; i < scm->num_fds; i++)\n        fd_close(scm->fds[i]);\n    free(scm);\n}\n\nint_t sys_sendmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags) {\n    int err;\n    STRACE(\"sendmsg(%d, %#x, %d)\", sock_fd, msghdr_addr, flags);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n\n    struct msghdr msg;\n    struct msghdr_ msg_fake;\n    if (user_get(msghdr_addr, msg_fake))\n        return _EFAULT;\n\n    // msg_name\n    struct sockaddr_max_ msg_name;\n    if (msg_fake.msg_name != 0) {\n        int err = sockaddr_read(msg_fake.msg_name, &msg_name, &msg_fake.msg_namelen);\n        if (err < 0)\n            return err;\n        msg.msg_name = &msg_name;\n        msg.msg_namelen = msg_fake.msg_namelen;\n    } else {\n        msg.msg_name = NULL;\n    }\n\n    // msg_iovec\n    struct iovec_ msg_iov_fake[msg_fake.msg_iovlen];\n    if (user_get(msg_fake.msg_iov, msg_iov_fake))\n        return _EFAULT;\n    struct iovec msg_iov[msg_fake.msg_iovlen];\n    memset(msg_iov, 0, sizeof(msg_iov));\n    msg.msg_iov = msg_iov;\n    msg.msg_iovlen = sizeof(msg_iov) / sizeof(msg_iov[0]);\n    for (size_t i = 0; i < (size_t) msg.msg_iovlen; i++) {\n        msg_iov[i].iov_len = msg_iov_fake[i].len;\n        msg_iov[i].iov_base = malloc(msg_iov_fake[i].len);\n        err = _EFAULT;\n        if (user_read(msg_iov_fake[i].base, msg_iov[i].iov_base, msg_iov_fake[i].len))\n            goto out_free_iov;\n    }\n\n    // msg_control\n    uint8_t msg_control_buf[2048];\n    uint8_t *msg_control = NULL;\n    if (msg_fake.msg_control != 0) {\n        if (msg_fake.msg_controllen > sizeof(msg_control_buf)) {\n            err = _EINVAL;\n            goto out_free_iov;\n        }\n        msg_control = msg_control_buf;\n        err = _EFAULT;\n        if (user_read(msg_fake.msg_control, msg_control, msg_fake.msg_controllen))\n            goto out_free_iov;\n    }\n    msg.msg_control = NULL;\n    msg.msg_controllen = 0;\n\n    struct scm *scm = NULL;\n    char real_msg_control[CMSG_SPACE(sizeof(int))]; // only used if actually sending an fd\n    if (sock->socket.domain == AF_LOCAL_ && msg_control != NULL && msg_fake.msg_controllen >= sizeof(struct cmsghdr_)) {\n        // figure out how many file descriptors we're sending\n        uint8_t *mhdr_end = msg_control + msg_fake.msg_controllen;\n        unsigned num_fds = 0;\n        struct cmsghdr_ *cmsg;\n        for (cmsg = (void *) msg_control; cmsg != NULL; cmsg = CMSG_NXTHDR_(cmsg, mhdr_end)) {\n            if (cmsg->level != SOL_SOCKET_)\n                continue;\n            if (cmsg->type != SCM_RIGHTS_)\n                return _EINVAL;\n            num_fds += (cmsg->len - sizeof(struct cmsghdr_)) / sizeof(fd_t);\n        }\n        if (num_fds > 253) // *magic*\n            return _EINVAL;\n\n        if (num_fds > 0) {\n            // send one (1) real fd and put the rest in a struct scm\n            static int real_fd = -1;\n            if (real_fd == -1) {\n                real_fd = open(\".\", O_RDONLY);\n                if (real_fd < 0)\n                    ERRNO_DIE(\"no\");\n            }\n            msg.msg_control = real_msg_control;\n            msg.msg_controllen = sizeof(real_msg_control);\n            struct cmsghdr *real_cmsg = CMSG_FIRSTHDR(&msg);\n            real_cmsg->cmsg_level = SOL_SOCKET;\n            real_cmsg->cmsg_type = SCM_RIGHTS;\n            real_cmsg->cmsg_len = CMSG_LEN(sizeof(real_fd));\n            memcpy(CMSG_DATA(real_cmsg), &real_fd, sizeof(real_fd));\n\n            scm = malloc(sizeof(struct scm) + num_fds * sizeof(struct fd *));\n            list_init(&scm->queue);\n            scm->num_fds = num_fds;\n            unsigned fd_i = 0;\n            for (cmsg = (void *) msg_control; cmsg != NULL; cmsg = CMSG_NXTHDR_(cmsg, mhdr_end)) {\n                if (cmsg->level != SOL_SOCKET_)\n                    continue;\n                fd_t *fds = (void *) cmsg->data;\n                for (unsigned i = 0; i < (cmsg->len - sizeof(struct cmsghdr_)) / sizeof(fd_t); i++) {\n                    STRACE(\" sending fd %d\", fds[i]);\n                    scm->fds[fd_i++] = fd_retain(f_get(fds[i]));\n                }\n            }\n            lock(&peer_lock);\n            struct fd *peer = sock->socket.unix_peer;\n            if (peer == NULL) {\n                unlock(&peer_lock);\n                err = _EPIPE;\n                goto out_free_scm;\n            }\n            lock(&peer->lock);\n            list_add_tail(&peer->socket.unix_scm, &scm->queue);\n            unlock(&peer->lock);\n            unlock(&peer_lock);\n        }\n    }\n\n    msg.msg_flags = sock_flags_to_real(msg_fake.msg_flags);\n    err = _EINVAL;\n    if (msg.msg_flags < 0)\n        goto out_free_scm;\n    int real_flags = sock_flags_to_real(flags);\n    if (real_flags < 0)\n        goto out_free_scm;\n\n    err = sendmsg(sock->real_fd, &msg, real_flags);\n    if (err < 0) {\n        err = errno_map();\n        goto out_free_scm;\n    }\n    goto out_free_iov;\n\nout_free_scm:\n    if (scm != NULL) {\n        lock(&peer_lock);\n        struct fd *peer = sock->socket.unix_peer;\n        if (peer != NULL) {\n            lock(&peer->lock);\n            list_remove_safe(&scm->queue);\n            unlock(&peer->lock);\n        }\n        unlock(&peer_lock);\n        scm_free(scm);\n    }\nout_free_iov:\n    for (size_t i = 0; i < (size_t) msg.msg_iovlen; i++)\n        free(msg_iov[i].iov_base);\n    return err;\n}\n\nint_t sys_recvmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags) {\n    STRACE(\"recvmsg(%d, %#x, %d)\", sock_fd, msghdr_addr, flags);\n    struct fd *sock = sock_getfd(sock_fd);\n    if (sock == NULL)\n        return _EBADF;\n\n    struct msghdr msg;\n    struct msghdr_ msg_fake;\n    if (user_get(msghdr_addr, msg_fake))\n        return _EFAULT;\n\n    // msg_name\n    char msg_name[msg_fake.msg_namelen];\n    if (msg_fake.msg_name != 0) {\n        msg.msg_name = msg_name;\n        msg.msg_namelen = sizeof(msg_name);\n    } else {\n        msg.msg_name = NULL;\n        msg.msg_namelen = 0;\n    }\n\n    char real_msg_control[CMSG_SPACE(sizeof(int))] = {}; // only used if needed\n    if (msg_fake.msg_controllen != 0) {\n        // msg_control, include room for one (1) fd\n        msg.msg_control = real_msg_control;\n        msg.msg_controllen = sizeof(real_msg_control);\n    } else {\n        msg.msg_control = NULL;\n        msg.msg_controllen = 0;\n    }\n\n    int real_flags = sock_flags_to_real(flags);\n    if (real_flags < 0)\n        return _EINVAL;\n\n    // msg_iovec (no initial content)\n    struct iovec_ msg_iov_fake[msg_fake.msg_iovlen];\n    if (user_get(msg_fake.msg_iov, msg_iov_fake))\n        return _EFAULT;\n    struct iovec msg_iov[msg_fake.msg_iovlen];\n    msg.msg_iov = msg_iov;\n    msg.msg_iovlen = sizeof(msg_iov) / sizeof(msg_iov[0]);\n    for (size_t i = 0; i < (size_t) msg.msg_iovlen; i++) {\n        msg_iov[i].iov_len = msg_iov_fake[i].len;\n        msg_iov[i].iov_base = malloc(msg_iov_fake[i].len);\n    }\n\n    ssize_t res = recvmsg(sock->real_fd, &msg, real_flags);\n    int err = 0;\n    if (res < 0)\n        err = errno_map();\n    // don't return err quite yet, there are outstanding mallocs\n\n    // msg_iovec (changed)\n    // copy as many bytes as were received, according to the return value\n    size_t n = res;\n    if (res < 0)\n        n = 0;\n    for (size_t i = 0; i < (size_t) msg.msg_iovlen; i++) {\n        size_t chunk_size = msg_iov[i].iov_len;\n        if (chunk_size > n)\n            chunk_size = n;\n        if (chunk_size > 0)\n            if (user_write(msg_iov_fake[i].base, msg_iov[i].iov_base, chunk_size))\n                return _EFAULT;\n        n -= chunk_size;\n        free(msg_iov[i].iov_base);\n    }\n\n    // msg_control (changed)\n    msg_fake.msg_controllen = 0;\n    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);\n    if (sock->socket.domain == AF_LOCAL_ && cmsg != NULL &&\n            cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {\n        int dummy_fd = ((int *) CMSG_DATA(cmsg))[0];\n        close(dummy_fd);\n\n        lock(&sock->lock);\n        assert(!list_empty(&sock->socket.unix_scm));\n        struct scm *scm = list_first_entry(&sock->socket.unix_scm, struct scm, queue);\n        list_remove(&scm->queue);\n        unlock(&sock->lock);\n\n        if (res < 0) {\n            scm_free(scm);\n            return err;\n        }\n\n        uint8_t msg_control[sizeof(struct cmsghdr_) + scm->num_fds * sizeof(fd_t)];\n        struct cmsghdr_ *cmsg = (void *) msg_control;\n        cmsg->len = sizeof(msg_control);\n        cmsg->level = SOL_SOCKET_;\n        cmsg->type = SCM_RIGHTS_;\n        fd_t *fds = (void *) cmsg->data;\n        for (unsigned i = 0; i < scm->num_fds; i++) {\n            fds[i] = f_install(scm->fds[i], 0);\n            STRACE(\" receiving fd %d\", fds[i]);\n        }\n        if (user_write(msg_fake.msg_control, cmsg, cmsg->len))\n            return _EFAULT;\n        msg_fake.msg_controllen = msg.msg_controllen;\n    }\n\n    // by now the iovecs and scm have been freed so we can return\n    if (res < 0)\n        return err;\n\n    // msg_name (changed)\n    if (msg.msg_name != 0) {\n        int err = sockaddr_write(msg_fake.msg_name, msg.msg_name, sizeof(msg_name), &msg.msg_namelen);\n        if (err < 0)\n            return err;\n    }\n    msg_fake.msg_namelen = msg.msg_namelen;\n\n    // msg_flags (changed)\n    msg_fake.msg_flags = sock_flags_from_real(msg.msg_flags);\n\n    if (user_put(msghdr_addr, msg_fake))\n        return _EFAULT;\n    return res;\n}\n\nstruct mmsghdr_ {\n    struct msghdr_ hdr;\n    uint_t len;\n};\n\nint_t sys_sendmmsg(fd_t sock_fd, addr_t msg_vec, uint_t vec_len, int_t flags) {\n    int num_sent = 0;\n    for (unsigned i = 0; i < vec_len; i++) {\n        addr_t msghdr = msg_vec + i * sizeof(struct mmsghdr_);\n        int_t res = sys_sendmsg(sock_fd, msghdr, flags);\n        if (res >= 0) {\n            addr_t msg_len_addr = msghdr + offsetof(struct mmsghdr_, len);\n            if (user_put(msg_len_addr, res))\n                res = _EFAULT;\n        }\n        if (res < 0) {\n            // From the man page:\n            // If an error occurs after at least one message has been sent, the\n            // call succeeds, and returns the number of messages sent.  The\n            // error code is lost.\n            if (num_sent > 0)\n                break;\n            return res;\n        }\n        num_sent++;\n        if (res == 0) {\n            // This means the socket is non-blocking and can't be written to anymore.\n            break;\n        }\n    }\n    return num_sent;\n}\n\nstatic void sock_translate_err(struct fd *fd, int *err) {\n    // on ios, when the device goes to sleep, all connected sockets are killed.\n    // reads/writes return ENOTCONN, which I'm pretty sure is a violation of\n    // posix. so instead, detect this and return ECONNRESET.\n    if (*err == _ENOTCONN) {\n        struct sockaddr addr;\n        socklen_t len = sizeof(addr);\n        if (getpeername(fd->real_fd, &addr, &len) < 0 && errno == EINVAL) {\n            *err = _ECONNRESET;\n        }\n    }\n}\n\nstatic ssize_t sock_read(struct fd *fd, void *buf, size_t size) {\n    int err = realfs_read(fd, buf, size);\n    sock_translate_err(fd, &err);\n    return err;\n}\n\nstatic ssize_t sock_write(struct fd *fd, const void *buf, size_t size) {\n    int err = realfs_write(fd, buf, size);\n    sock_translate_err(fd, &err);\n    return err;\n}\n\nstatic int sock_close(struct fd *fd) {\n    sockrestart_end_listen(fd);\n    // FIXME next 3 lines should go in a function like release_unix_names\n    inode_release_if_exist(fd->socket.unix_name_inode);\n    if (fd->socket.unix_name_abstract != NULL)\n        unix_abstract_release(fd->socket.unix_name_abstract);\n    lock(&peer_lock);\n    struct fd *peer = fd->socket.unix_peer;\n    if (peer != NULL)\n        peer->socket.unix_peer = NULL;\n    unlock(&peer_lock);\n    if (fd->socket.domain == AF_LOCAL_) {\n        lock(&fd->lock);\n        struct scm *scm, *tmp;\n        list_for_each_entry_safe(&fd->socket.unix_scm, scm, tmp, queue) {\n            list_remove(&scm->queue);\n            scm_free(scm);\n        }\n        unlock(&fd->lock);\n    }\n    return realfs_close(fd);\n}\n\nconst struct fd_ops socket_fdops = {\n    .read = sock_read,\n    .write = sock_write,\n    .close = sock_close,\n    .poll = realfs_poll,\n    .getflags = realfs_getflags,\n    .setflags = realfs_setflags,\n    .ioctl_size = realfs_ioctl_size,\n    .ioctl = realfs_ioctl,\n};\n\n#if defined(__GNUC__) && __GNUC__ >= 8\n#pragma GCC diagnostic ignored \"-Wcast-function-type\"\n#endif\n#if defined(__clang__)\n#pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n#endif\nstatic struct socket_call {\n    syscall_t func;\n    int args;\n} socket_calls[] = {\n    {NULL},\n    {(syscall_t) sys_socket, 3},\n    {(syscall_t) sys_bind, 3},\n    {(syscall_t) sys_connect, 3},\n    {(syscall_t) sys_listen, 2},\n    {(syscall_t) sys_accept, 3},\n    {(syscall_t) sys_getsockname, 3},\n    {(syscall_t) sys_getpeername, 3},\n    {(syscall_t) sys_socketpair, 4},\n    {(syscall_t) sys_send, 4}, // send\n    {(syscall_t) sys_recv, 4}, // recv\n    {(syscall_t) sys_sendto, 6},\n    {(syscall_t) sys_recvfrom, 6},\n    {(syscall_t) sys_shutdown, 2},\n    {(syscall_t) sys_setsockopt, 5},\n    {(syscall_t) sys_getsockopt, 5},\n    {(syscall_t) sys_sendmsg, 3},\n    {(syscall_t) sys_recvmsg, 3},\n    {NULL}, // accept4\n    {NULL}, // recvmmsg\n    {(syscall_t) sys_sendmmsg, 4},\n};\n\nint_t sys_socketcall(dword_t call_num, addr_t args_addr) {\n    STRACE(\"%d \", call_num);\n    if (call_num < 1 || call_num >= sizeof(socket_calls)/sizeof(socket_calls[0]))\n        return _EINVAL;\n    struct socket_call call = socket_calls[call_num];\n    if (call.func == NULL) {\n        FIXME(\"socketcall %d\", call_num);\n        return _ENOSYS;\n    }\n\n    dword_t args[6];\n    if (user_read(args_addr, args, sizeof(dword_t) * call.args))\n        return _EFAULT;\n    return call.func(args[0], args[1], args[2], args[3], args[4], args[5]);\n}\n"
  },
  {
    "path": "fs/sock.h",
    "content": "#ifndef SYS_SOCK_H\n#define SYS_SOCK_H\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include \"kernel/errno.h\"\n#include \"fs/fd.h\"\n#include \"misc.h\"\n#include \"debug.h\"\n\nint_t sys_socketcall(dword_t call_num, addr_t args_addr);\n\nint_t sys_socket(dword_t domain, dword_t type, dword_t protocol);\nint_t sys_bind(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len);\nint_t sys_connect(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len);\nint_t sys_listen(fd_t sock_fd, int_t backlog);\nint_t sys_accept(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr);\nint_t sys_getsockname(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr);\nint_t sys_getpeername(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr);\nint_t sys_socketpair(dword_t domain, dword_t type, dword_t protocol, addr_t sockets_addr);\nint_t sys_sendto(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, addr_t sockaddr_addr, dword_t sockaddr_len);\nint_t sys_recvfrom(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, addr_t sockaddr_addr, addr_t sockaddr_len_addr);\nint_t sys_shutdown(fd_t sock_fd, dword_t how);\nint_t sys_setsockopt(fd_t sock_fd, dword_t level, dword_t option, addr_t value_addr, dword_t value_len);\nint_t sys_getsockopt(fd_t sock_fd, dword_t level, dword_t option, addr_t value_addr, dword_t len_addr);\nint_t sys_sendmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags);\nint_t sys_recvmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags);\nint_t sys_sendmmsg(fd_t sock_fd, addr_t msgvec_addr, uint_t msgvec_len, int_t flags);\n\n#define SOCKADDR_DATA_MAX 108\n\nstruct sockaddr_ {\n    uint16_t family;\n    char data[14];\n};\nstruct sockaddr_max_ {\n    uint16_t family;\n    char data[SOCKADDR_DATA_MAX];\n};\n\nsize_t sockaddr_size(void *p);\n// result comes from malloc\nstruct sockaddr *sockaddr_to_real(void *p);\n\nstruct msghdr_ {\n    addr_t msg_name;\n    uint_t msg_namelen;\n    addr_t msg_iov;\n    uint_t msg_iovlen;\n    addr_t msg_control;\n    uint_t msg_controllen;\n    int_t msg_flags;\n};\n\nstruct cmsghdr_ {\n    dword_t len;\n    int_t level;\n    int_t type;\n    uint8_t data[];\n};\n#define SCM_RIGHTS_ 1\n// copied and ported from musl\n#define CMSG_LEN_(cmsg) (((cmsg)->len + sizeof(dword_t) - 1) & ~(dword_t)(sizeof(dword_t) - 1))\n#define CMSG_NEXT_(cmsg) ((uint8_t *)(cmsg) + CMSG_LEN_(cmsg))\n#define CMSG_NXTHDR_(cmsg, mhdr_end) ((cmsg)->len < sizeof (struct cmsghdr_) || \\\n        CMSG_LEN_(cmsg) + sizeof(struct cmsghdr_) >= (size_t) (mhdr_end - (uint8_t *)(cmsg)) \\\n        ? NULL : (struct cmsghdr_ *)CMSG_NEXT_(cmsg))\n\nstruct scm {\n    struct list queue;\n    unsigned num_fds;\n    struct fd *fds[];\n};\n\n#define PF_LOCAL_ 1\n#define PF_INET_ 2\n#define PF_INET6_ 10\n#define AF_LOCAL_ PF_LOCAL_\n#define AF_INET_ PF_INET_\n#define AF_INET6_ PF_INET6_\nstatic inline int sock_family_to_real(int fake) {\n    switch (fake) {\n        case PF_LOCAL_: return PF_LOCAL;\n        case PF_INET_: return PF_INET;\n        case PF_INET6_: return PF_INET6;\n    }\n    return -1;\n}\nstatic inline int sock_family_from_real(int fake) {\n    switch (fake) {\n        case PF_LOCAL: return PF_LOCAL_;\n        case PF_INET: return PF_INET_;\n        case PF_INET6: return PF_INET6_;\n    }\n    return -1;\n}\n\n#define SOCK_STREAM_ 1\n#define SOCK_DGRAM_ 2\n#define SOCK_RAW_ 3\n#define SOCK_NONBLOCK_ 0x800\n#define SOCK_CLOEXEC_ 0x80000\n\nstatic inline int sock_type_to_real(int type, int protocol) {\n    switch (type & 0xff) {\n        case SOCK_STREAM_:\n            if (protocol != 0 && protocol != IPPROTO_TCP)\n                return -1;\n            return SOCK_STREAM;\n        case SOCK_DGRAM_:\n            switch (protocol) {\n                default:\n                    return -1;\n                case 0:\n                case IPPROTO_UDP:\n                case IPPROTO_ICMP:\n                case IPPROTO_ICMPV6:\n                    break;\n            }\n            return SOCK_DGRAM;\n        case SOCK_RAW_:\n            switch (protocol) {\n                default:\n                    return -1;\n                case IPPROTO_RAW:\n                case IPPROTO_UDP:\n                case IPPROTO_ICMP:\n                case IPPROTO_ICMPV6:\n                    break;\n            }\n            return SOCK_DGRAM;\n    }\n    return -1;\n}\n\n#define MSG_OOB_ 0x1\n#define MSG_PEEK_ 0x2\n#define MSG_CTRUNC_  0x8\n#define MSG_TRUNC_  0x20\n#define MSG_DONTWAIT_ 0x40\n#define MSG_EOR_    0x80\n#define MSG_WAITALL_ 0x100\n\nstatic inline int sock_flags_to_real(int fake) {\n    int real = 0;\n    if (fake & MSG_OOB_) real |= MSG_OOB;\n    if (fake & MSG_PEEK_) real |= MSG_PEEK;\n    if (fake & MSG_CTRUNC_) real |= MSG_CTRUNC;\n    if (fake & MSG_TRUNC_) real |= MSG_TRUNC;\n    if (fake & MSG_DONTWAIT_) real |= MSG_DONTWAIT;\n    if (fake & MSG_EOR_) real |= MSG_EOR;\n    if (fake & MSG_WAITALL_) real |= MSG_WAITALL;\n    if (fake & ~(MSG_OOB_|MSG_PEEK_|MSG_CTRUNC_|MSG_TRUNC_|MSG_DONTWAIT_|MSG_EOR_|MSG_WAITALL_))\n        TRACE(\"unimplemented socket flags %d\\n\", fake);\n    return real;\n}\nstatic inline int sock_flags_from_real(int real) {\n    int fake = 0;\n    if (real & MSG_OOB) fake |= MSG_OOB_;\n    if (real & MSG_PEEK) fake |= MSG_PEEK_;\n    if (real & MSG_CTRUNC) fake |= MSG_CTRUNC_;\n    if (real & MSG_TRUNC) fake |= MSG_TRUNC_;\n    if (real & MSG_DONTWAIT) fake |= MSG_DONTWAIT_;\n    if (real & MSG_EOR) fake |= MSG_EOR_;\n    if (real & MSG_WAITALL) fake |= MSG_WAITALL_;\n    if (real & ~(MSG_OOB|MSG_PEEK|MSG_CTRUNC|MSG_TRUNC|MSG_DONTWAIT|MSG_EOR|MSG_WAITALL))\n        TRACE(\"unimplemented socket flags %d\\n\", real);\n    return fake;\n}\n\n#define SOL_SOCKET_ 1\n\n#define SO_REUSEADDR_ 2\n#define SO_TYPE_ 3\n#define SO_ERROR_ 4\n#define SO_BROADCAST_ 6\n#define SO_SNDBUF_ 7\n#define SO_RCVBUF_ 8\n#define SO_KEEPALIVE_ 9\n#define SO_LINGER_ 13\n#define SO_PEERCRED_ 17\n#define SO_TIMESTAMP_ 29\n#define SO_PROTOCOL_ 38\n#define SO_DOMAIN_ 39\n#define SO_RCVTIMEO_ 66\n#define SO_SNDTIMEO_ 67\n#define IP_TOS_ 1\n#define IP_TTL_ 2\n#define IP_HDRINCL_ 3\n#define IP_RETOPTS_ 7\n#define IP_MTU_DISCOVER_ 10\n#define IP_RECVTTL_ 12\n#define IP_RECVTOS_ 13\n#define TCP_NODELAY_ 1\n#define TCP_DEFER_ACCEPT_ 9\n#define TCP_INFO_ 11\n#define TCP_CONGESTION_ 13\n#define IPV6_UNICAST_HOPS_ 16\n#define IPV6_V6ONLY_ 26\n#define IPV6_TCLASS_ 67\n#define ICMP6_FILTER_ 1\n\nstatic inline int sock_opt_to_real(int fake, int level) {\n    switch (level) {\n        case SOL_SOCKET_: switch (fake) {\n            case SO_REUSEADDR_: return SO_REUSEADDR;\n            case SO_TYPE_: return SO_TYPE;\n            case SO_ERROR_: return SO_ERROR;\n            case SO_BROADCAST_: return SO_BROADCAST;\n            case SO_KEEPALIVE_: return SO_KEEPALIVE;\n            case SO_LINGER_: return SO_LINGER;\n            case SO_SNDBUF_: return SO_SNDBUF;\n            case SO_RCVBUF_: return SO_RCVBUF;\n            case SO_TIMESTAMP_: return SO_TIMESTAMP;\n            case SO_RCVTIMEO_: return SO_RCVTIMEO;\n            case SO_SNDTIMEO_: return SO_SNDTIMEO;\n        } break;\n        case IPPROTO_TCP: switch (fake) {\n            case TCP_NODELAY_: return TCP_NODELAY;\n            case TCP_DEFER_ACCEPT_: return 0; // unimplemented\n#if defined(__linux__)\n            case TCP_INFO_: return TCP_INFO;\n            case TCP_CONGESTION_: return TCP_CONGESTION;\n#endif\n        } break;\n        case IPPROTO_IP: switch (fake) {\n            case IP_TOS_: return IP_TOS;\n            case IP_TTL_: return IP_TTL;\n            case IP_HDRINCL_: return IP_HDRINCL;\n            case IP_RETOPTS_: return IP_RETOPTS;\n            case IP_RECVTTL_: return IP_RECVTTL;\n            case IP_RECVTOS_: return IP_RECVTOS;\n        } break;\n        case IPPROTO_IPV6: switch (fake) {\n            case IPV6_UNICAST_HOPS_: return IPV6_UNICAST_HOPS;\n            case IPV6_TCLASS_: return IPV6_TCLASS;\n            case IPV6_V6ONLY_: return IPV6_V6ONLY;\n        } break;\n    }\n    return -1;\n}\n\nstatic inline int sock_level_to_real(int fake) {\n    if (fake == SOL_SOCKET_)\n        return SOL_SOCKET;\n    return fake;\n}\n\nextern const char *sock_tmp_prefix;\n\nstruct tcp_info_ {\n    uint8_t state;\n    uint8_t ca_state;\n    uint8_t retransmits;\n    uint8_t probes;\n    uint8_t backoff;\n    uint8_t options;\n    uint8_t snd_wscale:4, rcv_wscale:4;\n\n    uint32_t rto;\n    uint32_t ato;\n    uint32_t snd_mss;\n    uint32_t rcv_mss;\n\n    uint32_t unacked;\n    uint32_t sacked;\n    uint32_t lost;\n    uint32_t retrans;\n    uint32_t fackets;\n\n    uint32_t last_data_sent;\n    uint32_t last_ack_sent;\n    uint32_t last_data_recv;\n    uint32_t last_ack_recv;\n\n    uint32_t pmtu;\n    uint32_t rcv_ssthresh;\n    uint32_t rtt;\n    uint32_t rttvar;\n    uint32_t snd_ssthresh;\n    uint32_t snd_cwnd;\n    uint32_t advmss;\n    uint32_t reordering;\n\n    uint32_t rcv_rtt;\n    uint32_t rcv_space;\n\n    uint32_t total_retrans;\n};\n\n#endif\n"
  },
  {
    "path": "fs/sockrestart.c",
    "content": "#include <string.h>\n#include <signal.h>\n#include <pthread.h>\n#include \"fs/sockrestart.h\"\n#include \"fs/fd.h\"\n#include \"fs/sock.h\"\n#include \"kernel/task.h\"\n#include \"util/list.h\"\nextern const struct fd_ops socket_fdops;\n\nstatic lock_t sockrestart_lock = LOCK_INITIALIZER;\nstatic struct list listen_fds = LIST_INITIALIZER(listen_fds);\n\nvoid sockrestart_begin_listen(struct fd *sock) {\n    if (sock->ops != &socket_fdops)\n        return;\n    lock(&sockrestart_lock);\n    list_add(&listen_fds, &sock->sockrestart.listen);\n    unlock(&sockrestart_lock);\n}\n\nvoid sockrestart_end_listen(struct fd *sock) {\n    if (sock->ops != &socket_fdops)\n        return;\n    lock(&sockrestart_lock);\n    list_remove_safe(&sock->sockrestart.listen);\n    unlock(&sockrestart_lock);\n}\n\nstatic struct list listen_tasks = LIST_INITIALIZER(listen_tasks);\n\nvoid sockrestart_begin_listen_wait(struct fd *sock) {\n    if (sock->ops != &socket_fdops)\n        return;\n    lock(&sockrestart_lock);\n    if (current->sockrestart.count == 0)\n        list_add(&listen_tasks, &current->sockrestart.listen);\n    current->sockrestart.count++;\n    unlock(&sockrestart_lock);\n}\n\nvoid sockrestart_end_listen_wait(struct fd *sock) {\n    if (sock->ops != &socket_fdops)\n        return;\n    lock(&sockrestart_lock);\n    current->sockrestart.count--;\n    if (current->sockrestart.count == 0)\n        list_remove(&current->sockrestart.listen);\n    unlock(&sockrestart_lock);\n}\n\nbool sockrestart_should_restart_listen_wait() {\n    lock(&sockrestart_lock);\n    bool punt = current->sockrestart.punt;\n    current->sockrestart.punt = false;\n    unlock(&sockrestart_lock);\n    return punt;\n}\n\nstruct saved_socket {\n    struct fd *sock;\n    int type;\n    int proto;\n    union {\n        char name[128];\n        struct sockaddr name_addr;\n    };\n    socklen_t name_len;\n    struct list saved;\n};\n\nstatic struct list saved_sockets = LIST_INITIALIZER(saved_sockets);\n\n// these should only be called from the main thread, but it's easiest to just lock for the whole time\n\nvoid sockrestart_on_suspend() {\n    lock(&sockrestart_lock);\n    assert(list_empty(&saved_sockets));\n    struct fd *sock;\n    list_for_each_entry(&listen_fds, sock, sockrestart.listen) {\n        struct saved_socket *saved = malloc(sizeof(struct saved_socket));\n        if (saved == NULL)\n            continue; // better than a crash\n        saved->sock = fd_retain(sock);\n        saved->proto = sock->socket.protocol;\n        unsigned size = sizeof(saved->type);\n        getsockopt(sock->real_fd, SOL_SOCKET, SO_TYPE, &saved->type, &size);\n        assert(size == sizeof(saved->type));\n        saved->name_len = sizeof(saved->name);\n        getsockname(sock->real_fd, (struct sockaddr *) &saved->name, &saved->name_len);\n        list_add(&saved_sockets, &saved->saved);\n    }\n    unlock(&sockrestart_lock);\n}\n\nvoid sockrestart_on_resume() {\n    lock(&sockrestart_lock);\n    struct saved_socket *saved, *tmp;\n    list_for_each_entry_safe(&saved_sockets, saved, tmp, saved) {\n        list_remove(&saved->saved);\n        int new_sock = socket(saved->name_addr.sa_family, saved->type, saved->proto);\n        if (new_sock < 0) {\n            printk(\"restarting socket(%d, %d, %d) failed: %s\\n\",\n                    saved->name_addr.sa_family, saved->type, saved->proto, strerror(errno));\n            goto thank_u_next;\n        }\n        if (bind(new_sock, (struct sockaddr *) &saved->name, saved->name_len) < 0) {\n            printk(\"rebinding socket failed: %s\\n\", strerror(errno));\n            goto thank_u_next;\n        }\n        dup2(new_sock, saved->sock->real_fd);\n\nthank_u_next:\n        fd_close(saved->sock);\n    }\n    struct task *task;\n    list_for_each_entry(&listen_tasks, task, sockrestart.listen) {\n        task->sockrestart.punt = true;\n        pthread_kill(task->thread, SIGUSR1);\n    }\n    unlock(&sockrestart_lock);\n}\n"
  },
  {
    "path": "fs/sockrestart.h",
    "content": "// Hack to work around the idiotic way iOS handles suspending apps that have\n// listening sockets.\n// Basically the actual socket part of the file just gets freed, and the socket\n// ceases to be a socket. Any attempt to do socket things with it will just\n// immediately fail, and anyone blocked on accept will never wake up.\n// Solution: keep track of all the listening sockets, and the threads that are\n// blocked on them. On suspend, make a record of the names and configuration of\n// all the listening sockets. On resume, open new sockets, reconfigure them,\n// use dup2 to replace the original sockets, and get any thread waiting on them\n// to restart the wait.\n// This file contains hooks into various other places to do all that.\n// https://developer.apple.com/library/archive/technotes/tn2277/_index.html\n#ifndef FS_SOCKRESTART_H\n#define FS_SOCKRESTART_H\n#include <stdbool.h>\n#include \"util/list.h\"\nstruct fd;\n\nvoid sockrestart_begin_listen(struct fd *sock);\nvoid sockrestart_end_listen(struct fd *sock);\nvoid sockrestart_begin_listen_wait(struct fd *sock);\nvoid sockrestart_end_listen_wait(struct fd *sock);\nbool sockrestart_should_restart_listen_wait(void);\nvoid sockrestart_on_suspend(void);\nvoid sockrestart_on_resume(void);\n\nstruct fd_sockrestart {\n    struct list listen;\n};\n\nstruct task_sockrestart {\n    int count;\n    bool punt;\n    struct list listen;\n};\n\n#endif\n"
  },
  {
    "path": "fs/sqlutil.h",
    "content": "#ifndef FS_SQLUTIL_H\n#define FS_SQLUTIL_H\n#include <sqlite3.h>\n\n// Some nice sqlite macros for anything outside of fs/fake.c\n\n#define Q(...) #__VA_ARGS__\n\n#define HANDLE_ERR(db) \\\n    die(\"sqlite error while rebuilding: %s\\n\", sqlite3_errmsg(db))\n\n#define CHECK_ERR() \\\n    if (err != SQLITE_OK && err != SQLITE_ROW && err != SQLITE_DONE) \\\n        HANDLE_ERR(db)\n#define EXEC(sql) \\\n    err = sqlite3_exec(db, sql, NULL, NULL, NULL); \\\n    CHECK_ERR();\n#define PREPARE(sql) ({ \\\n    sqlite3_stmt *stmt; \\\n    err = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); \\\n    CHECK_ERR(); \\\n    stmt; \\\n})\n#define STEP(stmt) ({ \\\n    err = sqlite3_step(stmt); \\\n    CHECK_ERR(); \\\n    err == SQLITE_ROW; \\\n})\n#define RESET(stmt) \\\n    err = sqlite3_reset(stmt); \\\n    CHECK_ERR()\n#define STEP_RESET(stmt) \\\n    STEP(stmt); \\\n    RESET(stmt)\n#define FINALIZE(stmt) \\\n    err = sqlite3_finalize(stmt); \\\n    CHECK_ERR()\n\n#endif\n"
  },
  {
    "path": "fs/stat.c",
    "content": "#include <string.h>\n#include <sys/stat.h>\n#include <limits.h>\n\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"fs/path.h\"\n\nstruct newstat64 stat_convert_newstat64(struct statbuf stat) {\n    struct newstat64 newstat;\n    newstat.dev = stat.dev;\n    newstat.fucked_ino = stat.inode;\n    newstat.ino = stat.inode;\n    newstat.mode = stat.mode;\n    newstat.nlink = stat.nlink;\n    newstat.uid = stat.uid;\n    newstat.gid = stat.gid;\n    newstat.rdev = stat.rdev;\n    newstat.size = stat.size;\n    newstat.blksize = stat.blksize;\n    newstat.blocks = stat.blocks;\n    newstat.atime = stat.atime;\n    newstat.atime_nsec = stat.atime_nsec;\n    newstat.mtime = stat.mtime;\n    newstat.mtime_nsec = stat.mtime_nsec;\n    newstat.ctime = stat.ctime;\n    newstat.ctime_nsec = stat.ctime_nsec;\n    return newstat;\n}\n\nint generic_statat(struct fd *at, const char *path_raw, struct statbuf *stat, bool follow_links) {\n    char path[MAX_PATH];\n    int err = path_normalize(at, path_raw, path, follow_links ? N_SYMLINK_FOLLOW : N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = find_mount_and_trim_path(path);\n    memset(stat, 0, sizeof(*stat));\n    err = mount->fs->stat(mount, path, stat);\n    mount_release(mount);\n    return err;\n}\n\n// TODO get rid of this and maybe everything else in the file\nstatic struct fd *at_fd(fd_t f) {\n    if (f == AT_FDCWD_)\n        return AT_PWD;\n    return f_get(f);\n}\n\nstatic dword_t sys_stat_path(fd_t at_f, addr_t path_addr, addr_t statbuf_addr, bool follow_links) {\n    int err;\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"stat(at=%d, path=\\\"%s\\\", statbuf=0x%x, follow_links=%d)\", at_f, path, statbuf_addr, follow_links);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    struct statbuf stat = {};\n    if ((err = generic_statat(at, path, &stat, follow_links)) < 0)\n        return err;\n    struct newstat64 newstat = stat_convert_newstat64(stat);\n    if (user_put(statbuf_addr, newstat))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_stat64(addr_t path_addr, addr_t statbuf_addr) {\n    return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, true);\n}\n\ndword_t sys_lstat64(addr_t path_addr, addr_t statbuf_addr) {\n    return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, false);\n}\n\ndword_t sys_fstatat64(fd_t at, addr_t path_addr, addr_t statbuf_addr, dword_t flags) {\n    return sys_stat_path(at, path_addr, statbuf_addr, !(flags & AT_SYMLINK_NOFOLLOW_));\n}\n\ndword_t sys_fstat64(fd_t fd_no, addr_t statbuf_addr) {\n    STRACE(\"fstat64(%d, 0x%x)\", fd_no, statbuf_addr);\n    struct fd *fd = f_get(fd_no);\n    if (fd == NULL)\n        return _EBADF;\n    struct statbuf stat = {};\n    int err = fd->mount->fs->fstat(fd, &stat);\n    if (err < 0)\n        return err;\n    struct newstat64 newstat = stat_convert_newstat64(stat);\n    if (user_put(statbuf_addr, newstat))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_statx(fd_t at_f, addr_t path_addr, int_t flags, uint_t mask, addr_t statx_addr) {\n    int err;\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n\n    STRACE(\"statx(at=%d, path=\\\"%s\\\", flags=%d, mask=%d, statx=0x%x)\", at_f, path, flags, mask, statx_addr);\n\n    struct statbuf stat = {};\n\n    if ((flags & AT_EMPTY_PATH_) && strcmp(path, \"\") == 0) {\n        struct fd *fd = at;\n        int err = fd->mount->fs->fstat(fd, &stat);\n        if (err < 0)\n            return err;\n    } else {\n        bool follow_links = !(flags & AT_SYMLINK_NOFOLLOW_);\n        int err = generic_statat(at, path, &stat, follow_links);\n        if (err < 0)\n            return err;\n    }\n\n    // for now, ignore the requested mask and just fill in the same fields as stat returns\n    struct statx_ statx = {};\n    statx.mask = STATX_BASIC_STATS_;\n    statx.blksize = stat.blksize;\n    statx.nlink = stat.nlink;\n    statx.uid = stat.uid;\n    statx.gid = stat.gid;\n    statx.mode = stat.mode;\n    statx.ino = stat.inode;\n    statx.size = stat.size;\n    statx.blocks = stat.blocks;\n    statx.atime.sec = stat.atime;\n    statx.atime.nsec = stat.atime_nsec;\n    statx.mtime.sec = stat.mtime;\n    statx.mtime.nsec = stat.mtime_nsec;\n    statx.ctime.sec = stat.ctime;\n    statx.ctime.nsec = stat.ctime_nsec;\n    statx.rdev_major = dev_major(stat.rdev);\n    statx.rdev_minor = dev_minor(stat.rdev);\n    statx.dev_major = dev_major(stat.dev);\n    statx.dev_minor = dev_minor(stat.dev);\n\n    if (user_put(statx_addr, statx))\n        return _EFAULT;\n    return 0;\n}\n"
  },
  {
    "path": "fs/stat.h",
    "content": "#ifndef FS_STAT_H\n#define FS_STAT_H\n\n#include \"misc.h\"\n\nstruct statbuf {\n    qword_t dev;\n    qword_t inode;\n    dword_t mode;\n    dword_t nlink;\n    dword_t uid;\n    dword_t gid;\n    qword_t rdev;\n    qword_t size;\n    dword_t blksize;\n    qword_t blocks;\n    dword_t atime;\n    dword_t atime_nsec;\n    dword_t mtime;\n    dword_t mtime_nsec;\n    dword_t ctime;\n    dword_t ctime_nsec;\n};\n\nstruct oldstat {\n    word_t dev;\n    word_t ino;\n    word_t mode;\n    word_t nlink;\n    word_t uid;\n    word_t gid;\n    word_t rdev;\n    uint_t size;\n    uint_t atime;\n    uint_t mtime;\n    uint_t ctime;\n};\n\nstruct newstat {\n    dword_t dev;\n    dword_t ino;\n    word_t mode;\n    word_t nlink;\n    word_t uid;\n    word_t gid;\n    dword_t rdev;\n    dword_t size;\n    dword_t blksize;\n    dword_t blocks;\n    dword_t atime;\n    dword_t atime_nsec;\n    dword_t mtime;\n    dword_t mtime_nsec;\n    dword_t ctime;\n    dword_t ctime_nsec;\n    char pad[8];\n};\n\nstruct newstat64 {\n    qword_t dev;\n    dword_t _pad1;\n    dword_t fucked_ino;\n    dword_t mode;\n    dword_t nlink;\n    dword_t uid;\n    dword_t gid;\n    qword_t rdev;\n    dword_t _pad2;\n    qword_t size;\n    dword_t blksize;\n    qword_t blocks;\n    dword_t atime;\n    dword_t atime_nsec;\n    dword_t mtime;\n    dword_t mtime_nsec;\n    dword_t ctime;\n    dword_t ctime_nsec;\n    qword_t ino;\n} __attribute__((packed));\n\nstruct statfsbuf {\n    long type;\n    long bsize;\n    uint64_t blocks;\n    uint64_t bfree;\n    uint64_t bavail;\n    uint64_t files;\n    uint64_t ffree;\n    uint64_t fsid;\n    long namelen;\n    long frsize;\n    long flags;\n    long spare[4];\n};\n\nstruct statfs_ {\n    uint_t type;\n    uint_t bsize;\n    uint_t blocks;\n    uint_t bfree;\n    uint_t bavail;\n    uint_t files;\n    uint_t ffree;\n    uint64_t fsid;\n    uint_t namelen;\n    uint_t frsize;\n    uint_t flags;\n    uint_t spare[4];\n} __attribute__((packed));\n\nstruct statfs64_ {\n    uint_t type;\n    uint_t bsize;\n    uint64_t blocks;\n    uint64_t bfree;\n    uint64_t bavail;\n    uint64_t files;\n    uint64_t ffree;\n    uint64_t fsid;\n    uint_t namelen;\n    uint_t frsize;\n    uint_t flags;\n    uint_t pad[4];\n} __attribute__((packed));\n\nstruct statx_timestamp_ {\n    int64_t sec;\n    uint32_t nsec;\n    uint32_t _pad;\n};\n\nstruct statx_ {\n    uint32_t mask;\n    uint32_t blksize;\n    uint64_t attributes;\n    uint32_t nlink;\n    uint32_t uid;\n    uint32_t gid;\n    uint16_t mode;\n    uint16_t _pad1;\n    uint64_t ino;\n    uint64_t size;\n    uint64_t blocks;\n    uint64_t attributes_mask;\n    struct statx_timestamp_ atime;\n    struct statx_timestamp_ btime;\n    struct statx_timestamp_ ctime;\n    struct statx_timestamp_ mtime;\n    uint32_t rdev_major;\n    uint32_t rdev_minor;\n    uint32_t dev_major;\n    uint32_t dev_minor;\n    uint64_t mnt_id;\n    uint32_t dio_mem_align;\n    uint32_t dio_offset_align;\n    uint32_t _pad2[24];\n} __attribute__((packed));\n\n#define STATX_BASIC_STATS_ 0x7ff\n\n#endif\n"
  },
  {
    "path": "fs/tmp.c",
    "content": "#include <sys/stat.h>\n#include <string.h>\n#include \"kernel/task.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/fs.h\"\n#include \"fs/path.h\"\n#include \"util/refcount.h\"\n#include \"debug.h\"\n\n// ========================\n// ======== INODES ========\n// ========================\n\nstruct tmp_inode {\n    struct refcount refcount;\n    lock_t lock;\n\n    struct statbuf stat;\n    union {\n        void *file_data;\n        //char *symlink_data;\n    };\n};\n\nstatic struct tmp_inode *tmp_inode_new(mode_t_ mode) {\n    struct tmp_inode *node = malloc(sizeof(struct tmp_inode));\n    if (node == NULL)\n        return NULL;\n    refcount_init(node);\n    lock_init(&node->lock);\n\n    node->stat = (struct statbuf) {};\n    static _Atomic ino_t next_inode = 1;\n    node->stat.inode = next_inode++;\n\n    node->stat.mode = mode;\n    node->stat.uid = current->euid;\n    node->stat.gid = current->egid;\n    if (S_ISREG(mode)) {\n        node->file_data = malloc(0);\n        if (node->file_data == NULL) {\n            free(node);\n            return NULL;\n        }\n    }\n    return node;\n}\n\nDEFINE_REFCOUNT_STATIC(tmp_inode)\n\nstatic void tmp_inode_cleanup(struct tmp_inode *inode) {\n    if (S_ISREG(inode->stat.mode)) {\n        free(inode->file_data);\n    }\n    free(inode);\n}\n\n// ===================================\n// ======== DIRECTORY ENTRIES ========\n// ===================================\n\nstruct tmp_dirent {\n    char name[MAX_NAME + 1];\n    struct tmp_inode *inode;\n    unsigned long index;\n\n    struct tmp_dirent *parent;\n    struct list children;\n    unsigned long next_index;\n\n    struct refcount refcount;\n    lock_t lock;\n    struct list dir;\n};\n\nDEFINE_REFCOUNT_STATIC(tmp_dirent)\n\nstatic void tmp_dirent_cleanup(struct tmp_dirent *dirent) {\n    list_remove(&dirent->dir); // TODO locking thinking emoji\n    tmp_inode_release(dirent->inode);\n    free(dirent);\n}\n\nstatic void tmp_dirent_init(struct tmp_dirent *dirent) {\n    refcount_init(dirent);\n    list_init(&dirent->children);\n    dirent->next_index = 0;\n    lock_init(&dirent->lock);\n}\n\n// Frees the child inode on failure, so you don't need to! But be careful you don't free it yourself.\n// In other words: Takes ownership of `child`\nstatic int tmpfs_dir_link(struct tmp_dirent *dir, const char *name, struct tmp_inode *child, struct tmp_dirent **dirent_out) {\n    if (!S_ISDIR(dir->inode->stat.mode)) {\n        tmp_inode_release(child);\n        return _ENOTDIR;\n    }\n    struct tmp_dirent *new_dirent = malloc(sizeof(struct tmp_dirent));\n    if (new_dirent == NULL) {\n        tmp_inode_release(child);\n        return _ENOMEM;\n    }\n\n    tmp_dirent_init(new_dirent);\n    strcpy(new_dirent->name, name);\n    new_dirent->inode = tmp_inode_retain(child);\n    new_dirent->index = dir->next_index++;\n    new_dirent->parent = tmp_dirent_retain(dir);\n    list_add_tail(&dir->children, &new_dirent->dir);\n\n    if (dirent_out)\n        *dirent_out = tmp_dirent_retain(new_dirent);\n    return 0;\n}\n\nstatic void tmpfs_fd_seekdir(struct fd *fd, struct tmp_dirent *dirent) {\n    if (dirent != NULL)\n        tmp_dirent_retain(dirent);\n    if (fd->tmpfs.dir_pos != NULL)\n        tmp_dirent_release(fd->tmpfs.dir_pos);\n    fd->tmpfs.dir_pos = dirent;\n}\n\nstatic struct tmp_dirent *tmpfs_dir_lookup(struct tmp_dirent *dir, const char *name) {\n    if (!S_ISDIR(dir->inode->stat.mode))\n        return ERR_PTR(_ENOTDIR);\n    struct tmp_dirent *dirent = NULL;\n    struct tmp_dirent *d;\n    list_for_each_entry(&dir->children, d, dir) {\n        if (d->inode == NULL)\n            continue;\n        if (strcmp(d->name, name) == 0) {\n            dirent = d;\n            break;\n        }\n    }\n    if (dirent == NULL)\n        return ERR_PTR(_ENOENT);\n    return tmp_dirent_retain(dirent);\n}\n\n// TODO: should this function even exist? can't tmpfs_dir_link check for existence?\nstatic int tmpfs_dir_lookup_existence(struct tmp_dirent *dir, const char *name) {\n    struct tmp_dirent *dirent = tmpfs_dir_lookup(dir, name);\n    if (dirent == ERR_PTR(_ENOENT))\n        return 0;\n    if (IS_ERR(dirent))\n        return PTR_ERR(dirent);\n    tmp_dirent_release(dirent);\n    return _EEXIST;\n}\n\nstatic struct tmp_dirent *__tmpfs_lookup(struct mount *mount, const char *path, bool parent, const char **filename_out) {\n    struct tmp_dirent *root = mount->data;\n    struct tmp_dirent *dirent = tmp_dirent_retain(root); // strong reference\n\n    char component[MAX_NAME + 1] = {};\n    int err = 0;\n    while (path_next_component(&path, component, &err)) {\n        if (parent && *path == '\\0')\n            break;\n\n        lock(&dirent->lock);\n        struct tmp_dirent *child = tmpfs_dir_lookup(dirent, component);\n        unlock(&dirent->lock);\n\n        tmp_dirent_release(dirent);\n        if (IS_ERR(child))\n            return child;\n        dirent = child;\n    }\n\n    if (parent && filename_out)\n        *filename_out = path - strlen(component);\n\n    if (err < 0)\n        return ERR_PTR(err);\n    return dirent;\n}\nstatic struct tmp_dirent *tmpfs_lookup(struct mount *mount, const char *path) {\n    return __tmpfs_lookup(mount, path, false, NULL);\n}\nstatic struct tmp_dirent *tmpfs_lookup_parent(struct mount *mount, const char *path, const char **filename_out) {\n    if (strcmp(path, \"/\") == 0)\n        return NULL;\n    return __tmpfs_lookup(mount, path, true, filename_out);\n}\n\nstatic int tmpfs_file_resize(struct tmp_inode *file, size_t size) {\n    assert(S_ISREG(file->stat.mode));\n    size_t old_size = file->stat.size;\n    void *new_data = realloc(file->file_data, size);\n    if (new_data == NULL)\n        return _ENOMEM;\n    file->file_data = new_data;\n    file->stat.size = size;\n    memset((char *) file->file_data + old_size, 0, file->stat.size - old_size);\n    return 0;\n}\n\n// ========================\n// ======== FS OPS ========\n// ========================\n\nextern const struct fd_ops tmpfs_fdops;\n\nstatic int tmpfs_mount(struct mount *mount) {\n    struct tmp_inode *root_inode = tmp_inode_new(S_IFDIR | 0777);\n    if (root_inode == NULL)\n        return _ENOMEM;\n\n    struct tmp_dirent *root = malloc(sizeof(struct tmp_dirent));\n    if (root == NULL) {\n        free(root_inode);\n        return _ENOMEM;\n    }\n\n    tmp_dirent_init(root);\n    strcpy(root->name, \"\");\n    root->inode = root_inode;\n    root->parent = NULL;\n\n    mount->data = root;\n    return 0;\n}\n\n#if 0\n// This is the only place where a tmpfs directory tree is recursively freed.\nstatic void tmpfs_unmount_tree(struct tmp_inode *tree) {\n    assert(refcount_get(tree) == 1); // otherwise mount_remove should have returned EBUSY\n    if (S_ISDIR(tree->stat.mode)) {\n        struct tmp_dirent *dirent, *tmp;\n        list_for_each_entry_safe(&tree->dir.entries, dirent, tmp, dir) {\n            if (dirent->inode != NULL)\n                tmpfs_unmount_tree(dirent->inode);\n            tmp_dirent_release(dirent);\n        }\n    }\n    tmp_inode_release(tree);\n}\n#endif\n\nstatic int tmpfs_umount(struct mount *UNUSED(mount)) {\n    // big fat fuckin TODO\n    // struct tmp_inode *root = mount->data;\n    // tmpfs_unmount_tree(root);\n    TODO(\"tmpfs umount\");\n    return 0;\n}\n\nstatic struct fd *tmpfs_open(struct mount *mount, const char *path, int flags, int mode) {\n    struct tmp_dirent *dirent;\n    if (flags & O_CREAT_) {\n        // FIXME: will create a file when given a path that ends with a slash\n        const char *filename;\n        struct tmp_dirent *parent = tmpfs_lookup_parent(mount, path, &filename);\n        if (IS_ERR(parent))\n            return ERR_PTR(PTR_ERR(parent));\n        lock(&parent->lock);\n        int err = 0;\n\n        dirent = tmpfs_dir_lookup(parent, filename);\n        if (flags & O_EXCL_ && !IS_ERR(dirent)) {\n            err = _EEXIST;\n            goto out_creat;\n        }\n\n        if (dirent == ERR_PTR(_ENOENT)) {\n            struct tmp_inode *inode = tmp_inode_new(S_IFREG | mode);\n            if (inode == NULL) {\n                err = _ENOMEM;\n                goto out_creat;\n            }\n            err = tmpfs_dir_link(parent, filename, inode, &dirent);\n            tmp_inode_release(inode);\n            if (err < 0) {\n                goto out_creat;\n            }\n        }\n\nout_creat:\n        if (err < 0) {\n            tmp_dirent_release(dirent);\n            dirent = ERR_PTR(err);\n        }\n        unlock(&parent->lock);\n        tmp_dirent_release(parent);\n    } else {\n        dirent = tmpfs_lookup(mount, path);\n    }\n    if (IS_ERR(dirent))\n        return ERR_PTR(PTR_ERR(dirent));\n\n    struct fd *fd = fd_create(&tmpfs_fdops);\n    if (fd == NULL) {\n        tmp_dirent_release(dirent);\n        return ERR_PTR(_ENOMEM);\n    }\n    fd->tmpfs.dirent = dirent;\n\n    fd->tmpfs.dir_pos = NULL;\n    lock(&dirent->lock);\n    if (!list_empty(&dirent->children)) {\n        tmpfs_fd_seekdir(fd, list_first_entry(&dirent->children, struct tmp_dirent, dir));\n    }\n    unlock(&dirent->lock);\n    return fd;\n}\n\nstatic int tmpfs_stat(struct mount *mount, const char *path, struct statbuf *stat) {\n    struct tmp_dirent *dirent = tmpfs_lookup(mount, path);\n    if (IS_ERR(dirent))\n        return PTR_ERR(dirent);\n    struct tmp_inode *inode = dirent->inode;\n    lock(&inode->lock);\n    *stat = dirent->inode->stat;\n    unlock(&inode->lock);\n    tmp_dirent_release(dirent);\n    return 0;\n}\n\nstatic int tmpfs_close(struct fd *fd) {\n    // shouldn't need locking as this is the last reference to the fd\n    tmp_dirent_release(fd->tmpfs.dirent);\n    fd->tmpfs.dirent = NULL;\n    return 0;\n}\n\nstatic int tmpfs_mkdir(struct mount *mount, const char *path, mode_t_ mode) {\n    const char *filename;\n    struct tmp_dirent *parent = tmpfs_lookup_parent(mount, path, &filename);\n    if (IS_ERR(parent))\n        return PTR_ERR(parent);\n    lock(&parent->lock);\n\n    int err = tmpfs_dir_lookup_existence(parent, filename);\n    if (err < 0)\n        goto out;\n\n    struct tmp_inode *inode = tmp_inode_new(S_IFDIR | mode);\n    err = _ENOMEM;\n    if (inode == NULL)\n        goto out;\n\n    err = tmpfs_dir_link(parent, filename, inode, NULL);\nout:\n    unlock(&parent->lock);\n    tmp_dirent_release(parent);\n    return err;\n}\n\n// ========================\n// ======== FD OPS ========\n// ========================\n\nstatic struct tmp_inode *tmpfs_fd_inode(struct fd *fd) {\n    return fd->tmpfs.dirent->inode;\n}\n\nstatic int tmpfs_getpath(struct fd *fd, char *buf) {\n    struct tmp_dirent *dirent = fd->tmpfs.dirent;\n    struct tmp_dirent *root_dirent = fd->mount->data;\n    char *p = buf + MAX_PATH - 1;\n    *p = '\\0';\n    while (dirent != root_dirent) {\n        size_t name_len = strlen(dirent->name);\n        p -= name_len + 1;\n        if (p < buf)\n            return _ENAMETOOLONG;\n        p[0] = '/';\n        memcpy(&p[1], dirent->name, name_len);\n    }\n    memmove(buf, p, strlen(p) + 1);\n    return 0;\n}\n\nstatic int tmpfs_fstat(struct fd *fd, struct statbuf *stat) {\n    struct tmp_inode *inode = tmpfs_fd_inode(fd);\n    lock(&inode->lock);\n    *stat = inode->stat;\n    unlock(&inode->lock);\n    return 0;\n}\n\nstatic ssize_t tmpfs_read(struct fd *fd, void *buf, size_t bufsize) {\n    ssize_t res;\n    struct tmp_inode *inode = tmpfs_fd_inode(fd);\n    lock(&inode->lock);\n    res = _EISDIR;\n    if (S_ISDIR(inode->stat.mode))\n        goto out;\n    assert(S_ISREG(inode->stat.mode));\n\n    if (bufsize > inode->stat.size - fd->offset) {\n        bufsize = inode->stat.size - fd->offset;\n        if (fd->offset >= inode->stat.size)\n            bufsize = 0;\n    }\n    memcpy(buf, inode->file_data + fd->offset, bufsize);\n    fd->offset += bufsize;\n    res = bufsize;\n\nout:\n    unlock(&inode->lock);\n    return res;\n}\n\nstatic ssize_t tmpfs_write(struct fd *fd, const void *buf, size_t bufsize) {\n    ssize_t res;\n    struct tmp_inode *inode = tmpfs_fd_inode(fd);\n    lock(&inode->lock);\n    res = _EISDIR;\n    if (S_ISDIR(inode->stat.mode))\n        goto out;\n    assert(S_ISREG(inode->stat.mode));\n\n    if (inode->stat.size < fd->offset + bufsize) {\n        res = tmpfs_file_resize(inode, fd->offset + bufsize);\n        if (res < 0)\n            goto out;\n    }\n    memcpy(inode->file_data + fd->offset, buf, bufsize);\n    fd->offset += bufsize;\n    res = bufsize;\n\nout:\n    unlock(&inode->lock);\n    return res;\n}\n\nstatic off_t_ tmpfs_lseek(struct fd *fd, off_t_ off, int whence) {\n    qword_t size = 0;\n    if (whence == LSEEK_END) {\n        struct tmp_inode *inode = tmpfs_fd_inode(fd);\n        lock(&inode->lock);\n        size = inode->stat.size;\n        unlock(&inode->lock);\n    }\n\n    int err = generic_seek(fd, off, whence, size);\n    if (err < 0)\n        return err;\n\n    return fd->offset;\n}\n\nstatic int tmpfs_readdir(struct fd *fd, struct dir_entry *entry) {\n    struct tmp_dirent *parent = fd->tmpfs.dirent;\n    int res = _ENOTDIR;\n    if (!S_ISDIR(parent->inode->stat.mode))\n        goto out;\n\n    lock(&fd->lock);\n    lock(&parent->lock);\n    struct tmp_dirent *dirent = fd->tmpfs.dir_pos;\n    if (dirent == NULL) {\n        res = 0;\n        goto out;\n    }\n    struct tmp_dirent *next_dirent = list_next_entry(dirent, dir);\n    if (&next_dirent->dir == &parent->children) // end of list\n        next_dirent = NULL;\n    tmpfs_fd_seekdir(fd, next_dirent);\n\n    entry->inode = dirent->inode->stat.inode;\n    strcpy(entry->name, dirent->name);\n    res = 1;\n\nout:\n    unlock(&parent->lock);\n    unlock(&fd->lock);\n    return res;\n}\n\nstatic unsigned long tmpfs_telldir(struct fd *fd) {\n    if (fd->tmpfs.dir_pos == NULL)\n        return (unsigned long) -1;\n    return fd->tmpfs.dir_pos->index;\n}\n\nstatic void tmpfs_seekdir(struct fd *fd, unsigned long ptr) {\n    struct tmp_dirent *dir = fd->tmpfs.dirent;\n    lock(&dir->lock);\n    assert(S_ISDIR(dir->inode->stat.mode));\n    struct tmp_dirent *child;\n    list_for_each_entry(&dir->children, child, dir) {\n        if (child->index >= ptr)\n            break;\n    }\n    if (&child->dir == &dir->children)\n        child = NULL;\n    tmpfs_fd_seekdir(fd, child);\n    unlock(&dir->lock);\n}\n\nconst struct fs_ops tmpfs = {\n    .name = \"tmpfs\", .magic = 0x01021994,\n    .mount = tmpfs_mount,\n    .umount = tmpfs_umount,\n    .open = tmpfs_open,\n    .close = tmpfs_close,\n    .stat = tmpfs_stat,\n    .fstat = tmpfs_fstat,\n    .getpath = tmpfs_getpath,\n    .mkdir = tmpfs_mkdir,\n};\n\nconst struct fd_ops tmpfs_fdops = {\n    .read = tmpfs_read,\n    .write = tmpfs_write,\n    .lseek = tmpfs_lseek,\n    .readdir = tmpfs_readdir,\n    .telldir = tmpfs_telldir,\n    .seekdir = tmpfs_seekdir,\n};\n"
  },
  {
    "path": "fs/tty-real.c",
    "content": "#include \"debug.h\"\n#include <string.h>\n#include <unistd.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <pthread.h>\n#include <signal.h>\n\n#include \"kernel/calls.h\"\n#include \"fs/tty.h\"\n#include \"fs/devices.h\"\n\n// Only /dev/tty1 will be connected, the rest will go to a black hole.\n#define REAL_TTY_NUM 1\n\nvoid real_tty_reset_term(void);\n\nstatic void *real_tty_read_thread(void *_tty) {\n    struct tty *tty = _tty;\n    char ch;\n    for (;;) {\n        int err = read(STDIN_FILENO, &ch, 1);\n        if (err != 1) {\n            printk(\"tty read returned %d\\n\", err);\n            if (err < 0)\n                printk(\"error: %s\\n\", strerror(errno));\n            continue;\n        }\n        if (ch == '\\x1c') {\n            // ^\\ (so ^C still works for emulated SIGINT)\n            real_tty_reset_term();\n            raise(SIGINT);\n        }\n        tty_input(tty, &ch, 1, 0);\n    }\n    return NULL;\n}\n\nstatic struct termios_ termios_from_real(struct termios real) {\n    struct termios_ fake = {};\n#define FLAG(t, x) \\\n    if (real.c_##t##flag & x) \\\n        fake.t##flags |= x##_\n    FLAG(o, OPOST);\n    FLAG(o, ONLCR);\n    FLAG(o, OCRNL);\n    FLAG(o, ONOCR);\n    FLAG(o, ONLRET);\n    FLAG(i, INLCR);\n    FLAG(i, IGNCR);\n    FLAG(i, ICRNL);\n    FLAG(l, ISIG);\n    FLAG(l, ICANON);\n    FLAG(l, ECHO);\n    FLAG(l, ECHOE);\n    FLAG(l, ECHOK);\n    FLAG(l, NOFLSH);\n    FLAG(l, ECHOCTL);\n#undef FLAG\n\n#define CC(x) \\\n    fake.cc[V##x##_] = real.c_cc[V##x]\n    CC(INTR);\n    CC(QUIT);\n    CC(ERASE);\n    CC(KILL);\n    CC(EOF);\n    CC(TIME);\n    CC(MIN);\n    CC(START);\n    CC(STOP);\n    CC(SUSP);\n    CC(EOL);\n    CC(REPRINT);\n    CC(DISCARD);\n    CC(WERASE);\n    CC(LNEXT);\n    CC(EOL2);\n#undef CC\n    return fake;\n}\n\nstatic struct termios old_termios;\nstatic bool real_tty_is_open;\nstatic int real_tty_init(struct tty *tty) {\n    if (tty->num != REAL_TTY_NUM)\n        return 0;\n\n    struct winsize winsz;\n    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsz) < 0) {\n        if (errno == ENOTTY)\n            goto notty;\n        return errno_map();\n    }\n    tty->winsize.col = winsz.ws_col;\n    tty->winsize.row = winsz.ws_row;\n    tty->winsize.xpixel = winsz.ws_xpixel;\n    tty->winsize.ypixel = winsz.ws_ypixel;\n\n    struct termios termios;\n    if (tcgetattr(STDIN_FILENO, &termios) < 0)\n        return errno_map();\n    tty->termios = termios_from_real(termios);\n\n    old_termios = termios;\n    cfmakeraw(&termios);\n#ifdef NO_CRLF\n    termios.c_oflag |= OPOST | ONLCR;\n#endif\n    if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) < 0)\n        ERRNO_DIE(\"failed to set terminal to raw mode\");\nnotty:\n\n    if (pthread_create(&tty->thread, NULL,  real_tty_read_thread, tty) < 0)\n        // ok if this actually happened it would be weird AF\n        return _EIO;\n    pthread_detach(tty->thread);\n    real_tty_is_open = true;\n    return 0;\n}\n\nstatic int real_tty_write(struct tty *tty, const void *buf, size_t len, bool UNUSED(blocking)) {\n    if (tty->num != REAL_TTY_NUM)\n        return len;\n    return write(STDOUT_FILENO, buf, len);\n}\n\nvoid real_tty_reset_term() {\n    if (!real_tty_is_open) return;\n    if (tcsetattr(STDIN_FILENO, TCSANOW, &old_termios) < 0 && errno != ENOTTY) {\n        printk(\"failed to reset terminal: %s\\n\", strerror(errno));\n        abort();\n    }\n}\n\nstatic void real_tty_cleanup(struct tty *tty) {\n    if (tty->num != REAL_TTY_NUM)\n        return;\n    real_tty_reset_term();\n    pthread_cancel(tty->thread);\n}\n\nstruct tty_driver_ops real_tty_ops = {\n    .init = real_tty_init,\n    .write = real_tty_write,\n    .cleanup = real_tty_cleanup,\n};\nDEFINE_TTY_DRIVER(real_tty_driver, &real_tty_ops, TTY_CONSOLE_MAJOR, 64);\n"
  },
  {
    "path": "fs/tty.c",
    "content": "#define DEFAULT_CHANNEL debug\n#include \"debug.h\"\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"fs/poll.h\"\n#include \"fs/tty.h\"\n#include \"fs/devices.h\"\n\nextern struct tty_driver pty_master;\nextern struct tty_driver pty_slave;\n\nstruct tty_driver *tty_drivers[256] = {\n    [TTY_CONSOLE_MAJOR] = NULL, // will be filled in by create_stdio\n    [TTY_PSEUDO_MASTER_MAJOR] = &pty_master,\n    [TTY_PSEUDO_SLAVE_MAJOR] = &pty_slave,\n};\n\n// lock this before locking a tty\nlock_t ttys_lock = LOCK_INITIALIZER;\n\nstruct tty *tty_alloc(struct tty_driver *driver, int type, int num) {\n    struct tty *tty = malloc(sizeof(struct tty));\n    if (tty == NULL)\n        return NULL;\n\n    tty->refcount = 0;\n    tty->driver = driver;\n    tty->type = type;\n    tty->num = num;\n    tty->hung_up = false;\n    tty->ever_opened = false;\n    tty->session = 0;\n    tty->fg_group = 0;\n    list_init(&tty->fds);\n\n    tty->termios.iflags = ICRNL_ | IXON_;\n    tty->termios.oflags = OPOST_ | ONLCR_;\n    tty->termios.cflags = 0;\n    tty->termios.lflags = ISIG_ | ICANON_ | ECHO_ | ECHOE_ | ECHOK_ | ECHOCTL_ | ECHOKE_ | IEXTEN_;\n    // from include/asm-generic/termios.h\n    memcpy(tty->termios.cc, \"\\003\\034\\177\\025\\004\\0\\1\\0\\021\\023\\032\\0\\022\\017\\027\\026\\0\\0\\0\", 19);\n    memset(&tty->winsize, 0, sizeof(tty->winsize));\n\n    lock_init(&tty->lock);\n    lock_init(&tty->fds_lock);\n    cond_init(&tty->produced);\n    cond_init(&tty->consumed);\n    memset(tty->buf_flag, false, sizeof(tty->buf_flag));\n    tty->bufsize = 0;\n    tty->packet_flags = 0;\n\n    return tty;\n}\n\nstruct tty *tty_get(struct tty_driver *driver, int type, int num) {\n    lock(&ttys_lock);\n    struct tty *tty = driver->ttys[num];\n    // pty_reserve_next stores 1 to avoid races on the same tty\n    if (tty == NULL || tty == (void *) 1 /* ew */) {\n        tty = tty_alloc(driver, type, num);\n        if (tty == NULL) {\n            unlock(&ttys_lock);\n            return ERR_PTR(_ENOMEM);\n        }\n\n        if (driver->ops->init) {\n            int err = driver->ops->init(tty);\n            if (err < 0) {\n                unlock(&ttys_lock);\n                return ERR_PTR(err);\n            }\n        }\n        driver->ttys[num] = tty;\n    }\n    lock(&tty->lock);\n    tty->refcount++;\n    tty->ever_opened = true;\n    unlock(&tty->lock);\n    unlock(&ttys_lock);\n    return tty;\n}\n\nstatic struct tty *get_slave_side_tty(struct tty *tty) {\n  if (tty->type == TTY_PSEUDO_MASTER_MAJOR) {\n      return tty->pty.other;\n  } else {\n      return tty;\n  }\n}\n\nstatic void tty_poll_wakeup(struct tty *tty, int events) {\n    unlock(&tty->lock);\n    struct fd *fd;\n    lock(&tty->fds_lock);\n    list_for_each_entry(&tty->fds, fd, tty_other_fds) {\n        poll_wakeup(fd, events);\n    }\n    unlock(&tty->fds_lock);\n    lock(&tty->lock);\n}\n\nvoid tty_release(struct tty *tty) {\n    lock(&tty->lock);\n    if (--tty->refcount == 0) {\n        struct tty_driver *driver = tty->driver;\n        if (driver->ops->cleanup)\n            driver->ops->cleanup(tty);\n        driver->ttys[tty->num] = NULL;\n        unlock(&tty->lock);\n        cond_destroy(&tty->produced);\n        free(tty);\n    } else {\n        unlock(&tty->lock);\n    }\n}\n\n// must call with tty lock\nstatic void tty_set_controlling(struct tgroup *group, struct tty *tty) {\n    lock(&group->lock);\n    if (group->tty == NULL) {\n        tty->refcount++;\n        group->tty = tty;\n        tty->session = group->sid;\n        tty->fg_group = group->pgid;\n    }\n    unlock(&group->lock);\n}\n\n// by default, /dev/console is /dev/tty1\nint console_major = TTY_CONSOLE_MAJOR;\nint console_minor = 1;\n\nint tty_open(struct tty *tty, struct fd *fd) {\n    fd->tty = tty;\n\n    lock(&tty->fds_lock);\n    list_add(&tty->fds, &fd->tty_other_fds);\n    unlock(&tty->fds_lock);\n\n    if (!(fd->flags & O_NOCTTY_)) {\n        // Make this our controlling terminal if:\n        // - the terminal doesn't already have a session\n        // - we're a session leader\n        lock(&pids_lock);\n        lock(&tty->lock);\n        if (tty->session == 0 && current->group->sid == current->pid)\n            tty_set_controlling(current->group, tty);\n        unlock(&tty->lock);\n        unlock(&pids_lock);\n    }\n\n    return 0;\n}\n\nstatic int tty_device_open(int major, int minor, struct fd *fd) {\n    struct tty *tty;\n    if (major == TTY_ALTERNATE_MAJOR) {\n        if (minor == DEV_TTY_MINOR) {\n            lock(&ttys_lock);\n            lock(&current->group->lock);\n            tty = current->group->tty;\n            unlock(&current->group->lock);\n            if (tty != NULL) {\n                lock(&tty->lock);\n                tty->refcount++;\n                unlock(&tty->lock);\n            }\n            unlock(&ttys_lock);\n            if (tty == NULL)\n                return _ENXIO;\n        } else if (minor == DEV_CONSOLE_MINOR) {\n            return tty_device_open(console_major, console_minor, fd);\n        } else if (minor == DEV_PTMX_MINOR) {\n            return ptmx_open(fd);\n        } else {\n            return _ENXIO;\n        }\n    } else {\n        struct tty_driver *driver = tty_drivers[major];\n        assert(driver != NULL);\n        tty = tty_get(driver, major, minor);\n        if (IS_ERR(tty))\n            return PTR_ERR(tty);\n    }\n\n    if (tty->driver->ops->open) {\n        int err = tty->driver->ops->open(tty);\n        if (err < 0) {\n            lock(&ttys_lock);\n            tty_release(tty);\n            unlock(&ttys_lock);\n            return err;\n        }\n    }\n\n    return tty_open(tty, fd);\n}\n\nstatic int tty_close(struct fd *fd) {\n    if (fd->tty != NULL) {\n        struct tty *tty = fd->tty;\n        lock(&tty->fds_lock);\n        list_remove_safe(&fd->tty_other_fds);\n        unlock(&tty->fds_lock);\n        lock(&ttys_lock);\n        if (tty->driver->ops->close)\n            tty->driver->ops->close(tty);\n        tty_release(tty);\n        unlock(&ttys_lock);\n    }\n    return 0;\n}\n\nstatic void tty_input_wakeup(struct tty *tty) {\n    notify(&tty->produced);\n    tty_poll_wakeup(tty, POLL_READ);\n}\n\nstatic int tty_push_char(struct tty *tty, char ch, bool flag, int blocking) {\n    while (tty->bufsize >= sizeof(tty->buf)) {\n        if (!blocking)\n            return _EAGAIN;\n        if (wait_for(&tty->consumed, &tty->lock, NULL))\n            return _EINTR;\n    }\n    tty->buf[tty->bufsize] = ch;\n    tty->buf_flag[tty->bufsize++] = flag;\n    return 0;\n}\n\nstatic void tty_echo(struct tty *tty, const char *data, size_t size) {\n    tty->driver->ops->write(tty, data, size, false);\n}\n\nstatic bool tty_send_input_signal(struct tty *tty, char ch, sigset_t_ *queue) {\n    if (!(tty->termios.lflags & ISIG_))\n        return false;\n    unsigned char *cc = tty->termios.cc;\n    int sig;\n    if (ch == '\\0')\n        return false; // '\\0' is used to disable cc entries\n    else if (ch == cc[VINTR_])\n        sig = SIGINT_;\n    else if (ch == cc[VQUIT_])\n        sig = SIGQUIT_;\n    else if (ch == cc[VSUSP_])\n        sig = SIGTSTP_;\n    else\n        return false;\n\n    if (tty->fg_group != 0) {\n        if (!(tty->termios.lflags & NOFLSH_))\n            tty->bufsize = 0;\n        sigset_add(queue, sig);\n    }\n    return true;\n}\n\nssize_t tty_input(struct tty *tty, const char *input, size_t size, bool blocking) {\n    int err = 0;\n    size_t done_size = 0;\n    sigset_t_ queue = 0; // to prevent having to lock tty->lock and pids_lock at the same time\n\n    lock(&tty->lock);\n    dword_t lflags = tty->termios.lflags;\n    dword_t iflags = tty->termios.iflags;\n    unsigned char *cc = tty->termios.cc;\n\n#define SHOULD_ECHOCTL(ch) \\\n    (lflags & ECHOCTL_ && \\\n     ((0 <= ch && ch < ' ') || ch == '\\x7f') && \\\n     !(ch == '\\t' || ch == '\\n' || ch == cc[VSTART_] || ch == cc[VSTOP_]))\n\n    if (lflags & ICANON_) {\n        for (size_t i = 0; i < size; i++) {\n            done_size++;\n            char ch = input[i];\n            bool echo = lflags & ECHO_;\n\n            if (iflags & INLCR_ && ch == '\\n')\n                ch = '\\r';\n            else if (iflags & ICRNL_ && ch == '\\r')\n                ch = '\\n';\n            if (iflags & IGNCR_ && ch == '\\r')\n                continue;\n\n            if (ch == '\\0') {\n                // '\\0' is used to disable cc entries\n                goto no_special;\n            } else if (ch == cc[VERASE_] || ch == cc[VKILL_]) {\n                // FIXME ECHOE and ECHOK are supposed to enable these\n                // ECHOKE enables erasing the line instead of echoing the kill char and outputting a newline\n                echo = lflags & ECHOK_;\n                int count = tty->bufsize;\n                if (ch == cc[VERASE_] && tty->bufsize > 0) {\n                    echo = lflags & ECHOE_;\n                    count = 1;\n                }\n                if (!(lflags & ECHO_))\n                    echo = false;\n                for (int i = 0; i < count; i++) {\n                    // don't delete past a flag\n                    if (tty->buf_flag[tty->bufsize - 1])\n                        break;\n                    tty->bufsize--;\n                    if (echo) {\n                        tty_echo(tty, \"\\b \\b\", 3);\n                        if (SHOULD_ECHOCTL(tty->buf[tty->bufsize]))\n                            tty_echo(tty, \"\\b \\b\", 3);\n                    }\n                }\n                echo = false;\n            } else if (ch == cc[VEOF_]) {\n                ch = '\\0';\n                goto canon_wake;\n            } else if (ch == '\\n' || ch == cc[VEOL_]) {\n                // echo it now, before the read call goes through\n                if (echo)\n                    tty_echo(tty, \"\\r\\n\", 2);\ncanon_wake:\n                err = tty_push_char(tty, ch, /*flag*/true, blocking);\n                if (err < 0) {\n                    done_size--;\n                    break;\n                }\n                echo = false;\n                tty_input_wakeup(tty);\n            } else {\n                if (!tty_send_input_signal(tty, ch, &queue)) {\nno_special:\n                    err = tty_push_char(tty, ch, /*flag*/false, blocking);\n                    if (err < 0) {\n                        done_size--;\n                        break;\n                    }\n                }\n            }\n\n            if (echo) {\n                if (SHOULD_ECHOCTL(ch)) {\n                    tty_echo(tty, \"^\", 1);\n                    ch ^= '\\100';\n                }\n                tty_echo(tty, &ch, 1);\n            }\n        }\n    } else {\n        for (size_t i = 0; i < size; i++) {\n            done_size++;\n            if (tty_send_input_signal(tty, input[i], &queue))\n                continue;\n            while (tty->bufsize >= sizeof(tty->buf)) {\n                err = _EAGAIN;\n                if (!blocking)\n                    break;\n                err = wait_for(&tty->consumed, &tty->lock, NULL);\n                if (err < 0)\n                    break;\n            }\n            if (err < 0) {\n                done_size--;\n                break;\n            }\n            assert(tty->bufsize < sizeof(tty->buf));\n            tty->buf[tty->bufsize++] = input[i];\n        }\n        if (tty->bufsize > 0)\n            tty_input_wakeup(tty);\n    }\n\n    pid_t_ fg_group = tty->fg_group;\n    assert(tty->bufsize <= sizeof(tty->buf));\n    unlock(&tty->lock);\n\n    if (fg_group != 0) {\n        for (int sig = 1; sig < NUM_SIGS; sig++) {\n            if (sigset_has(queue, sig))\n                send_group_signal(fg_group, sig, SIGINFO_NIL);\n        }\n    }\n\n    if (done_size > 0)\n        return done_size;\n    return err;\n}\n\n// expects bufsize <= tty->bufsize\nstatic void tty_read_into_buf(struct tty *tty, void *buf, size_t bufsize) {\n    assert(bufsize <= tty->bufsize);\n    memcpy(buf, tty->buf, bufsize);\n    tty->bufsize -= bufsize;\n    memmove(tty->buf, tty->buf + bufsize, tty->bufsize); // magic!\n    memmove(tty->buf_flag, tty->buf_flag + bufsize, tty->bufsize);\n    notify(&tty->consumed);\n}\n\nstatic size_t tty_canon_size(struct tty *tty) {\n    bool *flag_ptr = memchr(tty->buf_flag, true, tty->bufsize);\n    if (flag_ptr == NULL)\n        return -1;\n    return flag_ptr - tty->buf_flag + 1;\n}\n\nstatic bool pty_is_half_closed_master(struct tty *tty) {\n    if (tty->driver != &pty_master)\n        return false;\n\n    struct tty *slave = tty->pty.other;\n    // only time one tty lock is nested in another\n    lock(&slave->lock);\n    bool half_closed = slave->ever_opened && (slave->refcount == 1 || slave->hung_up);\n    unlock(&slave->lock);\n    return half_closed;\n}\n\nstatic bool tty_is_current(struct tty *tty) {\n    lock(&current->group->lock);\n    bool is_current = current->group->tty == tty;\n    unlock(&current->group->lock);\n    return is_current;\n}\n\nstatic int tty_signal_if_background(struct tty *tty, pid_t_ current_pgid, int sig) {\n    // you can apparently access a terminal that's not your controlling\n    // terminal all you want\n    if (!tty_is_current(tty))\n        return 0;\n    // check if we're in the foreground\n    if (tty->fg_group == 0 || current_pgid == tty->fg_group)\n        return 0;\n\n    if (!try_self_signal(sig))\n        return _EIO;\n    else\n        return _EINTR;\n}\n\nstatic ssize_t tty_read(struct fd *fd, void *buf, size_t bufsize) {\n    // important because otherwise we'll block\n    if (bufsize == 0)\n        return 0;\n\n    int err = 0;\n    struct tty *tty = fd->tty;\n    lock(&pids_lock);\n    lock(&tty->lock);\n    if (tty->hung_up || pty_is_half_closed_master(tty)) {\n        unlock(&pids_lock);\n        goto error;\n    }\n\n    pid_t_ current_pgid = current->group->pgid;\n    unlock(&pids_lock);\n    err = tty_signal_if_background(tty, current_pgid, SIGTTIN_);\n    if (err < 0)\n        goto error;\n\n    int bufsize_extra = 0;\n    if (tty->driver == &pty_master && tty->pty.packet_mode) {\n        char *cbuf = buf;\n        *cbuf++ = tty->packet_flags;\n        bufsize--;\n        bufsize_extra++;\n        buf = cbuf;\n        if (tty->packet_flags != 0) {\n            bufsize = 0;\n            goto out;\n        }\n\n        // check again in case bufsize was 1\n        if (bufsize == 0)\n            goto out;\n    }\n\n    // wait loop(s)\n    if (tty->termios.lflags & ICANON_) {\n        size_t canon_size;\n        while ((canon_size = tty_canon_size(tty)) == (size_t) -1) {\n            err = _EIO;\n            if (pty_is_half_closed_master(tty))\n                goto error;\n            err = _EAGAIN;\n            if (fd->flags & O_NONBLOCK_)\n                goto error;\n            err = wait_for(&tty->produced, &tty->lock, NULL);\n            if (err < 0)\n                goto error;\n        }\n        // null byte means eof was typed\n        if (tty->buf[canon_size-1] == '\\0')\n            canon_size--;\n\n        if (bufsize > canon_size)\n            bufsize = canon_size;\n    } else {\n        dword_t min = tty->termios.cc[VMIN_];\n        dword_t time = tty->termios.cc[VTIME_];\n\n        struct timespec timeout;\n        // time is in tenths of a second\n        timeout.tv_sec = time / 10;\n        timeout.tv_nsec = (time % 10) * 100000000;\n        struct timespec *timeout_ptr = &timeout;\n        if (time == 0)\n            timeout_ptr = NULL;\n\n        while (tty->bufsize < min) {\n            err = _EIO;\n            if (pty_is_half_closed_master(tty))\n                goto error;\n            err = _EAGAIN;\n            if (fd->flags & O_NONBLOCK_)\n                goto error;\n            // there should be no timeout for the first character read\n            err = wait_for(&tty->produced, &tty->lock, tty->bufsize == 0 ? NULL : timeout_ptr);\n            if (err == _ETIMEDOUT)\n                break;\n            if (err == _EINTR)\n                goto error;\n        }\n    }\n\n    if (bufsize > tty->bufsize)\n        bufsize = tty->bufsize;\n    tty_read_into_buf(tty, buf, bufsize);\n    if (tty->bufsize > 0 && tty->buf[0] == '\\0' && tty->buf_flag[0]) {\n        // remove the eof so the next read can succeed\n        char dummy;\n        tty_read_into_buf(tty, &dummy, 1);\n    }\n\nout:\n    unlock(&tty->lock);\n    return bufsize + bufsize_extra;\nerror:\n    unlock(&tty->lock);\n    return err;\n}\n\nstatic ssize_t tty_write(struct fd *fd, const void *buf, size_t bufsize) {\n    struct tty *tty = fd->tty;\n    lock(&tty->lock);\n    if (tty->hung_up || pty_is_half_closed_master(tty)) {\n        unlock(&tty->lock);\n        return _EIO;\n    }\n\n    bool blocking = !(fd->flags & O_NONBLOCK_);\n    dword_t oflags = tty->termios.oflags;\n    // we have to unlock it now to avoid lock ordering problems with ptys\n    // the code below is safe because it only accesses tty->driver which is immutable\n    // I reviewed real driver and ios driver and they're safe\n    unlock(&tty->lock);\n\n    int err = 0;\n    char *postbuf = NULL;\n    size_t postbufsize = bufsize;\n    if (oflags & OPOST_) {\n        postbuf = malloc(bufsize * 2);\n        postbufsize = 0;\n        const char *cbuf = buf;\n        for (size_t i = 0; i < bufsize; i++) {\n            char ch = cbuf[i];\n            if (ch == '\\r' && oflags & ONLRET_)\n                continue;\n            else if (ch == '\\r' && oflags & OCRNL_)\n                ch = '\\n';\n            else if (ch == '\\n' && oflags & ONLCR_)\n                postbuf[postbufsize++] = '\\r';\n            postbuf[postbufsize++] = ch;\n        }\n        buf = postbuf;\n    }\n    err = tty->driver->ops->write(tty, buf, postbufsize, blocking);\n    if (postbuf)\n        free(postbuf);\n    if (err < 0)\n        return err;\n    return bufsize;\n}\n\nstatic int tty_poll(struct fd *fd) {\n    struct tty *tty = fd->tty;\n    lock(&tty->lock);\n    int types = 0;\n    types |= POLL_WRITE; // FIXME now that we have ptys, you can't always write without blocking\n    if (tty->hung_up) {\n        types |= POLL_READ | POLL_WRITE | POLL_ERR | POLL_HUP;\n    } else if (pty_is_half_closed_master(tty)) {\n        types |= POLL_READ | POLL_HUP;\n    } else if (tty->termios.lflags & ICANON_) {\n        if (tty_canon_size(tty) != (size_t) -1)\n            types |= POLL_READ;\n    } else {\n        if (tty->bufsize > 0)\n            types |= POLL_READ;\n    }\n    if (tty->driver == &pty_master && tty->packet_flags != 0)\n        types |= POLL_PRI;\n    unlock(&tty->lock);\n    return types;\n}\n\nstatic ssize_t tty_ioctl_size(int cmd) {\n    switch (cmd) {\n        case TCGETS_: case TCSETS_: case TCSETSF_: case TCSETSW_:\n            return sizeof(struct termios_);\n        case TIOCGWINSZ_: case TIOCSWINSZ_:\n            return sizeof(struct winsize_);\n        case TIOCGPGRP_: case TIOCSPGRP_:\n        case TIOCSPTLCK_: case TIOCGPTN_:\n        case TIOCPKT_: case TIOCGPKT_:\n        case FIONREAD_:\n            return sizeof(dword_t);\n        case TCFLSH_: case TIOCSCTTY_:\n            return 0;\n    }\n    return -1;\n}\n\nstatic int tiocsctty(struct tty *tty, int force) {\n    int err = 0;\n    unlock(&tty->lock); //aaaaaaaa\n    // it's safe because literally nothing happens between that unlock and the last lock, and repulsive for the same reason\n    // locking is ***hard**\n    lock(&pids_lock);\n    lock(&tty->lock);\n    // do nothing if this is already our controlling tty\n    if (current->group->sid == current->pid && current->group->sid == tty->session)\n        goto out;\n    // must not already have a tty\n    if (current->group->tty != NULL) {\n        err = _EPERM;\n        goto out;\n    }\n\n    if (tty->session) {\n        if (force == 1 && superuser()) {\n            // steal it\n            struct pid *pid = pid_get(tty->session);\n            struct tgroup *tgroup;\n            list_for_each_entry(&pid->session, tgroup, session) {\n                lock(&tgroup->lock);\n                if (tgroup->tty == tty) {\n                    tgroup->tty = NULL;\n                    tty->refcount--;\n                }\n                unlock(&tgroup->lock);\n            }\n        } else {\n            err = _EPERM;\n            goto out;\n        }\n    }\n\n    tty_set_controlling(current->group, tty);\nout:\n    unlock(&pids_lock);\n    return err;\n}\n\nstatic int tiocgpgrp(struct tty *tty, pid_t_ *fg_group) {\n    int err = 0;\n    struct tty *slave = get_slave_side_tty(tty);\n    if (slave != tty) {\n        lock(&slave->lock);\n    }\n\n    if (tty == slave && (!tty_is_current(slave) || slave->fg_group == 0)) {\n        err = _ENOTTY;\n        goto error_no_ctrl_tty;\n    }\n    *fg_group = slave->fg_group;\n    STRACE(\"tty group = %d\\n\", slave->fg_group);\n\nerror_no_ctrl_tty:\n    if (slave != tty)\n        unlock(&slave->lock);\n    return err;\n}\n\n// These ioctls are separated out because they have to operate on the slave\n// side of a pseudoterminal pair even if the master is specified\nstatic int tty_mode_ioctl(struct tty *in_tty, int cmd, void *arg) {\n    int err = 0;\n    struct tty *tty = in_tty;\n    if (in_tty->driver == &pty_master) {\n        tty = in_tty->pty.other;\n        lock(&tty->lock);\n    }\n\n    switch (cmd) {\n        case TCGETS_:\n            *(struct termios_ *) arg = tty->termios;\n            break;\n        case TCSETSF_:\n            tty->bufsize = 0;\n            notify(&tty->consumed);\n            fallthrough;\n        case TCSETSW_:\n            // we have no output buffer currently\n        case TCSETS_:\n            tty->termios = *(struct termios_ *) arg;\n            break;\n\n        case TIOCGWINSZ_:\n            *(struct winsize_ *) arg = tty->winsize;\n            break;\n        case TIOCSWINSZ_:\n            tty_set_winsize(tty, *(struct winsize_ *) arg);\n            break;\n\n        default:\n            err = _ENOTTY;\n            break;\n    }\n\n    if (in_tty->driver == &pty_master)\n        unlock(&tty->lock);\n    return err;\n}\n\nstatic int tty_ioctl(struct fd *fd, int cmd, void *arg) {\n    int err = 0;\n    struct tty *tty = fd->tty;\n    lock(&tty->lock);\n    if (tty->hung_up) {\n        unlock(&tty->lock);\n        if (cmd == TIOCSPGRP_)\n            return _ENOTTY;\n        return _EIO;\n    }\n\n    switch (cmd) {\n        case TCFLSH_:\n            // only input flushing is currently useful\n            switch ((uintptr_t) arg) {\n                case TCIFLUSH_:\n                case TCIOFLUSH_:\n                    tty->bufsize = 0;\n                    notify(&tty->consumed);\n                    break;\n                case TCOFLUSH_:\n                    break;\n                default:\n                    err = _EINVAL;\n                    break;\n            };\n            break;\n\n        case TIOCSCTTY_:\n            err = tiocsctty(tty, (uintptr_t) arg);\n            break;\n\n        case TIOCGPGRP_:\n            err = tiocgpgrp(tty, (pid_t_ *) arg);\n            break;\n\n        case TIOCSPGRP_:\n            // see \"aaaaaaaa\" comment above\n            unlock(&tty->lock);\n            lock(&pids_lock);\n            lock(&tty->lock);\n            pid_t_ sid = current->group->sid;\n            unlock(&pids_lock);\n            if (!tty_is_current(tty) || sid != tty->session) {\n                err = _ENOTTY;\n                break;\n            }\n            // TODO group must be in the right session\n            tty->fg_group = *(dword_t *) arg;\n            STRACE(\"tty group set to = %d\\n\", tty->fg_group);\n            break;\n\n        case FIONREAD_:\n            *(dword_t *) arg = tty->bufsize;\n            break;\n\n        default:\n            err = tty_mode_ioctl(tty, cmd, arg);\n            if (err == _ENOTTY && tty->driver->ops->ioctl)\n                err = tty->driver->ops->ioctl(tty, cmd, arg);\n    }\n\n    unlock(&tty->lock);\n    return err;\n}\n\nvoid tty_set_winsize(struct tty *tty, struct winsize_ winsize) {\n    tty->winsize = winsize;\n    if (tty->fg_group != 0)\n        send_group_signal(tty->fg_group, SIGWINCH_, SIGINFO_NIL);\n}\n\nvoid tty_hangup(struct tty *tty) {\n    tty->hung_up = true;\n    tty_input_wakeup(tty);\n}\n\nstruct dev_ops tty_dev = {\n    .open = tty_device_open,\n    .fd.close = tty_close,\n    .fd.read = tty_read,\n    .fd.write = tty_write,\n    .fd.poll = tty_poll,\n    .fd.ioctl_size = tty_ioctl_size,\n    .fd.ioctl = tty_ioctl,\n};\n"
  },
  {
    "path": "fs/tty.h",
    "content": "#ifndef TTY_H\n#define TTY_H\n\n#include \"kernel/fs.h\"\n#include \"fs/dev.h\"\n\nstruct winsize_ {\n    word_t row;\n    word_t col;\n    word_t xpixel;\n    word_t ypixel;\n};\n\n// This is the definition of __kernel_termios from glibc\nstruct termios_ {\n    dword_t iflags;\n    dword_t oflags;\n    dword_t cflags;\n    dword_t lflags;\n    byte_t line;\n    byte_t cc[19];\n};\n\n#define VINTR_ 0\n#define VQUIT_ 1\n#define VERASE_ 2\n#define VKILL_ 3\n#define VEOF_ 4\n#define VTIME_ 5\n#define VMIN_ 6\n#define VSWTC_ 7\n#define VSTART_ 8\n#define VSTOP_ 9\n#define VSUSP_ 10\n#define VEOL_ 11\n#define VREPRINT_ 12\n#define VDISCARD_ 13\n#define VWERASE_ 14\n#define VLNEXT_ 15\n#define VEOL2_ 16\n\n#define ISIG_ (1 << 0)\n#define ICANON_ (1 << 1)\n#define ECHO_ (1 << 3)\n#define ECHOE_ (1 << 4)\n#define ECHOK_ (1 << 5)\n#define ECHOKE_ (1 << 6)\n#define NOFLSH_ (1 << 7)\n#define ECHOCTL_ (1 << 9)\n#define IEXTEN_ (1 << 15)\n\n#define INLCR_ (1 << 6)\n#define IGNCR_ (1 << 7)\n#define ICRNL_ (1 << 8)\n#define IXON_ (1 << 10)\n\n#define OPOST_ (1 << 0)\n#define ONLCR_ (1 << 2)\n#define OCRNL_ (1 << 3)\n#define ONOCR_ (1 << 4)\n#define ONLRET_ (1 << 5)\n\n#define TCGETS_ 0x5401\n#define TCSETS_ 0x5402\n#define TCSETSW_ 0x5403\n#define TCSETSF_ 0x5404\n#define TCFLSH_ 0x540b\n#define TIOCSCTTY_ 0x540e\n#define TIOCGPGRP_ 0x540f\n#define TIOCSPGRP_ 0x5410\n#define TIOCGWINSZ_ 0x5413\n#define TIOCSWINSZ_ 0x5414\n#define TIOCPKT_ 0x5420\n#define TIOCGPTN_ 0x80045430\n#define TIOCSPTLCK_ 0x40045431\n#define TIOCGPKT_ 0x80045438\n\n#define TCIFLUSH_ 0\n#define TCOFLUSH_ 1\n#define TCIOFLUSH_ 2\n\nstruct tty_driver {\n    const struct tty_driver_ops *ops;\n    int major;\n    struct tty **ttys;\n    unsigned limit;\n};\n\n#define DEFINE_TTY_DRIVER(name, driver_ops, _major, size) \\\n    static struct tty *name##_ttys[size]; \\\n    struct tty_driver name = {.ops = driver_ops, .major = _major, .ttys = name##_ttys, .limit = size}\n\nstruct tty_driver_ops {\n    int (*init)(struct tty *tty);\n    int (*open)(struct tty *tty);\n    int (*close)(struct tty *tty);\n    int (*write)(struct tty *tty, const void *buf, size_t len, bool blocking);\n    int (*ioctl)(struct tty *tty, int cmd, void *arg);\n    void (*cleanup)(struct tty *tty);\n};\n\n// indexed by major number\nextern struct tty_driver *tty_drivers[256];\nextern struct tty_driver real_tty_driver;\n\nstruct tty {\n    unsigned refcount;\n    struct tty_driver *driver;\n    bool hung_up;\n    bool ever_opened;\n\n#define TTY_BUF_SIZE 4096\n    char buf[TTY_BUF_SIZE];\n    // A flag is a marker indicating the end of a canonical mode input. Flags\n    // are created by EOL and EOF characters. You can't backspace past a flag.\n    bool buf_flag[TTY_BUF_SIZE];\n    size_t bufsize;\n    uint8_t packet_flags;\n    cond_t produced;\n    cond_t consumed;\n\n    struct winsize_ winsize;\n    struct termios_ termios;\n    int type;\n    int num;\n\n    pid_t_ session;\n    pid_t_ fg_group;\n\n    struct list fds;\n    // only locks fds, to keep the lock order\n    lock_t fds_lock;\n\n    // this never nests with itself, except in pty_is_half_closed_master\n    lock_t lock;\n\n    union {\n        pthread_t thread; // for real tty driver\n        struct {\n            struct tty *other;\n            mode_t_ perms;\n            uid_t_ uid;\n            uid_t_ gid;\n            bool locked;\n            bool packet_mode;\n        } pty;\n        void *data;\n    };\n};\n\n// if blocking, may return _EINTR, otherwise, may return _EAGAIN\nssize_t tty_input(struct tty *tty, const char *input, size_t len, bool blocking);\nvoid tty_set_winsize(struct tty *tty, struct winsize_ winsize);\nvoid tty_hangup(struct tty *tty);\n\n// public for the benefit of ptys\nstruct tty *tty_get(struct tty_driver *driver, int type, int num);\nstruct tty *tty_alloc(struct tty_driver *driver, int type, int num);\nint tty_open(struct tty *tty, struct fd *fd);\nextern lock_t ttys_lock;\nvoid tty_release(struct tty *tty); // must be called with ttys_lock\n\nextern struct dev_ops tty_dev;\nextern struct dev_ops ptmx_dev;\n\nint ptmx_open(struct fd *fd);\n// Should call with a driver declared *without* DEFINE_TTY_DRIVER, as it overwrites the ttys field.\nstruct tty *pty_open_fake(struct tty_driver *driver);\n\n#endif\n"
  },
  {
    "path": "iSH.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\tBB4A922324ED9402002F5A96 /* iSH pre */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = BB4A922424ED9402002F5A96 /* Build configuration list for PBXAggregateTarget \"iSH pre\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB4A922A24ED9421002F5A96 /* Add icons to Info.plist */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"iSH pre\";\n\t\t\tproductName = \"iSH pre\";\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t0FC2383C2991FBA6004C09EC /* mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FC2383B2991FB82004C09EC /* mmx.c */; };\n\t\t491B31E62883BF22008EEFB0 /* ThemeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 491B31E52883BF22008EEFB0 /* ThemeViewController.m */; };\n\t\t49302D8E277669D300C9885A /* ish.c in Sources */ = {isa = PBXBuildFile; fileRef = 49302D8D277669D300C9885A /* ish.c */; };\n\t\t496C7AB227CC697E005D7613 /* Theme.m in Sources */ = {isa = PBXBuildFile; fileRef = 491C4C1E27C8FB65008B1DFA /* Theme.m */; };\n\t\t496C7AB327CC734D005D7613 /* ThemesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 491C4C1627C8F84A008B1DFA /* ThemesViewController.m */; };\n\t\t497A08BF29633FF400B323CF /* mach_exc.defs in Sources */ = {isa = PBXBuildFile; fileRef = 49087C26295DF1350058075B /* mach_exc.defs */; settings = {ATTRIBUTES = (Server, ); }; };\n\t\t497A08C029633FFA00B323CF /* hook.c in Sources */ = {isa = PBXBuildFile; fileRef = 49087C14295DF0490058075B /* hook.c */; };\n\t\t497A08C12963401100B323CF /* AccessibilityFixes.m in Sources */ = {isa = PBXBuildFile; fileRef = 497A08BC2961168400B323CF /* AccessibilityFixes.m */; };\n\t\t497F6CF5254E5EA500C82F46 /* float80-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C5A254E5C7E00C82F46 /* float80-test.c */; };\n\t\t497F6CF6254E5EA500C82F46 /* float80.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C66254E5C7F00C82F46 /* float80.c */; };\n\t\t497F6CF7254E5EA500C82F46 /* fpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C5D254E5C7E00C82F46 /* fpu.c */; };\n\t\t497F6CF9254E5EA500C82F46 /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C60254E5C7F00C82F46 /* memory.c */; };\n\t\t497F6CFA254E5EA500C82F46 /* tlb.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C59254E5C7E00C82F46 /* tlb.c */; };\n\t\t497F6CFB254E5EA500C82F46 /* vec.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C64254E5C7F00C82F46 /* vec.c */; };\n\t\t497F6CFC254E5EA500C82F46 /* adhoc.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BFE254E5C0E00C82F46 /* adhoc.c */; };\n\t\t497F6CFD254E5EA500C82F46 /* dev.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BDC254E5C0D00C82F46 /* dev.c */; };\n\t\t497F6CFE254E5EA500C82F46 /* dir.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF9254E5C0E00C82F46 /* dir.c */; };\n\t\t497F6CFF254E5EA500C82F46 /* dyndev.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C03254E5C0E00C82F46 /* dyndev.c */; };\n\t\t497F6D00254E5EA500C82F46 /* fake-migrate.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BEA254E5C0D00C82F46 /* fake-migrate.c */; };\n\t\t497F6D01254E5EA500C82F46 /* fake-rebuild.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BEF254E5C0D00C82F46 /* fake-rebuild.c */; };\n\t\t497F6D02254E5EA500C82F46 /* fake.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE8254E5C0D00C82F46 /* fake.c */; };\n\t\t497F6D03254E5EA500C82F46 /* fd.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE7254E5C0D00C82F46 /* fd.c */; };\n\t\t497F6D04254E5EA500C82F46 /* generic.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE4254E5C0D00C82F46 /* generic.c */; };\n\t\t497F6D05254E5EA600C82F46 /* inode.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE6254E5C0D00C82F46 /* inode.c */; };\n\t\t497F6D06254E5EA600C82F46 /* lock.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE9254E5C0D00C82F46 /* lock.c */; };\n\t\t497F6D07254E5EA600C82F46 /* mem.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BFF254E5C0E00C82F46 /* mem.c */; };\n\t\t497F6D08254E5EA600C82F46 /* mount.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE0254E5C0D00C82F46 /* mount.c */; };\n\t\t497F6D09254E5EA600C82F46 /* path.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BDB254E5C0D00C82F46 /* path.c */; };\n\t\t497F6D0A254E5EA600C82F46 /* pipe.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BFC254E5C0E00C82F46 /* pipe.c */; };\n\t\t497F6D0B254E5EA600C82F46 /* poll.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF6254E5C0E00C82F46 /* poll.c */; };\n\t\t497F6D0C254E5EA600C82F46 /* pid.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF2254E5C0D00C82F46 /* pid.c */; };\n\t\t497F6D0D254E5EA600C82F46 /* root.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF3254E5C0D00C82F46 /* root.c */; };\n\t\t497F6D0E254E5EA600C82F46 /* entry.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF4254E5C0D00C82F46 /* entry.c */; };\n\t\t497F6D0F254E5EA600C82F46 /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BDD254E5C0D00C82F46 /* proc.c */; };\n\t\t497F6D10254E5EA600C82F46 /* pty.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C06254E5C0E00C82F46 /* pty.c */; };\n\t\t497F6D11254E5EA600C82F46 /* real.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C04254E5C0E00C82F46 /* real.c */; };\n\t\t497F6D12254E5EA600C82F46 /* sock.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BF7254E5C0E00C82F46 /* sock.c */; };\n\t\t497F6D13254E5EA600C82F46 /* sockrestart.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BFB254E5C0E00C82F46 /* sockrestart.c */; };\n\t\t497F6D14254E5EA600C82F46 /* stat.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE3254E5C0D00C82F46 /* stat.c */; };\n\t\t497F6D15254E5EA600C82F46 /* tmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BEB254E5C0D00C82F46 /* tmp.c */; };\n\t\t497F6D16254E5EA600C82F46 /* tty-real.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C05254E5C0E00C82F46 /* tty-real.c */; };\n\t\t497F6D17254E5EA600C82F46 /* tty.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6BE2254E5C0D00C82F46 /* tty.c */; };\n\t\t497F6D18254E5EA600C82F46 /* gen.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C3D254E5C4F00C82F46 /* gen.c */; };\n\t\t497F6D19254E5EA600C82F46 /* helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C3B254E5C4F00C82F46 /* helpers.c */; };\n\t\t497F6D1A254E5EA600C82F46 /* asbestos.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C40254E5C4F00C82F46 /* asbestos.c */; };\n\t\t497F6D1B254E5EA600C82F46 /* offsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C3C254E5C4F00C82F46 /* offsets.c */; };\n\t\t497F6D1C254E5EA600C82F46 /* calls.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C9F254E5C9800C82F46 /* calls.c */; };\n\t\t497F6D1D254E5EA600C82F46 /* epoll.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C7D254E5C9700C82F46 /* epoll.c */; };\n\t\t497F6D1E254E5EA600C82F46 /* errno.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C91254E5C9800C82F46 /* errno.c */; };\n\t\t497F6D1F254E5EA600C82F46 /* eventfd.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C95254E5C9800C82F46 /* eventfd.c */; };\n\t\t497F6D20254E5EA600C82F46 /* exec.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C81254E5C9700C82F46 /* exec.c */; };\n\t\t497F6D21254E5EA600C82F46 /* exit.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C90254E5C9700C82F46 /* exit.c */; };\n\t\t497F6D22254E5EA600C82F46 /* fork.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C8D254E5C9700C82F46 /* fork.c */; };\n\t\t497F6D23254E5EA600C82F46 /* fs_info.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C96254E5C9800C82F46 /* fs_info.c */; };\n\t\t497F6D24254E5EA600C82F46 /* fs.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C98254E5C9800C82F46 /* fs.c */; };\n\t\t497F6D25254E5EA600C82F46 /* futex.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C9B254E5C9800C82F46 /* futex.c */; };\n\t\t497F6D26254E5EA600C82F46 /* getset.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C99254E5C9800C82F46 /* getset.c */; };\n\t\t497F6D27254E5EA600C82F46 /* group.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C8F254E5C9700C82F46 /* group.c */; };\n\t\t497F6D28254E5EA600C82F46 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C7F254E5C9700C82F46 /* init.c */; };\n\t\t497F6D29254E5EA600C82F46 /* ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C7A254E5C9700C82F46 /* ipc.c */; };\n\t\t497F6D2A254E5EA600C82F46 /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C83254E5C9700C82F46 /* log.c */; };\n\t\t497F6D2B254E5EA600C82F46 /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C85254E5C9700C82F46 /* misc.c */; };\n\t\t497F6D2C254E5EA600C82F46 /* mmap.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C9D254E5C9800C82F46 /* mmap.c */; };\n\t\t497F6D2D254E5EA600C82F46 /* poll.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C94254E5C9800C82F46 /* poll.c */; };\n\t\t497F6D2E254E5EA600C82F46 /* ptrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C8A254E5C9700C82F46 /* ptrace.c */; };\n\t\t497F6D2F254E5EA600C82F46 /* random.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C7E254E5C9700C82F46 /* random.c */; };\n\t\t497F6D30254E5EA600C82F46 /* resource.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CA0254E5C9800C82F46 /* resource.c */; };\n\t\t497F6D31254E5EA600C82F46 /* signal.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C87254E5C9700C82F46 /* signal.c */; };\n\t\t497F6D32254E5EA600C82F46 /* task.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C78254E5C9700C82F46 /* task.c */; };\n\t\t497F6D33254E5EA600C82F46 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C89254E5C9700C82F46 /* time.c */; };\n\t\t497F6D34254E5EA600C82F46 /* tls.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C8E254E5C9700C82F46 /* tls.c */; };\n\t\t497F6D35254E5EA600C82F46 /* uname.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C93254E5C9800C82F46 /* uname.c */; };\n\t\t497F6D36254E5EA600C82F46 /* user.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C8B254E5C9700C82F46 /* user.c */; };\n\t\t497F6D37254E5EA600C82F46 /* vdso.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6C79254E5C9700C82F46 /* vdso.c */; };\n\t\t497F6D38254E5EA600C82F46 /* darwin.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CC3254E5CB300C82F46 /* darwin.c */; };\n\t\t497F6D39254E5EA600C82F46 /* linux.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CC4254E5CB300C82F46 /* linux.c */; };\n\t\t497F6D3A254E5EA600C82F46 /* fifo.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CCF254E5CC800C82F46 /* fifo.c */; };\n\t\t497F6D3B254E5EA600C82F46 /* sync.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CD2254E5CC800C82F46 /* sync.c */; };\n\t\t497F6D3C254E5EA600C82F46 /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 497F6CCD254E5CC800C82F46 /* timer.c */; };\n\t\t497F6D3D254E5EA600C82F46 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BB7D93822087C2890008DA78 /* main.c */; };\n\t\t497F6D5C254E609700C82F46 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BB7D93822087C2890008DA78 /* main.c */; };\n\t\t497F6D87254E62E100C82F46 /* libish.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB13F7DC200AD81D003D1C4D /* libish.a */; };\n\t\t49FF844B2A06249F00850B7A /* ExceptionExfiltrator.m in Sources */ = {isa = PBXBuildFile; fileRef = 49FF844A2A06249F00850B7A /* ExceptionExfiltrator.m */; };\n\t\tBB0F552E239F8A790032A2A1 /* Icons.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB0F552D239F8A790032A2A1 /* Icons.plist */; };\n\t\tBB10E5C9248DBAAC009C7A74 /* libarchive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB10E5C8248DBAA1009C7A74 /* libarchive.a */; };\n\t\tBB10E5D0248DC21D009C7A74 /* Roots.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB10E5CF248DC21D009C7A74 /* Roots.storyboard */; };\n\t\tBB123ACC26C9EFD900419CDA /* LinuxTTY.c in Sources */ = {isa = PBXBuildFile; fileRef = BB123ACB26C9EFD900419CDA /* LinuxTTY.c */; };\n\t\tBB123ACE26C9F13500419CDA /* IOSCalls.m in Sources */ = {isa = PBXBuildFile; fileRef = BB123ACD26C9F13500419CDA /* IOSCalls.m */; };\n\t\tBB13F7EA200ADCED003D1C4D /* libish.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB13F7DC200AD81D003D1C4D /* libish.a */; };\n\t\tBB21A1702689048200BD19B4 /* libfakefs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB21A16F2689041500BD19B4 /* libfakefs.a */; };\n\t\tBB21A18426890C7E00BD19B4 /* libfakefs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB21A16F2689041500BD19B4 /* libfakefs.a */; };\n\t\tBB21A1862689500000BD19B4 /* libish_emu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB2C5B2590257E00545EAB /* libish_emu.a */; };\n\t\tBB235537235D49B300139E00 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB235536235D49B300139E00 /* CoreLocation.framework */; };\n\t\tBB28C79226896B1F00BDC834 /* AboutAppearanceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A28E4E9219A8B670073D200 /* AboutAppearanceViewController.m */; };\n\t\tBB28C79326896B1F00BDC834 /* AltIconViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB267FA523A48F1500ED7CAF /* AltIconViewController.m */; };\n\t\tBB28C79426896B1F00BDC834 /* AboutNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB1D9D92234A8FE100F364E8 /* AboutNavigationController.m */; };\n\t\tBB28C79526896B1F00BDC834 /* TerminalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B571F96D90D00FFB7A4 /* TerminalViewController.m */; };\n\t\tBB28C79626896B1F00BDC834 /* ProgressReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBAEE33B249BDADC0069EBB5 /* ProgressReportViewController.m */; };\n\t\tBB28C79726896B1F00BDC834 /* TerminalView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB78AB2A1FAD22440013E782 /* TerminalView.m */; };\n\t\tBB28C79826896B1F00BDC834 /* ScrollbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB23F58C231E1D1400585522 /* ScrollbarView.m */; };\n\t\tBB28C79926896B1F00BDC834 /* DelayedUITask.m in Sources */ = {isa = PBXBuildFile; fileRef = BB455E101FB37F6600AFB48B /* DelayedUITask.m */; };\n\t\tBB28C79A26896B1F00BDC834 /* Terminal.m in Sources */ = {isa = PBXBuildFile; fileRef = BB0FC5911F980A6B00803272 /* Terminal.m */; };\n\t\tBB28C79B26896B1F00BDC834 /* FontPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB101B372364CF57000A93BC /* FontPickerViewController.m */; };\n\t\tBB28C79D26896B1F00BDC834 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BBCC9D952365430800424C83 /* SceneDelegate.m */; };\n\t\tBB28C79E26896B1F00BDC834 /* AboutExternalKeyboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB82A7FC21B4C2E8006AA5FD /* AboutExternalKeyboardViewController.m */; };\n\t\tBB28C79F26896B1F00BDC834 /* PassthroughView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB149E7F256DC97C00F57815 /* PassthroughView.m */; };\n\t\tBB28C7A026896B1F00BDC834 /* UserPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 8632A7BE219A59FB00F02325 /* UserPreferences.m */; };\n\t\tBB28C7A126896B1F00BDC834 /* RootsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB10E5D2248DCFEA009C7A74 /* RootsTableViewController.m */; };\n\t\tBB28C7A326896B1F00BDC834 /* BarButton.m in Sources */ = {isa = PBXBuildFile; fileRef = BB60F55121573FCA003A4E52 /* BarButton.m */; };\n\t\tBB28C7A426896B1F00BDC834 /* AppGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = BB9C7B84240A240E00F5D4F0 /* AppGroup.m */; };\n\t\tBB28C7A526896B1F00BDC834 /* UIApplication+OpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = BBFB557B215878C600DFE6DE /* UIApplication+OpenURL.m */; };\n\t\tBB28C7A626896B1F00BDC834 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBFB5578215876CD00DFE6DE /* AboutViewController.m */; };\n\t\tBB28C7A726896B1F00BDC834 /* UIViewController+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = BBFB557021586C4800DFE6DE /* UIViewController+Extras.m */; };\n\t\tBB28C7A826896B1F00BDC834 /* NSObject+SaneKVO.m in Sources */ = {isa = PBXBuildFile; fileRef = BBCED896255BB65A00CA0701 /* NSObject+SaneKVO.m */; };\n\t\tBB28C7AE26896C1D00BDC834 /* libiSHApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBEF191E26806364001225BD /* libiSHApp.a */; };\n\t\tBB28C7B226896C4600BDC834 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B621F96D90D00FFB7A4 /* main.m */; };\n\t\tBB28C7B826896CE600BDC834 /* ArrowBarButton.m in Sources */ = {isa = PBXBuildFile; fileRef = BBFB557F21587B6800DFE6DE /* ArrowBarButton.m */; };\n\t\tBB28C7BA268975AA00BDC834 /* iOSFS.m in Sources */ = {isa = PBXBuildFile; fileRef = 408A2639236440F8008A4E81 /* iOSFS.m */; };\n\t\tBB28C7BB268975AE00BDC834 /* LocationDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = BB235533235D488400139E00 /* LocationDevice.m */; };\n\t\tBB28C7BC268975B000BDC834 /* PasteboardDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 650B337322EA235C00B4C03E /* PasteboardDevice.m */; };\n\t\tBB28C7BF2689799800BDC834 /* Roots.m in Sources */ = {isa = PBXBuildFile; fileRef = BB10E5CD248DBE66009C7A74 /* Roots.m */; };\n\t\tBB2B4DAC231D94C300CB578B /* hterm_all.js in Resources */ = {isa = PBXBuildFile; fileRef = BB2B4DAB231D94C300CB578B /* hterm_all.js */; };\n\t\tBB2B4DAD231D998300CB578B /* term.js in Resources */ = {isa = PBXBuildFile; fileRef = BB4A539C1FAA490C00A72ACE /* term.js */; };\n\t\tBB2B4DAE231D998300CB578B /* term.css in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53AF1FAA787900A72ACE /* term.css */; };\n\t\tBB2B4DAF231D998300CB578B /* term.html in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53A91FAA496700A72ACE /* term.html */; };\n\t\tBB38597827BCEC70000A1082 /* circular.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597627BCEC70000A1082 /* circular.png */; };\n\t\tBB38597D27BCEC78000A1082 /* dollarblock1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597927BCEC78000A1082 /* dollarblock1.png */; };\n\t\tBB38597F27BCEC78000A1082 /* dollarblock2.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597A27BCEC78000A1082 /* dollarblock2.png */; };\n\t\tBB38598127BCEC78000A1082 /* freeiosterminal.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597B27BCEC78000A1082 /* freeiosterminal.png */; };\n\t\tBB38598527BCEC84000A1082 /* ishcolontildehash.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38598227BCEC83000A1082 /* ishcolontildehash.png */; };\n\t\tBB38599327BCEE6B000A1082 /* notsurewhatthisis.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88202589734A00208600 /* notsurewhatthisis.png */; };\n\t\tBB38599427BCEE6B000A1082 /* pydann1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553123A0AB9B0032A2A1 /* pydann1.png */; };\n\t\tBB38599527BCEE6B000A1082 /* pydann2.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553523A0ACFC0032A2A1 /* pydann2.png */; };\n\t\tBB38599627BCEE6B000A1082 /* idollarhash.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8297D24EDAC11009D042C /* idollarhash.png */; };\n\t\tBB38599727BCEE6B000A1082 /* freeiosterminal.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597B27BCEC78000A1082 /* freeiosterminal.png */; };\n\t\tBB38599827BCEE6B000A1082 /* metal.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8298624EE5853009D042C /* metal.png */; };\n\t\tBB38599927BCEE6B000A1082 /* dollarblock1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597927BCEC78000A1082 /* dollarblock1.png */; };\n\t\tBB38599A27BCEE6B000A1082 /* is.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38598327BCEC83000A1082 /* is.png */; };\n\t\tBB38599B27BCEE6B000A1082 /* rgb.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88052589661A00208600 /* rgb.png */; };\n\t\tBB38599C27BCEE6B000A1082 /* sprite64.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B880E2589662200208600 /* sprite64.png */; };\n\t\tBB38599D27BCEE6B000A1082 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = BB1B9A4223A5E96900414052 /* icon.png */; };\n\t\tBB38599E27BCEE6B000A1082 /* circular.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597627BCEC70000A1082 /* circular.png */; };\n\t\tBB38599F27BCEE6B000A1082 /* icon1337.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A922D24EDA461002F5A96 /* icon1337.png */; };\n\t\tBB3859A027BCEE6B000A1082 /* 3d.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A923124EDA560002F5A96 /* 3d.png */; };\n\t\tBB3859A127BCEE6B000A1082 /* iinhash.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8298024EDACBB009D042C /* iinhash.png */; };\n\t\tBB3859A227BCEE6B000A1082 /* dollarblock2.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38597A27BCEC78000A1082 /* dollarblock2.png */; };\n\t\tBB3859A327BCEE6B000A1082 /* ihash1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553323A0AC760032A2A1 /* ihash1.png */; };\n\t\tBB3859A427BCEE6B000A1082 /* ishcolontildehash.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38598227BCEC83000A1082 /* ishcolontildehash.png */; };\n\t\tBB3859A527BCEE6B000A1082 /* reworked.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88172589662A00208600 /* reworked.png */; };\n\t\tBB3859A627BCEE6B000A1082 /* colontildehash.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A922F24EDA55C002F5A96 /* colontildehash.png */; };\n\t\tBB3859A727BCEE6B000A1082 /* uninspired.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F552F239F8B360032A2A1 /* uninspired.png */; };\n\t\tBB3859A827BCEE6C000A1082 /* is.png in Resources */ = {isa = PBXBuildFile; fileRef = BB38598327BCEC83000A1082 /* is.png */; };\n\t\tBB41591F255EF9E300E0950C /* UITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BB41591E255EF9E300E0950C /* UITests.m */; };\n\t\tBB6DB261216435340047A611 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BB6DB260216435330047A611 /* libiconv.tbd */; };\n\t\tBB77636726E486A7008DB44A /* fakefs.c in Sources */ = {isa = PBXBuildFile; fileRef = BB10E5C2248DBA7B009C7A74 /* fakefs.c */; };\n\t\tBB792B5B1F96D90D00FFB7A4 /* Terminal.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB792B591F96D90D00FFB7A4 /* Terminal.storyboard */; };\n\t\tBB792B5D1F96D90D00FFB7A4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB792B5C1F96D90D00FFB7A4 /* Assets.xcassets */; };\n\t\tBB792B601F96D90D00FFB7A4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB792B5E1F96D90D00FFB7A4 /* LaunchScreen.storyboard */; };\n\t\tBB88F4942154760800A341FD /* FileProviderExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = BB88F4932154760800A341FD /* FileProviderExtension.m */; };\n\t\tBB88F4972154760800A341FD /* FileProviderItem.m in Sources */ = {isa = PBXBuildFile; fileRef = BB88F4962154760800A341FD /* FileProviderItem.m */; };\n\t\tBB88F49A2154760800A341FD /* FileProviderEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = BB88F4992154760800A341FD /* FileProviderEnumerator.m */; };\n\t\tBB88F49F2154760800A341FD /* iSHFileProvider.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = BB88F4902154760800A341FD /* iSHFileProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tBB8C3AFF26B7B8CF00E38DDC /* fakefs.c in Sources */ = {isa = PBXBuildFile; fileRef = BB8C3AFD26B7B8AF00E38DDC /* fakefs.c */; };\n\t\tBB9C7B87240A2B1E00F5D4F0 /* AppGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = BB9C7B84240A240E00F5D4F0 /* AppGroup.m */; };\n\t\tBBA8E2C1236FF5EA00515F76 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBA8E2C0236FF5EA00515F76 /* SystemConfiguration.framework */; };\n\t\tBBAEE339249B58E80069EBB5 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBAEE338249B58E80069EBB5 /* libbz2.tbd */; };\n\t\tBBBCE7E321D2F02200CA00B3 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BBBCE7E521D2F02200CA00B3 /* About.storyboard */; };\n\t\tBBBD741426B7BE2E00321AC5 /* libfakefs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB21A16F2689041500BD19B4 /* libfakefs.a */; };\n\t\tBBBDDF922CE0122F0071F1F3 /* emu_asbestos.c in Sources */ = {isa = PBXBuildFile; fileRef = BBBDDF912CE0122F0071F1F3 /* emu_asbestos.c */; };\n\t\tBBBDDF932CE0132F0071F1F3 /* libiSHLinuxUser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBBDDF8F2CE00F6B0071F1F3 /* libiSHLinuxUser.a */; };\n\t\tBBBF7B5C2415CDBB00EC4C14 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BBBF7B5B2415CDBB00EC4C14 /* Settings.bundle */; };\n\t\tBBBF9CBD27C217B6002A30F7 /* PasteboardDeviceLinux.c in Sources */ = {isa = PBXBuildFile; fileRef = BBBF9CBB27C217A6002A30F7 /* PasteboardDeviceLinux.c */; };\n\t\tBBBF9CBF27C44114002A30F7 /* iSHFileProvider.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = BB88F4902154760800A341FD /* iSHFileProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tBBBFE94921C5CFF100509DD5 /* NSError+ISHErrno.m in Sources */ = {isa = PBXBuildFile; fileRef = BB13F4DD21C5770000343E17 /* NSError+ISHErrno.m */; };\n\t\tBBC3863E276817A900CC8C2E /* UpgradeRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBA5D87D27536E7600B39D77 /* UpgradeRootViewController.m */; };\n\t\tBBC3863F276817AD00CC8C2E /* CurrentRoot.m in Sources */ = {isa = PBXBuildFile; fileRef = BB565EA32734F1FB00C93EAE /* CurrentRoot.m */; };\n\t\tBBD23D5F258DA450003DCB40 /* Screenshots.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD23D5E258DA450003DCB40 /* Screenshots.m */; };\n\t\tBBD23D77258DA4BC003DCB40 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD23D76258DA4BC003DCB40 /* SnapshotHelper.swift */; };\n\t\tBBE64AC22A198FCC00589372 /* ExceptionExfiltrator.m in Sources */ = {isa = PBXBuildFile; fileRef = 49FF844A2A06249F00850B7A /* ExceptionExfiltrator.m */; };\n\t\tBBECF3AD269132F400DEC937 /* LinuxInterop.c in Sources */ = {isa = PBXBuildFile; fileRef = BBECF39D2691313B00DEC937 /* LinuxInterop.c */; };\n\t\tBBECF3AE2691330F00DEC937 /* libiSHLinux.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBECF3A22691314C00DEC937 /* libiSHLinux.a */; };\n\t\tBBECF3B9269136FB00DEC937 /* liblinux.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBECF3B8269136E100DEC937 /* liblinux.a */; };\n\t\tBBECF3BC26913F1600DEC937 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B541F96D90D00FFB7A4 /* AppDelegate.m */; };\n\t\tBBEEA9E8277D25090069495B /* LinuxRoot.c in Sources */ = {isa = PBXBuildFile; fileRef = BBEEA9E7277D25090069495B /* LinuxRoot.c */; };\n\t\tBBEEA9EA277DAB400069495B /* LinuxPTY.c in Sources */ = {isa = PBXBuildFile; fileRef = BBEEA9E9277DAB400069495B /* LinuxPTY.c */; };\n\t\tBBEF1970268066D1001225BD /* libiSHApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBEF191E26806364001225BD /* libiSHApp.a */; };\n\t\tBBEF1972268066D1001225BD /* libish_emu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB2C5B2590257E00545EAB /* libish_emu.a */; };\n\t\tBBEF1973268066D1001225BD /* libarchive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB10E5C8248DBAA1009C7A74 /* libarchive.a */; };\n\t\tBBEF1974268066D1001225BD /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB235536235D49B300139E00 /* CoreLocation.framework */; };\n\t\tBBEF1975268066D1001225BD /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBA8E2C0236FF5EA00515F76 /* SystemConfiguration.framework */; };\n\t\tBBEF1976268066D1001225BD /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBAEE338249B58E80069EBB5 /* libbz2.tbd */; };\n\t\tBBEF1977268066D1001225BD /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BB6DB260216435330047A611 /* libiconv.tbd */; };\n\t\tBBEF1978268066D1001225BD /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB55652158644C00DFE6DE /* libresolv.tbd */; };\n\t\tBBEF197D268066D1001225BD /* 3d.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A923124EDA560002F5A96 /* 3d.png */; };\n\t\tBBEF197E268066D1001225BD /* colontildehash.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A922F24EDA55C002F5A96 /* colontildehash.png */; };\n\t\tBBEF197F268066D1001225BD /* metal.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8298624EE5853009D042C /* metal.png */; };\n\t\tBBEF1980268066D1001225BD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB792B5C1F96D90D00FFB7A4 /* Assets.xcassets */; };\n\t\tBBEF1981268066D1001225BD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB792B5E1F96D90D00FFB7A4 /* LaunchScreen.storyboard */; };\n\t\tBBEF1982268066D1001225BD /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BBBF7B5B2415CDBB00EC4C14 /* Settings.bundle */; };\n\t\tBBEF1983268066D1001225BD /* pydann1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553123A0AB9B0032A2A1 /* pydann1.png */; };\n\t\tBBEF1984268066D1001225BD /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BBBCE7E521D2F02200CA00B3 /* About.storyboard */; };\n\t\tBBEF1985268066D1001225BD /* Terminal.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB792B591F96D90D00FFB7A4 /* Terminal.storyboard */; };\n\t\tBBEF1986268066D1001225BD /* Icons.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB0F552D239F8A790032A2A1 /* Icons.plist */; };\n\t\tBBEF1987268066D1001225BD /* uninspired.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F552F239F8B360032A2A1 /* uninspired.png */; };\n\t\tBBEF1988268066D1001225BD /* iinhash.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8298024EDACBB009D042C /* iinhash.png */; };\n\t\tBBEF1989268066D1001225BD /* pydann2.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553523A0ACFC0032A2A1 /* pydann2.png */; };\n\t\tBBEF198A268066D1001225BD /* ihash1.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0F553323A0AC760032A2A1 /* ihash1.png */; };\n\t\tBBEF198B268066D1001225BD /* term.html in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53A91FAA496700A72ACE /* term.html */; };\n\t\tBBEF198C268066D1001225BD /* notsurewhatthisis.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88202589734A00208600 /* notsurewhatthisis.png */; };\n\t\tBBEF198D268066D1001225BD /* reworked.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88172589662A00208600 /* reworked.png */; };\n\t\tBBEF198E268066D1001225BD /* Roots.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB10E5CF248DC21D009C7A74 /* Roots.storyboard */; };\n\t\tBBEF198F268066D1001225BD /* idollarhash.png in Resources */ = {isa = PBXBuildFile; fileRef = BBC8297D24EDAC11009D042C /* idollarhash.png */; };\n\t\tBBEF1990268066D1001225BD /* hterm_all.js in Resources */ = {isa = PBXBuildFile; fileRef = BB2B4DAB231D94C300CB578B /* hterm_all.js */; };\n\t\tBBEF1991268066D1001225BD /* icon1337.png in Resources */ = {isa = PBXBuildFile; fileRef = BB4A922D24EDA461002F5A96 /* icon1337.png */; };\n\t\tBBEF1992268066D1001225BD /* rgb.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B88052589661A00208600 /* rgb.png */; };\n\t\tBBEF1993268066D1001225BD /* term.css in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53AF1FAA787900A72ACE /* term.css */; };\n\t\tBBEF1994268066D1001225BD /* term.js in Resources */ = {isa = PBXBuildFile; fileRef = BB4A539C1FAA490C00A72ACE /* term.js */; };\n\t\tBBEF1995268066D1001225BD /* sprite64.png in Resources */ = {isa = PBXBuildFile; fileRef = BB0B880E2589662200208600 /* sprite64.png */; };\n\t\tBBEF1996268066D1001225BD /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = BB1B9A4223A5E96900414052 /* icon.png */; };\n\t\tBBF06F6C2CC4C134009F5DB5 /* fchdir.c in Sources */ = {isa = PBXBuildFile; fileRef = BB0DF6F22CC4B01000EFECAE /* fchdir.c */; };\n\t\tBBFB2C7E259026C200545EAB /* libish_emu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB2C5B2590257E00545EAB /* libish_emu.a */; };\n\t\tBBFB2CEC2590296B00545EAB /* libish_emu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB2C5B2590257E00545EAB /* libish_emu.a */; };\n\t\tBBFB55662158644C00DFE6DE /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB55652158644C00DFE6DE /* libresolv.tbd */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXBuildRule section */\n\t\t497A08BE29633FC100B323CF /* PBXBuildRule */ = {\n\t\t\tisa = PBXBuildRule;\n\t\t\tcompilerSpec = com.apple.compilers.proxy.script;\n\t\t\tfileType = sourcecode.mig;\n\t\t\tinputFiles = (\n\t\t\t);\n\t\t\tisEditable = 1;\n\t\t\toutputFiles = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/mach_excServer.h\",\n\t\t\t\t\"$(DERIVED_FILE_DIR)/mach_excServer.c\",\n\t\t\t);\n\t\t\tscript = \"set -x\\n\\nunset IPHONEOS_DEPLOYMENT_TARGET\\n\\nsdk=\\\"$(dirname \\\"$SCRIPT_INPUT_FILE\\\")/../../../\\\"\\ncd \\\"$DERIVED_FILE_DIR\\\"\\nmig -sheader \\\"$DERIVED_FILE_DIR/mach_excServer.h\\\" -server \\\"$DERIVED_FILE_DIR/mach_excServer.c\\\" -Wno-incompatible-sysroot -isysroot \\\"$sdk\\\" \\\"$SCRIPT_INPUT_FILE\\\"\\n\";\n\t\t};\n/* End PBXBuildRule section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t497F6D5A254E609000C82F46 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7DB200AD81D003D1C4D;\n\t\t\tremoteInfo = libish;\n\t\t};\n\t\tBB10E5C7248DBAA1009C7A74 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = BB10E0D6248DA67B009C7A74;\n\t\t\tremoteInfo = libarchive;\n\t\t};\n\t\tBB10E5CA248DBAB7009C7A74 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB10E0D5248DA67B009C7A74;\n\t\t\tremoteInfo = libarchive;\n\t\t};\n\t\tBB13F7D4200ACCA8003D1C4D /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7CA200ACC31003D1C4D;\n\t\t\tremoteInfo = Meson;\n\t\t};\n\t\tBB13F7E6200AD874003D1C4D /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7DB200AD81D003D1C4D;\n\t\t\tremoteInfo = libish;\n\t\t};\n\t\tBB13F7E8200AD967003D1C4D /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7D0200ACCA2003D1C4D;\n\t\t\tremoteInfo = Ninja;\n\t\t};\n\t\tBB21A1682689041500BD19B4 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7D0200ACCA2003D1C4D;\n\t\t\tremoteInfo = Ninja;\n\t\t};\n\t\tBB415921255EF9E300E0950C /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB792B4F1F96D90D00FFB7A4;\n\t\t\tremoteInfo = iSH;\n\t\t};\n\t\tBB4A922724ED940C002F5A96 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB4A922324ED9402002F5A96;\n\t\t\tremoteInfo = \"iSH pre\";\n\t\t};\n\t\tBB88F49D2154760800A341FD /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB88F48F2154760800A341FD;\n\t\t\tremoteInfo = iSHFiles;\n\t\t};\n\t\tBBBDDF822CE00F6A0071F1F3 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BBECF3AF269136E100DEC937;\n\t\t\tremoteInfo = liblinux;\n\t\t};\n\t\tBBECF3B1269136E100DEC937 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7D0200ACCA2003D1C4D;\n\t\t\tremoteInfo = Ninja;\n\t\t};\n\t\tBBECF3BD2691417C00DEC937 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BBECF3AF269136E100DEC937;\n\t\t\tremoteInfo = liblinux;\n\t\t};\n\t\tBBEF1928268063FD001225BD /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BBEF191D26806364001225BD;\n\t\t\tremoteInfo = libiSH;\n\t\t};\n\t\tBBEF1966268066D1001225BD /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB4A922324ED9402002F5A96;\n\t\t\tremoteInfo = \"iSH pre\";\n\t\t};\n\t\tBBEF1968268066D1001225BD /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BBEF191D26806364001225BD;\n\t\t\tremoteInfo = libiSH;\n\t\t};\n\t\tBBEF196A268066D1001225BD /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB10E0D5248DA67B009C7A74;\n\t\t\tremoteInfo = libarchive;\n\t\t};\n\t\tBBFB2C562590257E00545EAB /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BB792B461F96D8E000FFB7A4 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BB13F7D0200ACCA2003D1C4D;\n\t\t\tremoteInfo = Ninja;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t497F6D45254E605F00C82F46 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = /usr/share/man/man1/;\n\t\t\tdstSubfolderSpec = 0;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 1;\n\t\t};\n\t\tBB88F4A32154760800A341FD /* Embed Foundation Extensions */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 13;\n\t\t\tfiles = (\n\t\t\t\tBB88F49F2154760800A341FD /* iSHFileProvider.appex in Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tname = \"Embed Foundation Extensions\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBEF1997268066D1001225BD /* Embed Foundation Extensions */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 13;\n\t\t\tfiles = (\n\t\t\t\tBBBF9CBF27C44114002A30F7 /* iSHFileProvider.appex in Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tname = \"Embed Foundation Extensions\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0FC2383B2991FB82004C09EC /* mmx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mmx.c; sourceTree = \"<group>\"; };\n\t\t408A2639236440F8008A4E81 /* iOSFS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSFS.m; sourceTree = \"<group>\"; };\n\t\t408A263B23644102008A4E81 /* iOSFS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iOSFS.h; sourceTree = \"<group>\"; };\n\t\t49087C13295DF0490058075B /* hook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hook.h; sourceTree = \"<group>\"; };\n\t\t49087C14295DF0490058075B /* hook.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hook.c; sourceTree = \"<group>\"; };\n\t\t49087C26295DF1350058075B /* mach_exc.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = mach_exc.defs; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/mach_exc.defs; sourceTree = DEVELOPER_DIR; };\n\t\t491B31E42883BF22008EEFB0 /* ThemeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThemeViewController.h; sourceTree = \"<group>\"; };\n\t\t491B31E52883BF22008EEFB0 /* ThemeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThemeViewController.m; sourceTree = \"<group>\"; };\n\t\t491C4C1527C8F84A008B1DFA /* ThemesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThemesViewController.h; sourceTree = \"<group>\"; };\n\t\t491C4C1627C8F84A008B1DFA /* ThemesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThemesViewController.m; sourceTree = \"<group>\"; };\n\t\t491C4C1D27C8FB65008B1DFA /* Theme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Theme.h; sourceTree = \"<group>\"; };\n\t\t491C4C1E27C8FB65008B1DFA /* Theme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Theme.m; sourceTree = \"<group>\"; };\n\t\t49302D8D277669D300C9885A /* ish.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ish.c; sourceTree = \"<group>\"; };\n\t\t49302D972777FAB400C9885A /* ish.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ish.h; sourceTree = \"<group>\"; };\n\t\t493B378F290F5320000C41ED /* CLI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CLI.xcconfig; sourceTree = \"<group>\"; };\n\t\t494D03F72727DAC6004F0CCE /* xcode-ninja.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = \"xcode-ninja.sh\"; path = \"app/xcode-ninja.sh\"; sourceTree = \"<group>\"; };\n\t\t497A08BC2961168400B323CF /* AccessibilityFixes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AccessibilityFixes.m; sourceTree = \"<group>\"; };\n\t\t497F6BDA254E5C0D00C82F46 /* mem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem.h; sourceTree = \"<group>\"; };\n\t\t497F6BDB254E5C0D00C82F46 /* path.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = path.c; sourceTree = \"<group>\"; };\n\t\t497F6BDC254E5C0D00C82F46 /* dev.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dev.c; sourceTree = \"<group>\"; };\n\t\t497F6BDD254E5C0D00C82F46 /* proc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proc.c; sourceTree = \"<group>\"; };\n\t\t497F6BDE254E5C0D00C82F46 /* dev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dev.h; sourceTree = \"<group>\"; };\n\t\t497F6BDF254E5C0D00C82F46 /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = path.h; sourceTree = \"<group>\"; };\n\t\t497F6BE0254E5C0D00C82F46 /* mount.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mount.c; sourceTree = \"<group>\"; };\n\t\t497F6BE1254E5C0D00C82F46 /* sock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sock.h; sourceTree = \"<group>\"; };\n\t\t497F6BE2254E5C0D00C82F46 /* tty.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tty.c; sourceTree = \"<group>\"; };\n\t\t497F6BE3254E5C0D00C82F46 /* stat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stat.c; sourceTree = \"<group>\"; };\n\t\t497F6BE4254E5C0D00C82F46 /* generic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = generic.c; sourceTree = \"<group>\"; };\n\t\t497F6BE5254E5C0D00C82F46 /* tty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tty.h; sourceTree = \"<group>\"; };\n\t\t497F6BE6254E5C0D00C82F46 /* inode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inode.c; sourceTree = \"<group>\"; };\n\t\t497F6BE7254E5C0D00C82F46 /* fd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fd.c; sourceTree = \"<group>\"; };\n\t\t497F6BE8254E5C0D00C82F46 /* fake.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fake.c; sourceTree = \"<group>\"; };\n\t\t497F6BE9254E5C0D00C82F46 /* lock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lock.c; sourceTree = \"<group>\"; };\n\t\t497F6BEA254E5C0D00C82F46 /* fake-migrate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = \"fake-migrate.c\"; sourceTree = \"<group>\"; };\n\t\t497F6BEB254E5C0D00C82F46 /* tmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tmp.c; sourceTree = \"<group>\"; };\n\t\t497F6BEC254E5C0D00C82F46 /* fake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fake.h; sourceTree = \"<group>\"; };\n\t\t497F6BED254E5C0D00C82F46 /* sqlutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlutil.h; sourceTree = \"<group>\"; };\n\t\t497F6BEE254E5C0D00C82F46 /* poll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = poll.h; sourceTree = \"<group>\"; };\n\t\t497F6BEF254E5C0D00C82F46 /* fake-rebuild.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = \"fake-rebuild.c\"; sourceTree = \"<group>\"; };\n\t\t497F6BF0254E5C0D00C82F46 /* fd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fd.h; sourceTree = \"<group>\"; };\n\t\t497F6BF2254E5C0D00C82F46 /* pid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pid.c; sourceTree = \"<group>\"; };\n\t\t497F6BF3254E5C0D00C82F46 /* root.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = root.c; sourceTree = \"<group>\"; };\n\t\t497F6BF4254E5C0D00C82F46 /* entry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = entry.c; sourceTree = \"<group>\"; };\n\t\t497F6BF5254E5C0E00C82F46 /* proc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = proc.h; sourceTree = \"<group>\"; };\n\t\t497F6BF6254E5C0E00C82F46 /* poll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poll.c; sourceTree = \"<group>\"; };\n\t\t497F6BF7254E5C0E00C82F46 /* sock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sock.c; sourceTree = \"<group>\"; };\n\t\t497F6BF8254E5C0E00C82F46 /* real.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = real.h; sourceTree = \"<group>\"; };\n\t\t497F6BF9254E5C0E00C82F46 /* dir.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dir.c; sourceTree = \"<group>\"; };\n\t\t497F6BFA254E5C0E00C82F46 /* sockrestart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sockrestart.h; sourceTree = \"<group>\"; };\n\t\t497F6BFB254E5C0E00C82F46 /* sockrestart.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sockrestart.c; sourceTree = \"<group>\"; };\n\t\t497F6BFC254E5C0E00C82F46 /* pipe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pipe.c; sourceTree = \"<group>\"; };\n\t\t497F6BFD254E5C0E00C82F46 /* inode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inode.h; sourceTree = \"<group>\"; };\n\t\t497F6BFE254E5C0E00C82F46 /* adhoc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adhoc.c; sourceTree = \"<group>\"; };\n\t\t497F6BFF254E5C0E00C82F46 /* mem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mem.c; sourceTree = \"<group>\"; };\n\t\t497F6C00254E5C0E00C82F46 /* dyndev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dyndev.h; sourceTree = \"<group>\"; };\n\t\t497F6C01254E5C0E00C82F46 /* devices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = devices.h; sourceTree = \"<group>\"; };\n\t\t497F6C02254E5C0E00C82F46 /* stat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stat.h; sourceTree = \"<group>\"; };\n\t\t497F6C03254E5C0E00C82F46 /* dyndev.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dyndev.c; sourceTree = \"<group>\"; };\n\t\t497F6C04254E5C0E00C82F46 /* real.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = real.c; sourceTree = \"<group>\"; };\n\t\t497F6C05254E5C0E00C82F46 /* tty-real.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = \"tty-real.c\"; sourceTree = \"<group>\"; };\n\t\t497F6C06254E5C0E00C82F46 /* pty.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pty.c; sourceTree = \"<group>\"; };\n\t\t497F6C28254E5C4F00C82F46 /* control.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = control.S; sourceTree = \"<group>\"; };\n\t\t497F6C29254E5C4F00C82F46 /* misc.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = misc.S; sourceTree = \"<group>\"; };\n\t\t497F6C2A254E5C4F00C82F46 /* entry.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = entry.S; sourceTree = \"<group>\"; };\n\t\t497F6C2B254E5C4F00C82F46 /* math.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = math.S; sourceTree = \"<group>\"; };\n\t\t497F6C2C254E5C4F00C82F46 /* memory.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = memory.S; sourceTree = \"<group>\"; };\n\t\t497F6C2D254E5C4F00C82F46 /* gadgets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gadgets.h; sourceTree = \"<group>\"; };\n\t\t497F6C2E254E5C4F00C82F46 /* string.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = string.S; sourceTree = \"<group>\"; };\n\t\t497F6C2F254E5C4F00C82F46 /* bits.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = bits.S; sourceTree = \"<group>\"; };\n\t\t497F6C30254E5C4F00C82F46 /* gadgets-generic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"gadgets-generic.h\"; sourceTree = \"<group>\"; };\n\t\t497F6C32254E5C4F00C82F46 /* control.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = control.S; sourceTree = \"<group>\"; };\n\t\t497F6C33254E5C4F00C82F46 /* misc.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = misc.S; sourceTree = \"<group>\"; };\n\t\t497F6C34254E5C4F00C82F46 /* entry.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = entry.S; sourceTree = \"<group>\"; };\n\t\t497F6C35254E5C4F00C82F46 /* math.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = math.S; sourceTree = \"<group>\"; };\n\t\t497F6C36254E5C4F00C82F46 /* memory.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = memory.S; sourceTree = \"<group>\"; };\n\t\t497F6C37254E5C4F00C82F46 /* math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math.h; sourceTree = \"<group>\"; };\n\t\t497F6C38254E5C4F00C82F46 /* gadgets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gadgets.h; sourceTree = \"<group>\"; };\n\t\t497F6C39254E5C4F00C82F46 /* string.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = string.S; sourceTree = \"<group>\"; };\n\t\t497F6C3A254E5C4F00C82F46 /* bits.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = bits.S; sourceTree = \"<group>\"; };\n\t\t497F6C3B254E5C4F00C82F46 /* helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = helpers.c; sourceTree = \"<group>\"; };\n\t\t497F6C3C254E5C4F00C82F46 /* offsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = offsets.c; sourceTree = \"<group>\"; };\n\t\t497F6C3D254E5C4F00C82F46 /* gen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gen.c; sourceTree = \"<group>\"; };\n\t\t497F6C3E254E5C4F00C82F46 /* gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gen.h; sourceTree = \"<group>\"; };\n\t\t497F6C3F254E5C4F00C82F46 /* frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = frame.h; sourceTree = \"<group>\"; };\n\t\t497F6C40254E5C4F00C82F46 /* asbestos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = asbestos.c; sourceTree = \"<group>\"; };\n\t\t497F6C41254E5C4F00C82F46 /* asbestos.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asbestos.h; sourceTree = \"<group>\"; };\n\t\t497F6C58254E5C7E00C82F46 /* cpuid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpuid.h; sourceTree = \"<group>\"; };\n\t\t497F6C59254E5C7E00C82F46 /* tlb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tlb.c; sourceTree = \"<group>\"; };\n\t\t497F6C5A254E5C7E00C82F46 /* float80-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = \"float80-test.c\"; sourceTree = \"<group>\"; };\n\t\t497F6C5B254E5C7E00C82F46 /* float80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = float80.h; sourceTree = \"<group>\"; };\n\t\t497F6C5C254E5C7E00C82F46 /* interrupt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interrupt.h; sourceTree = \"<group>\"; };\n\t\t497F6C5D254E5C7E00C82F46 /* fpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fpu.c; sourceTree = \"<group>\"; };\n\t\t497F6C5E254E5C7E00C82F46 /* decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decode.h; sourceTree = \"<group>\"; };\n\t\t497F6C5F254E5C7F00C82F46 /* vec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec.h; sourceTree = \"<group>\"; };\n\t\t497F6C60254E5C7F00C82F46 /* memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memory.c; sourceTree = \"<group>\"; };\n\t\t497F6C61254E5C7F00C82F46 /* memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memory.h; sourceTree = \"<group>\"; };\n\t\t497F6C62254E5C7F00C82F46 /* cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu.h; sourceTree = \"<group>\"; };\n\t\t497F6C63254E5C7F00C82F46 /* fpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fpu.h; sourceTree = \"<group>\"; };\n\t\t497F6C64254E5C7F00C82F46 /* vec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vec.c; sourceTree = \"<group>\"; };\n\t\t497F6C65254E5C7F00C82F46 /* regid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = regid.h; sourceTree = \"<group>\"; };\n\t\t497F6C66254E5C7F00C82F46 /* float80.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = float80.c; sourceTree = \"<group>\"; };\n\t\t497F6C6A254E5C7F00C82F46 /* modrm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modrm.h; sourceTree = \"<group>\"; };\n\t\t497F6C6B254E5C7F00C82F46 /* tlb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tlb.h; sourceTree = \"<group>\"; };\n\t\t497F6C77254E5C9700C82F46 /* mm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mm.h; sourceTree = \"<group>\"; };\n\t\t497F6C78254E5C9700C82F46 /* task.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = task.c; sourceTree = \"<group>\"; };\n\t\t497F6C79254E5C9700C82F46 /* vdso.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vdso.c; sourceTree = \"<group>\"; };\n\t\t497F6C7A254E5C9700C82F46 /* ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ipc.c; sourceTree = \"<group>\"; };\n\t\t497F6C7B254E5C9700C82F46 /* calls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = calls.h; sourceTree = \"<group>\"; };\n\t\t497F6C7C254E5C9700C82F46 /* signal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = signal.h; sourceTree = \"<group>\"; };\n\t\t497F6C7D254E5C9700C82F46 /* epoll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = epoll.c; sourceTree = \"<group>\"; };\n\t\t497F6C7E254E5C9700C82F46 /* random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = random.c; sourceTree = \"<group>\"; };\n\t\t497F6C7F254E5C9700C82F46 /* init.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = init.c; sourceTree = \"<group>\"; };\n\t\t497F6C80254E5C9700C82F46 /* errno.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = errno.h; sourceTree = \"<group>\"; };\n\t\t497F6C81254E5C9700C82F46 /* exec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exec.c; sourceTree = \"<group>\"; };\n\t\t497F6C82254E5C9700C82F46 /* elf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = elf.h; sourceTree = \"<group>\"; };\n\t\t497F6C83254E5C9700C82F46 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = \"<group>\"; };\n\t\t497F6C84254E5C9700C82F46 /* vdso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vdso.h; sourceTree = \"<group>\"; };\n\t\t497F6C85254E5C9700C82F46 /* misc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = \"<group>\"; };\n\t\t497F6C86254E5C9700C82F46 /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = \"<group>\"; };\n\t\t497F6C87254E5C9700C82F46 /* signal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = signal.c; sourceTree = \"<group>\"; };\n\t\t497F6C88254E5C9700C82F46 /* fs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fs.h; sourceTree = \"<group>\"; };\n\t\t497F6C89254E5C9700C82F46 /* time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = \"<group>\"; };\n\t\t497F6C8A254E5C9700C82F46 /* ptrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ptrace.c; sourceTree = \"<group>\"; };\n\t\t497F6C8B254E5C9700C82F46 /* user.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = user.c; sourceTree = \"<group>\"; };\n\t\t497F6C8C254E5C9700C82F46 /* ptrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ptrace.h; sourceTree = \"<group>\"; };\n\t\t497F6C8D254E5C9700C82F46 /* fork.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fork.c; sourceTree = \"<group>\"; };\n\t\t497F6C8E254E5C9700C82F46 /* tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tls.c; sourceTree = \"<group>\"; };\n\t\t497F6C8F254E5C9700C82F46 /* group.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = group.c; sourceTree = \"<group>\"; };\n\t\t497F6C90254E5C9700C82F46 /* exit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exit.c; sourceTree = \"<group>\"; };\n\t\t497F6C91254E5C9800C82F46 /* errno.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = errno.c; sourceTree = \"<group>\"; };\n\t\t497F6C92254E5C9800C82F46 /* personality.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = personality.h; sourceTree = \"<group>\"; };\n\t\t497F6C93254E5C9800C82F46 /* uname.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uname.c; sourceTree = \"<group>\"; };\n\t\t497F6C94254E5C9800C82F46 /* poll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poll.c; sourceTree = \"<group>\"; };\n\t\t497F6C95254E5C9800C82F46 /* eventfd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eventfd.c; sourceTree = \"<group>\"; };\n\t\t497F6C96254E5C9800C82F46 /* fs_info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fs_info.c; sourceTree = \"<group>\"; };\n\t\t497F6C97254E5C9800C82F46 /* init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = init.h; sourceTree = \"<group>\"; };\n\t\t497F6C98254E5C9800C82F46 /* fs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fs.c; sourceTree = \"<group>\"; };\n\t\t497F6C99254E5C9800C82F46 /* getset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = getset.c; sourceTree = \"<group>\"; };\n\t\t497F6C9A254E5C9800C82F46 /* futex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = futex.h; sourceTree = \"<group>\"; };\n\t\t497F6C9B254E5C9800C82F46 /* futex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = futex.c; sourceTree = \"<group>\"; };\n\t\t497F6C9C254E5C9800C82F46 /* task.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task.h; sourceTree = \"<group>\"; };\n\t\t497F6C9D254E5C9800C82F46 /* mmap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mmap.c; sourceTree = \"<group>\"; };\n\t\t497F6C9E254E5C9800C82F46 /* random.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = random.h; sourceTree = \"<group>\"; };\n\t\t497F6C9F254E5C9800C82F46 /* calls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = calls.c; sourceTree = \"<group>\"; };\n\t\t497F6CA0254E5C9800C82F46 /* resource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resource.c; sourceTree = \"<group>\"; };\n\t\t497F6CA1254E5C9800C82F46 /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = \"<group>\"; };\n\t\t497F6CC2254E5CB300C82F46 /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform.h; sourceTree = \"<group>\"; };\n\t\t497F6CC3254E5CB300C82F46 /* darwin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = darwin.c; sourceTree = \"<group>\"; };\n\t\t497F6CC4254E5CB300C82F46 /* linux.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = linux.c; sourceTree = \"<group>\"; };\n\t\t497F6CCB254E5CC800C82F46 /* fifo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fifo.h; sourceTree = \"<group>\"; };\n\t\t497F6CCC254E5CC800C82F46 /* sync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sync.h; sourceTree = \"<group>\"; };\n\t\t497F6CCD254E5CC800C82F46 /* timer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = timer.c; sourceTree = \"<group>\"; };\n\t\t497F6CCE254E5CC800C82F46 /* refcount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = refcount.h; sourceTree = \"<group>\"; };\n\t\t497F6CCF254E5CC800C82F46 /* fifo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fifo.c; sourceTree = \"<group>\"; };\n\t\t497F6CD0254E5CC800C82F46 /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = \"<group>\"; };\n\t\t497F6CD1254E5CC800C82F46 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = \"<group>\"; };\n\t\t497F6CD2254E5CC800C82F46 /* sync.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sync.c; sourceTree = \"<group>\"; };\n\t\t497F6CD3254E5CC800C82F46 /* bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bits.h; sourceTree = \"<group>\"; };\n\t\t497F6CE4254E5E4C00C82F46 /* MakeXcodeAutoCompleteWork */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = MakeXcodeAutoCompleteWork; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t497F6D47254E605F00C82F46 /* ish */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = ish; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t49FF844A2A06249F00850B7A /* ExceptionExfiltrator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExceptionExfiltrator.m; sourceTree = \"<group>\"; };\n\t\t49FF844C2A0624AF00850B7A /* ExceptionExfiltrator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExceptionExfiltrator.h; sourceTree = \"<group>\"; };\n\t\t650B337222EA235C00B4C03E /* PasteboardDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasteboardDevice.h; sourceTree = \"<group>\"; };\n\t\t650B337322EA235C00B4C03E /* PasteboardDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PasteboardDevice.m; sourceTree = \"<group>\"; };\n\t\t8632A7BD219A59FB00F02325 /* UserPreferences.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserPreferences.h; sourceTree = \"<group>\"; };\n\t\t8632A7BE219A59FB00F02325 /* UserPreferences.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserPreferences.m; sourceTree = \"<group>\"; };\n\t\t9A28E4E8219A8B670073D200 /* AboutAppearanceViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutAppearanceViewController.h; sourceTree = \"<group>\"; };\n\t\t9A28E4E9219A8B670073D200 /* AboutAppearanceViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutAppearanceViewController.m; sourceTree = \"<group>\"; };\n\t\tBB0B848E2586A4CF00208600 /* XcodeDefault.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = XcodeDefault.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B859B2586F13200208600 /* XcodeDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = XcodeDebug.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B859C2586F19000208600 /* XcodeRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = XcodeRelease.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B85A42586F1CB00208600 /* ProjectDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ProjectDebug.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B85A52586F1E100208600 /* ProjectRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ProjectRelease.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B85AD2586F3EB00208600 /* Project.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Project.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B86CD2586FD8800208600 /* App.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = App.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B86D52587009D00208600 /* iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iOS.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB0B88052589661A00208600 /* rgb.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rgb.png; sourceTree = \"<group>\"; };\n\t\tBB0B880E2589662200208600 /* sprite64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sprite64.png; sourceTree = \"<group>\"; };\n\t\tBB0B88172589662A00208600 /* reworked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = reworked.png; sourceTree = \"<group>\"; };\n\t\tBB0B88202589734A00208600 /* notsurewhatthisis.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = notsurewhatthisis.png; sourceTree = \"<group>\"; };\n\t\tBB0C03002AEEC85500E5ECBB /* gen_apk_repositories.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gen_apk_repositories.py; path = app/gen_apk_repositories.py; sourceTree = \"<group>\"; };\n\t\tBB0DF6F22CC4B01000EFECAE /* fchdir.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fchdir.c; sourceTree = \"<group>\"; };\n\t\tBB0DF6F32CC4B01000EFECAE /* fchdir.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fchdir.h; sourceTree = \"<group>\"; };\n\t\tBB0F552D239F8A790032A2A1 /* Icons.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Icons.plist; sourceTree = \"<group>\"; };\n\t\tBB0F552F239F8B360032A2A1 /* uninspired.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = uninspired.png; sourceTree = \"<group>\"; };\n\t\tBB0F553123A0AB9B0032A2A1 /* pydann1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pydann1.png; sourceTree = \"<group>\"; };\n\t\tBB0F553323A0AC760032A2A1 /* ihash1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ihash1.png; sourceTree = \"<group>\"; };\n\t\tBB0F553523A0ACFC0032A2A1 /* pydann2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pydann2.png; sourceTree = \"<group>\"; };\n\t\tBB0FC5901F980A6B00803272 /* Terminal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Terminal.h; sourceTree = \"<group>\"; };\n\t\tBB0FC5911F980A6B00803272 /* Terminal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Terminal.m; sourceTree = \"<group>\"; };\n\t\tBB101B362364CF57000A93BC /* FontPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FontPickerViewController.h; sourceTree = \"<group>\"; };\n\t\tBB101B372364CF57000A93BC /* FontPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FontPickerViewController.m; sourceTree = \"<group>\"; };\n\t\tBB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = libarchive.xcodeproj; path = ../deps/libarchive.xcodeproj; sourceTree = \"<group>\"; };\n\t\tBB10E5C2248DBA7B009C7A74 /* fakefs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fakefs.c; path = tools/fakefs.c; sourceTree = SOURCE_ROOT; };\n\t\tBB10E5CC248DBE66009C7A74 /* Roots.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Roots.h; sourceTree = \"<group>\"; };\n\t\tBB10E5CD248DBE66009C7A74 /* Roots.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Roots.m; sourceTree = \"<group>\"; };\n\t\tBB10E5CF248DC21D009C7A74 /* Roots.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Roots.storyboard; sourceTree = \"<group>\"; };\n\t\tBB10E5D1248DCFEA009C7A74 /* RootsTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RootsTableViewController.h; sourceTree = \"<group>\"; };\n\t\tBB10E5D2248DCFEA009C7A74 /* RootsTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RootsTableViewController.m; sourceTree = \"<group>\"; };\n\t\tBB123ACB26C9EFD900419CDA /* LinuxTTY.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxTTY.c; sourceTree = \"<group>\"; };\n\t\tBB123ACD26C9F13500419CDA /* IOSCalls.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IOSCalls.m; sourceTree = \"<group>\"; };\n\t\tBB13F4DC21C5770000343E17 /* NSError+ISHErrno.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"NSError+ISHErrno.h\"; sourceTree = \"<group>\"; };\n\t\tBB13F4DD21C5770000343E17 /* NSError+ISHErrno.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"NSError+ISHErrno.m\"; sourceTree = \"<group>\"; };\n\t\tBB13F7C8200ACC24003D1C4D /* xcode-meson.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = \"xcode-meson.sh\"; path = \"app/xcode-meson.sh\"; sourceTree = \"<group>\"; };\n\t\tBB13F7DC200AD81D003D1C4D /* libish.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libish.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB149E7E256DC97C00F57815 /* PassthroughView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PassthroughView.h; sourceTree = \"<group>\"; };\n\t\tBB149E7F256DC97C00F57815 /* PassthroughView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PassthroughView.m; sourceTree = \"<group>\"; };\n\t\tBB1B9A4223A5E96900414052 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = app/Icons/icon.png; sourceTree = SOURCE_ROOT; };\n\t\tBB1D9D91234A8FE100F364E8 /* AboutNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutNavigationController.h; sourceTree = \"<group>\"; };\n\t\tBB1D9D92234A8FE100F364E8 /* AboutNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutNavigationController.m; sourceTree = \"<group>\"; };\n\t\tBB21A16F2689041500BD19B4 /* libfakefs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfakefs.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB21A18C268951CA00BD19B4 /* NotLinux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NotLinux.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB235533235D488400139E00 /* LocationDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocationDevice.m; sourceTree = \"<group>\"; };\n\t\tBB235535235D489400139E00 /* LocationDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocationDevice.h; sourceTree = \"<group>\"; };\n\t\tBB235536235D49B300139E00 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };\n\t\tBB23F58B231E1D1400585522 /* ScrollbarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollbarView.h; sourceTree = \"<group>\"; };\n\t\tBB23F58C231E1D1400585522 /* ScrollbarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScrollbarView.m; sourceTree = \"<group>\"; };\n\t\tBB267FA423A48F1500ED7CAF /* AltIconViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltIconViewController.h; sourceTree = \"<group>\"; };\n\t\tBB267FA523A48F1500ED7CAF /* AltIconViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AltIconViewController.m; sourceTree = \"<group>\"; };\n\t\tBB28C7522689522700BDC834 /* ProjectReleaseLinux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ProjectReleaseLinux.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB28C7532689522C00BDC834 /* ProjectDebugLinux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ProjectDebugLinux.xcconfig; sourceTree = \"<group>\"; };\n\t\tBB28C755268956C900BDC834 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };\n\t\tBB2B4DAB231D94C300CB578B /* hterm_all.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = hterm_all.js; path = ../../deps/libapps/hterm/dist/js/hterm_all.js; sourceTree = \"<group>\"; };\n\t\tBB38597627BCEC70000A1082 /* circular.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = circular.png; sourceTree = \"<group>\"; };\n\t\tBB38597927BCEC78000A1082 /* dollarblock1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dollarblock1.png; sourceTree = \"<group>\"; };\n\t\tBB38597A27BCEC78000A1082 /* dollarblock2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dollarblock2.png; sourceTree = \"<group>\"; };\n\t\tBB38597B27BCEC78000A1082 /* freeiosterminal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = freeiosterminal.png; sourceTree = \"<group>\"; };\n\t\tBB38598227BCEC83000A1082 /* ishcolontildehash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ishcolontildehash.png; sourceTree = \"<group>\"; };\n\t\tBB38598327BCEC83000A1082 /* is.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = is.png; sourceTree = \"<group>\"; };\n\t\tBB41591C255EF9E300E0950C /* iSHUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iSHUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB41591E255EF9E300E0950C /* UITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UITests.m; sourceTree = \"<group>\"; };\n\t\tBB415920255EF9E300E0950C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBB455E0F1FB37F6600AFB48B /* DelayedUITask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DelayedUITask.h; sourceTree = \"<group>\"; };\n\t\tBB455E101FB37F6600AFB48B /* DelayedUITask.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DelayedUITask.m; sourceTree = \"<group>\"; };\n\t\tBB4A539C1FAA490C00A72ACE /* term.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = term.js; sourceTree = \"<group>\"; };\n\t\tBB4A53A91FAA496700A72ACE /* term.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = term.html; sourceTree = \"<group>\"; };\n\t\tBB4A53AF1FAA787900A72ACE /* term.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = term.css; sourceTree = \"<group>\"; };\n\t\tBB4A922D24EDA461002F5A96 /* icon1337.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon1337.png; sourceTree = \"<group>\"; };\n\t\tBB4A922F24EDA55C002F5A96 /* colontildehash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = colontildehash.png; sourceTree = \"<group>\"; };\n\t\tBB4A923124EDA560002F5A96 /* 3d.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 3d.png; sourceTree = \"<group>\"; };\n\t\tBB565EA32734F1FB00C93EAE /* CurrentRoot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CurrentRoot.m; sourceTree = \"<group>\"; };\n\t\tBB565EA52734F22D00C93EAE /* CurrentRoot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CurrentRoot.h; sourceTree = \"<group>\"; };\n\t\tBB60F55021573FCA003A4E52 /* BarButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BarButton.h; sourceTree = \"<group>\"; };\n\t\tBB60F55121573FCA003A4E52 /* BarButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BarButton.m; sourceTree = \"<group>\"; };\n\t\tBB6DB260216435330047A611 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };\n\t\tBB78AB291FAD22440013E782 /* TerminalView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TerminalView.h; sourceTree = \"<group>\"; };\n\t\tBB78AB2A1FAD22440013E782 /* TerminalView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TerminalView.m; sourceTree = \"<group>\"; };\n\t\tBB792B501F96D90D00FFB7A4 /* iSH.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSH.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB792B531F96D90D00FFB7A4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\tBB792B541F96D90D00FFB7A4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\tBB792B561F96D90D00FFB7A4 /* TerminalViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TerminalViewController.h; sourceTree = \"<group>\"; };\n\t\tBB792B571F96D90D00FFB7A4 /* TerminalViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TerminalViewController.m; sourceTree = \"<group>\"; };\n\t\tBB792B5A1F96D90D00FFB7A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Terminal.storyboard; sourceTree = \"<group>\"; };\n\t\tBB792B5C1F96D90D00FFB7A4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tBB792B5F1F96D90D00FFB7A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tBB792B611F96D90D00FFB7A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBB792B621F96D90D00FFB7A4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\tBB7D93352087C2880008DA78 /* xX_main_Xx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xX_main_Xx.h; sourceTree = \"<group>\"; };\n\t\tBB7D933B2087C2880008DA78 /* misc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = misc.h; sourceTree = \"<group>\"; };\n\t\tBB7D93812087C2890008DA78 /* debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = \"<group>\"; };\n\t\tBB7D93822087C2890008DA78 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = \"<group>\"; };\n\t\tBB82A7FB21B4C2E8006AA5FD /* AboutExternalKeyboardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutExternalKeyboardViewController.h; sourceTree = \"<group>\"; };\n\t\tBB82A7FC21B4C2E8006AA5FD /* AboutExternalKeyboardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutExternalKeyboardViewController.m; sourceTree = \"<group>\"; };\n\t\tBB88F4902154760800A341FD /* iSHFileProvider.appex */ = {isa = PBXFileReference; explicitFileType = \"wrapper.app-extension\"; includeInIndex = 0; path = iSHFileProvider.appex; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBB88F4922154760800A341FD /* FileProviderExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileProviderExtension.h; sourceTree = \"<group>\"; };\n\t\tBB88F4932154760800A341FD /* FileProviderExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileProviderExtension.m; sourceTree = \"<group>\"; };\n\t\tBB88F4952154760800A341FD /* FileProviderItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileProviderItem.h; sourceTree = \"<group>\"; };\n\t\tBB88F4962154760800A341FD /* FileProviderItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileProviderItem.m; sourceTree = \"<group>\"; };\n\t\tBB88F4982154760800A341FD /* FileProviderEnumerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileProviderEnumerator.h; sourceTree = \"<group>\"; };\n\t\tBB88F4992154760800A341FD /* FileProviderEnumerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileProviderEnumerator.m; sourceTree = \"<group>\"; };\n\t\tBB88F49B2154760800A341FD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBB88F49C2154760800A341FD /* iSHFileProvider.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iSHFileProvider.entitlements; sourceTree = \"<group>\"; };\n\t\tBB88F4A4215476BA00A341FD /* iSH.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iSH.entitlements; sourceTree = \"<group>\"; };\n\t\tBB8C3AFD26B7B8AF00E38DDC /* fakefs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fakefs.c; path = linux/fakefs.c; sourceTree = SOURCE_ROOT; };\n\t\tBB9C7B84240A240E00F5D4F0 /* AppGroup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppGroup.m; sourceTree = \"<group>\"; };\n\t\tBB9C7B86240A29DE00F5D4F0 /* AppGroup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppGroup.h; sourceTree = \"<group>\"; };\n\t\tBB9C7B88240A343400F5D4F0 /* iSH.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = iSH.xcconfig; path = app/iSH.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBA5D87C27536E7600B39D77 /* UpgradeRootViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UpgradeRootViewController.h; sourceTree = \"<group>\"; };\n\t\tBBA5D87D27536E7600B39D77 /* UpgradeRootViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UpgradeRootViewController.m; sourceTree = \"<group>\"; };\n\t\tBBA8E2C0236FF5EA00515F76 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };\n\t\tBBAEE338249B58E80069EBB5 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; };\n\t\tBBAEE33A249BDADC0069EBB5 /* ProgressReportViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgressReportViewController.h; sourceTree = \"<group>\"; };\n\t\tBBAEE33B249BDADC0069EBB5 /* ProgressReportViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgressReportViewController.m; sourceTree = \"<group>\"; };\n\t\tBBBCE7E421D2F02200CA00B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = \"<group>\"; };\n\t\tBBBDDF8F2CE00F6B0071F1F3 /* libiSHLinuxUser.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiSHLinuxUser.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibLinuxUser.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBBDDF912CE0122F0071F1F3 /* emu_asbestos.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = emu_asbestos.c; path = linux/emu_asbestos.c; sourceTree = SOURCE_ROOT; };\n\t\tBBBF7B5B2415CDBB00EC4C14 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.plug-in\"; path = Settings.bundle; sourceTree = \"<group>\"; };\n\t\tBBBF9CBB27C217A6002A30F7 /* PasteboardDeviceLinux.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = PasteboardDeviceLinux.c; sourceTree = \"<group>\"; };\n\t\tBBBF9CC027C441C0002A30F7 /* fix_path.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fix_path.h; sourceTree = \"<group>\"; };\n\t\tBBBF9CC127C441C0002A30F7 /* fake-db.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"fake-db.h\"; sourceTree = \"<group>\"; };\n\t\tBBBF9CC227C448D5002A30F7 /* fake-db.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = \"fake-db.c\"; sourceTree = \"<group>\"; };\n\t\tBBC8297D24EDAC11009D042C /* idollarhash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = idollarhash.png; sourceTree = \"<group>\"; };\n\t\tBBC8298024EDACBB009D042C /* iinhash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = iinhash.png; sourceTree = \"<group>\"; };\n\t\tBBC8298624EE5853009D042C /* metal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = metal.png; sourceTree = \"<group>\"; };\n\t\tBBCC9D942365430800424C83 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = \"<group>\"; };\n\t\tBBCC9D952365430800424C83 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = \"<group>\"; };\n\t\tBBCED895255BB65A00CA0701 /* NSObject+SaneKVO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"NSObject+SaneKVO.h\"; sourceTree = \"<group>\"; };\n\t\tBBCED896255BB65A00CA0701 /* NSObject+SaneKVO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"NSObject+SaneKVO.m\"; sourceTree = \"<group>\"; };\n\t\tBBD23D5E258DA450003DCB40 /* Screenshots.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Screenshots.m; sourceTree = \"<group>\"; };\n\t\tBBD23D76258DA4BC003DCB40 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; };\n\t\tBBD23D95258DEF6B003DCB40 /* Screenshots.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Screenshots.xctestplan; sourceTree = \"<group>\"; };\n\t\tBBECF39B2691311D00DEC937 /* LinuxInterop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LinuxInterop.h; sourceTree = \"<group>\"; };\n\t\tBBECF39D2691313B00DEC937 /* LinuxInterop.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxInterop.c; sourceTree = \"<group>\"; };\n\t\tBBECF3A22691314C00DEC937 /* libiSHLinux.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiSHLinux.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBECF3B8269136E100DEC937 /* liblinux.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblinux.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibLinux.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBEEA9E7277D25090069495B /* LinuxRoot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxRoot.c; sourceTree = \"<group>\"; };\n\t\tBBEEA9E9277DAB400069495B /* LinuxPTY.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxPTY.c; sourceTree = \"<group>\"; };\n\t\tBBEF191E26806364001225BD /* libiSHApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiSHApp.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBEF192C26806546001225BD /* AppLib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppLib.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBEF199C268066D1001225BD /* iSH.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSH.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBEF19BA26806D7E001225BD /* Linux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Linux.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBFB2C5B2590257E00545EAB /* libish_emu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libish_emu.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLib.xcconfig; sourceTree = \"<group>\"; };\n\t\tBBFB55652158644C00DFE6DE /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };\n\t\tBBFB556F21586C4800DFE6DE /* UIViewController+Extras.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"UIViewController+Extras.h\"; sourceTree = \"<group>\"; };\n\t\tBBFB557021586C4800DFE6DE /* UIViewController+Extras.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"UIViewController+Extras.m\"; sourceTree = \"<group>\"; };\n\t\tBBFB5577215876CD00DFE6DE /* AboutViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutViewController.h; sourceTree = \"<group>\"; };\n\t\tBBFB5578215876CD00DFE6DE /* AboutViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutViewController.m; sourceTree = \"<group>\"; };\n\t\tBBFB557A215878C600DFE6DE /* UIApplication+OpenURL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"UIApplication+OpenURL.h\"; sourceTree = \"<group>\"; };\n\t\tBBFB557B215878C600DFE6DE /* UIApplication+OpenURL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"UIApplication+OpenURL.m\"; sourceTree = \"<group>\"; };\n\t\tBBFB557E21587B6800DFE6DE /* ArrowBarButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ArrowBarButton.h; sourceTree = \"<group>\"; };\n\t\tBBFB557F21587B6800DFE6DE /* ArrowBarButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ArrowBarButton.m; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t497F6D44254E605F00C82F46 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t497F6D87254E62E100C82F46 /* libish.a in Frameworks */,\n\t\t\t\tBBFB2C7E259026C200545EAB /* libish_emu.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB415919255EF9E300E0950C /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB792B4D1F96D90D00FFB7A4 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB28C7AE26896C1D00BDC834 /* libiSHApp.a in Frameworks */,\n\t\t\t\tBB21A18426890C7E00BD19B4 /* libfakefs.a in Frameworks */,\n\t\t\t\tBB13F7EA200ADCED003D1C4D /* libish.a in Frameworks */,\n\t\t\t\tBBFB2CEC2590296B00545EAB /* libish_emu.a in Frameworks */,\n\t\t\t\tBB10E5C9248DBAAC009C7A74 /* libarchive.a in Frameworks */,\n\t\t\t\tBB235537235D49B300139E00 /* CoreLocation.framework in Frameworks */,\n\t\t\t\tBBA8E2C1236FF5EA00515F76 /* SystemConfiguration.framework in Frameworks */,\n\t\t\t\tBBAEE339249B58E80069EBB5 /* libbz2.tbd in Frameworks */,\n\t\t\t\tBB6DB261216435340047A611 /* libiconv.tbd in Frameworks */,\n\t\t\t\tBBFB55662158644C00DFE6DE /* libresolv.tbd in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB88F48D2154760800A341FD /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB21A1862689500000BD19B4 /* libish_emu.a in Frameworks */,\n\t\t\t\tBB21A1702689048200BD19B4 /* libfakefs.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBEF196F268066D1001225BD /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBECF3B9269136FB00DEC937 /* liblinux.a in Frameworks */,\n\t\t\t\tBBECF3AE2691330F00DEC937 /* libiSHLinux.a in Frameworks */,\n\t\t\t\tBBBDDF932CE0132F0071F1F3 /* libiSHLinuxUser.a in Frameworks */,\n\t\t\t\tBBEF1970268066D1001225BD /* libiSHApp.a in Frameworks */,\n\t\t\t\tBBBD741426B7BE2E00321AC5 /* libfakefs.a in Frameworks */,\n\t\t\t\tBBEF1972268066D1001225BD /* libish_emu.a in Frameworks */,\n\t\t\t\tBBEF1973268066D1001225BD /* libarchive.a in Frameworks */,\n\t\t\t\tBBEF1974268066D1001225BD /* CoreLocation.framework in Frameworks */,\n\t\t\t\tBBEF1975268066D1001225BD /* SystemConfiguration.framework in Frameworks */,\n\t\t\t\tBBEF1976268066D1001225BD /* libbz2.tbd in Frameworks */,\n\t\t\t\tBBEF1977268066D1001225BD /* libiconv.tbd in Frameworks */,\n\t\t\t\tBBEF1978268066D1001225BD /* libresolv.tbd in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t497F6BF1254E5C0D00C82F46 /* proc */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6BF4254E5C0D00C82F46 /* entry.c */,\n\t\t\t\t49302D8D277669D300C9885A /* ish.c */,\n\t\t\t\t49302D972777FAB400C9885A /* ish.h */,\n\t\t\t\t497F6BF2254E5C0D00C82F46 /* pid.c */,\n\t\t\t\t497F6BF3254E5C0D00C82F46 /* root.c */,\n\t\t\t);\n\t\t\tpath = proc;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t497F6C27254E5C4F00C82F46 /* gadgets-x86_64 */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6C28254E5C4F00C82F46 /* control.S */,\n\t\t\t\t497F6C29254E5C4F00C82F46 /* misc.S */,\n\t\t\t\t497F6C2A254E5C4F00C82F46 /* entry.S */,\n\t\t\t\t497F6C2B254E5C4F00C82F46 /* math.S */,\n\t\t\t\t497F6C2C254E5C4F00C82F46 /* memory.S */,\n\t\t\t\t497F6C2D254E5C4F00C82F46 /* gadgets.h */,\n\t\t\t\t497F6C2E254E5C4F00C82F46 /* string.S */,\n\t\t\t\t497F6C2F254E5C4F00C82F46 /* bits.S */,\n\t\t\t);\n\t\t\tpath = \"gadgets-x86_64\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t497F6C31254E5C4F00C82F46 /* gadgets-aarch64 */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6C32254E5C4F00C82F46 /* control.S */,\n\t\t\t\t497F6C33254E5C4F00C82F46 /* misc.S */,\n\t\t\t\t497F6C34254E5C4F00C82F46 /* entry.S */,\n\t\t\t\t497F6C35254E5C4F00C82F46 /* math.S */,\n\t\t\t\t497F6C36254E5C4F00C82F46 /* memory.S */,\n\t\t\t\t497F6C37254E5C4F00C82F46 /* math.h */,\n\t\t\t\t497F6C38254E5C4F00C82F46 /* gadgets.h */,\n\t\t\t\t497F6C39254E5C4F00C82F46 /* string.S */,\n\t\t\t\t497F6C3A254E5C4F00C82F46 /* bits.S */,\n\t\t\t);\n\t\t\tpath = \"gadgets-aarch64\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB0B848D2586A4B100208600 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB0B86CD2586FD8800208600 /* App.xcconfig */,\n\t\t\t\tBBEF192C26806546001225BD /* AppLib.xcconfig */,\n\t\t\t\t493B378F290F5320000C41ED /* CLI.xcconfig */,\n\t\t\t\tBB0B86D52587009D00208600 /* iOS.xcconfig */,\n\t\t\t\tBBEF19BA26806D7E001225BD /* Linux.xcconfig */,\n\t\t\t\tBB21A18C268951CA00BD19B4 /* NotLinux.xcconfig */,\n\t\t\t\tBB0B85AD2586F3EB00208600 /* Project.xcconfig */,\n\t\t\t\tBB0B85A42586F1CB00208600 /* ProjectDebug.xcconfig */,\n\t\t\t\tBB28C7532689522C00BDC834 /* ProjectDebugLinux.xcconfig */,\n\t\t\t\tBB0B85A52586F1E100208600 /* ProjectRelease.xcconfig */,\n\t\t\t\tBB28C7522689522700BDC834 /* ProjectReleaseLinux.xcconfig */,\n\t\t\t\tBBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */,\n\t\t\t\tBBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */,\n\t\t\t\tBBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */,\n\t\t\t\tBB0B859B2586F13200208600 /* XcodeDebug.xcconfig */,\n\t\t\t\tBB0B848E2586A4CF00208600 /* XcodeDefault.xcconfig */,\n\t\t\t\tBB0B859C2586F19000208600 /* XcodeRelease.xcconfig */,\n\t\t\t);\n\t\t\tname = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB10E5C1248DB961009C7A74 /* Roots */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB10E5CC248DBE66009C7A74 /* Roots.h */,\n\t\t\t\tBB10E5CD248DBE66009C7A74 /* Roots.m */,\n\t\t\t\tBB10E5CF248DC21D009C7A74 /* Roots.storyboard */,\n\t\t\t\tBB10E5D1248DCFEA009C7A74 /* RootsTableViewController.h */,\n\t\t\t\tBB10E5D2248DCFEA009C7A74 /* RootsTableViewController.m */,\n\t\t\t\tBBAEE33A249BDADC0069EBB5 /* ProgressReportViewController.h */,\n\t\t\t\tBBAEE33B249BDADC0069EBB5 /* ProgressReportViewController.m */,\n\t\t\t\tBB10E5C2248DBA7B009C7A74 /* fakefs.c */,\n\t\t\t\tBB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */,\n\t\t\t);\n\t\t\tname = Roots;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB10E5C4248DBAA1009C7A74 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB10E5C8248DBAA1009C7A74 /* libarchive.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB18B27F1F97F2590059FCD8 /* Scripts */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB13F7C8200ACC24003D1C4D /* xcode-meson.sh */,\n\t\t\t\t494D03F72727DAC6004F0CCE /* xcode-ninja.sh */,\n\t\t\t\tBB0C03002AEEC85500E5ECBB /* gen_apk_repositories.py */,\n\t\t\t);\n\t\t\tname = Scripts;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB1B9A4123A5E92A00414052 /* Icons */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB0F552D239F8A790032A2A1 /* Icons.plist */,\n\t\t\t\tBB4A923124EDA560002F5A96 /* 3d.png */,\n\t\t\t\tBB38597627BCEC70000A1082 /* circular.png */,\n\t\t\t\tBB4A922F24EDA55C002F5A96 /* colontildehash.png */,\n\t\t\t\tBB38597927BCEC78000A1082 /* dollarblock1.png */,\n\t\t\t\tBB38597A27BCEC78000A1082 /* dollarblock2.png */,\n\t\t\t\tBB38597B27BCEC78000A1082 /* freeiosterminal.png */,\n\t\t\t\tBB1B9A4223A5E96900414052 /* icon.png */,\n\t\t\t\tBB4A922D24EDA461002F5A96 /* icon1337.png */,\n\t\t\t\tBBC8297D24EDAC11009D042C /* idollarhash.png */,\n\t\t\t\tBB0F553323A0AC760032A2A1 /* ihash1.png */,\n\t\t\t\tBBC8298024EDACBB009D042C /* iinhash.png */,\n\t\t\t\tBB38598327BCEC83000A1082 /* is.png */,\n\t\t\t\tBB38598227BCEC83000A1082 /* ishcolontildehash.png */,\n\t\t\t\tBBC8298624EE5853009D042C /* metal.png */,\n\t\t\t\tBB0B88202589734A00208600 /* notsurewhatthisis.png */,\n\t\t\t\tBB0F553123A0AB9B0032A2A1 /* pydann1.png */,\n\t\t\t\tBB0F553523A0ACFC0032A2A1 /* pydann2.png */,\n\t\t\t\tBB0B88172589662A00208600 /* reworked.png */,\n\t\t\t\tBB0B88052589661A00208600 /* rgb.png */,\n\t\t\t\tBB0B880E2589662200208600 /* sprite64.png */,\n\t\t\t\tBB0F552F239F8B360032A2A1 /* uninspired.png */,\n\t\t\t);\n\t\t\tpath = Icons;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB235532235D472F00139E00 /* Devices */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t650B337222EA235C00B4C03E /* PasteboardDevice.h */,\n\t\t\t\t650B337322EA235C00B4C03E /* PasteboardDevice.m */,\n\t\t\t\tBBBF9CBB27C217A6002A30F7 /* PasteboardDeviceLinux.c */,\n\t\t\t\tBB235535235D489400139E00 /* LocationDevice.h */,\n\t\t\t\tBB235533235D488400139E00 /* LocationDevice.m */,\n\t\t\t\t408A263B23644102008A4E81 /* iOSFS.h */,\n\t\t\t\t408A2639236440F8008A4E81 /* iOSFS.m */,\n\t\t\t);\n\t\t\tname = Devices;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB2D71072354244700A10D1E /* platform */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6CC3254E5CB300C82F46 /* darwin.c */,\n\t\t\t\t497F6CC4254E5CB300C82F46 /* linux.c */,\n\t\t\t\t497F6CC2254E5CB300C82F46 /* platform.h */,\n\t\t\t);\n\t\t\tpath = platform;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB41591D255EF9E300E0950C /* UITests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB41591E255EF9E300E0950C /* UITests.m */,\n\t\t\t\tBBD23D5E258DA450003DCB40 /* Screenshots.m */,\n\t\t\t\tBBD23D76258DA4BC003DCB40 /* SnapshotHelper.swift */,\n\t\t\t\tBBD23D95258DEF6B003DCB40 /* Screenshots.xctestplan */,\n\t\t\t\tBB415920255EF9E300E0950C /* Info.plist */,\n\t\t\t);\n\t\t\tpath = UITests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB4A53931FAA393B00A72ACE /* most of the code */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB7D93822087C2890008DA78 /* main.c */,\n\t\t\t\tBB7D93812087C2890008DA78 /* debug.h */,\n\t\t\t\tBB7D933B2087C2880008DA78 /* misc.h */,\n\t\t\t\tBB7D93352087C2880008DA78 /* xX_main_Xx.h */,\n\t\t\t\tBB88F4712152F75A00A341FD /* asbestos */,\n\t\t\t\tBB8636F82167F4F200E7ADC0 /* emu */,\n\t\t\t\tBB7D936A2087C2890008DA78 /* fs */,\n\t\t\t\tBB7D933C2087C2880008DA78 /* kernel */,\n\t\t\t\tBB2D71072354244700A10D1E /* platform */,\n\t\t\t\tBB7D93362087C2880008DA78 /* util */,\n\t\t\t);\n\t\t\tname = \"most of the code\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB4A53991FAA40FD00A72ACE /* terminal */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB4A53A91FAA496700A72ACE /* term.html */,\n\t\t\t\tBB4A539C1FAA490C00A72ACE /* term.js */,\n\t\t\t\tBB4A53AF1FAA787900A72ACE /* term.css */,\n\t\t\t\tBB2B4DAB231D94C300CB578B /* hterm_all.js */,\n\t\t\t);\n\t\t\tpath = terminal;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB792B451F96D8E000FFB7A4 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB792B521F96D90D00FFB7A4 /* app */,\n\t\t\t\tBB9C7B88240A343400F5D4F0 /* iSH.xcconfig */,\n\t\t\t\tBB4A53931FAA393B00A72ACE /* most of the code */,\n\t\t\t\tBB18B27F1F97F2590059FCD8 /* Scripts */,\n\t\t\t\tBB792B511F96D90D00FFB7A4 /* Products */,\n\t\t\t\tBB792B7D1F96E32B00FFB7A4 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t\tusesTabs = 0;\n\t\t};\n\t\tBB792B511F96D90D00FFB7A4 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB792B501F96D90D00FFB7A4 /* iSH.app */,\n\t\t\t\tBB88F4902154760800A341FD /* iSHFileProvider.appex */,\n\t\t\t\tBB13F7DC200AD81D003D1C4D /* libish.a */,\n\t\t\t\tBBFB2C5B2590257E00545EAB /* libish_emu.a */,\n\t\t\t\tBB41591C255EF9E300E0950C /* iSHUITests.xctest */,\n\t\t\t\t497F6D47254E605F00C82F46 /* ish */,\n\t\t\t\t497F6CE4254E5E4C00C82F46 /* MakeXcodeAutoCompleteWork */,\n\t\t\t\tBBEF191E26806364001225BD /* libiSHApp.a */,\n\t\t\t\tBBEF199C268066D1001225BD /* iSH.app */,\n\t\t\t\tBB21A16F2689041500BD19B4 /* libfakefs.a */,\n\t\t\t\tBBECF3A22691314C00DEC937 /* libiSHLinux.a */,\n\t\t\t\tBBECF3B8269136E100DEC937 /* liblinux.a */,\n\t\t\t\tBBBDDF8F2CE00F6B0071F1F3 /* libiSHLinuxUser.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB792B521F96D90D00FFB7A4 /* app */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB792B531F96D90D00FFB7A4 /* AppDelegate.h */,\n\t\t\t\tBB792B541F96D90D00FFB7A4 /* AppDelegate.m */,\n\t\t\t\tBB565EA52734F22D00C93EAE /* CurrentRoot.h */,\n\t\t\t\tBB565EA32734F1FB00C93EAE /* CurrentRoot.m */,\n\t\t\t\tBBCC9D942365430800424C83 /* SceneDelegate.h */,\n\t\t\t\tBBCC9D952365430800424C83 /* SceneDelegate.m */,\n\t\t\t\tBB792B561F96D90D00FFB7A4 /* TerminalViewController.h */,\n\t\t\t\tBB792B571F96D90D00FFB7A4 /* TerminalViewController.m */,\n\t\t\t\tBB792B591F96D90D00FFB7A4 /* Terminal.storyboard */,\n\t\t\t\tBBFB557D21587B2B00DFE6DE /* Bar */,\n\t\t\t\tBB78AB291FAD22440013E782 /* TerminalView.h */,\n\t\t\t\tBB78AB2A1FAD22440013E782 /* TerminalView.m */,\n\t\t\t\tBB0FC5901F980A6B00803272 /* Terminal.h */,\n\t\t\t\tBB0FC5911F980A6B00803272 /* Terminal.m */,\n\t\t\t\tBB23F58B231E1D1400585522 /* ScrollbarView.h */,\n\t\t\t\tBB23F58C231E1D1400585522 /* ScrollbarView.m */,\n\t\t\t\tBBECF39C2691312500DEC937 /* LinuxInterop */,\n\t\t\t\tBB4A53991FAA40FD00A72ACE /* terminal */,\n\t\t\t\tBB235532235D472F00139E00 /* Devices */,\n\t\t\t\tBBFB557321586C7600DFE6DE /* About */,\n\t\t\t\tBB10E5C1248DB961009C7A74 /* Roots */,\n\t\t\t\tBBFB557221586C6600DFE6DE /* Utilities */,\n\t\t\t\tBBBF7B5B2415CDBB00EC4C14 /* Settings.bundle */,\n\t\t\t\tBB792B5C1F96D90D00FFB7A4 /* Assets.xcassets */,\n\t\t\t\tBB1B9A4123A5E92A00414052 /* Icons */,\n\t\t\t\tBB792B5E1F96D90D00FFB7A4 /* LaunchScreen.storyboard */,\n\t\t\t\tBB792B611F96D90D00FFB7A4 /* Info.plist */,\n\t\t\t\tBB792B621F96D90D00FFB7A4 /* main.m */,\n\t\t\t\tBB0B848D2586A4B100208600 /* Configs */,\n\t\t\t\tBB88F4A4215476BA00A341FD /* iSH.entitlements */,\n\t\t\t\tBB88F4912154760800A341FD /* FileProvider */,\n\t\t\t\tBB41591D255EF9E300E0950C /* UITests */,\n\t\t\t);\n\t\t\tpath = app;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB792B7D1F96E32B00FFB7A4 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB28C755268956C900BDC834 /* Foundation.framework */,\n\t\t\t\tBBAEE338249B58E80069EBB5 /* libbz2.tbd */,\n\t\t\t\tBBA8E2C0236FF5EA00515F76 /* SystemConfiguration.framework */,\n\t\t\t\tBB235536235D49B300139E00 /* CoreLocation.framework */,\n\t\t\t\tBB6DB260216435330047A611 /* libiconv.tbd */,\n\t\t\t\tBBFB55652158644C00DFE6DE /* libresolv.tbd */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB7D93362087C2880008DA78 /* util */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6CD3254E5CC800C82F46 /* bits.h */,\n\t\t\t\tBB0DF6F22CC4B01000EFECAE /* fchdir.c */,\n\t\t\t\tBB0DF6F32CC4B01000EFECAE /* fchdir.h */,\n\t\t\t\t497F6CCF254E5CC800C82F46 /* fifo.c */,\n\t\t\t\t497F6CCB254E5CC800C82F46 /* fifo.h */,\n\t\t\t\t497F6CD0254E5CC800C82F46 /* list.h */,\n\t\t\t\t497F6CCE254E5CC800C82F46 /* refcount.h */,\n\t\t\t\t497F6CD2254E5CC800C82F46 /* sync.c */,\n\t\t\t\t497F6CCC254E5CC800C82F46 /* sync.h */,\n\t\t\t\t497F6CCD254E5CC800C82F46 /* timer.c */,\n\t\t\t\t497F6CD1254E5CC800C82F46 /* timer.h */,\n\t\t\t);\n\t\t\tpath = util;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB7D933C2087C2880008DA78 /* kernel */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6C9F254E5C9800C82F46 /* calls.c */,\n\t\t\t\t497F6C7B254E5C9700C82F46 /* calls.h */,\n\t\t\t\t497F6C82254E5C9700C82F46 /* elf.h */,\n\t\t\t\t497F6C7D254E5C9700C82F46 /* epoll.c */,\n\t\t\t\t497F6C91254E5C9800C82F46 /* errno.c */,\n\t\t\t\t497F6C80254E5C9700C82F46 /* errno.h */,\n\t\t\t\t497F6C95254E5C9800C82F46 /* eventfd.c */,\n\t\t\t\t497F6C81254E5C9700C82F46 /* exec.c */,\n\t\t\t\t497F6C90254E5C9700C82F46 /* exit.c */,\n\t\t\t\t497F6C8D254E5C9700C82F46 /* fork.c */,\n\t\t\t\t497F6C96254E5C9800C82F46 /* fs_info.c */,\n\t\t\t\t497F6C98254E5C9800C82F46 /* fs.c */,\n\t\t\t\t497F6C88254E5C9700C82F46 /* fs.h */,\n\t\t\t\t497F6C9B254E5C9800C82F46 /* futex.c */,\n\t\t\t\t497F6C9A254E5C9800C82F46 /* futex.h */,\n\t\t\t\t497F6C99254E5C9800C82F46 /* getset.c */,\n\t\t\t\t497F6C8F254E5C9700C82F46 /* group.c */,\n\t\t\t\t497F6C7F254E5C9700C82F46 /* init.c */,\n\t\t\t\t497F6C97254E5C9800C82F46 /* init.h */,\n\t\t\t\t497F6C7A254E5C9700C82F46 /* ipc.c */,\n\t\t\t\t497F6C83254E5C9700C82F46 /* log.c */,\n\t\t\t\t497F6C85254E5C9700C82F46 /* misc.c */,\n\t\t\t\t497F6C77254E5C9700C82F46 /* mm.h */,\n\t\t\t\t497F6C9D254E5C9800C82F46 /* mmap.c */,\n\t\t\t\t497F6C92254E5C9800C82F46 /* personality.h */,\n\t\t\t\t497F6C94254E5C9800C82F46 /* poll.c */,\n\t\t\t\t497F6C8A254E5C9700C82F46 /* ptrace.c */,\n\t\t\t\t497F6C8C254E5C9700C82F46 /* ptrace.h */,\n\t\t\t\t497F6C7E254E5C9700C82F46 /* random.c */,\n\t\t\t\t497F6C9E254E5C9800C82F46 /* random.h */,\n\t\t\t\t497F6CA0254E5C9800C82F46 /* resource.c */,\n\t\t\t\t497F6CA1254E5C9800C82F46 /* resource.h */,\n\t\t\t\t497F6C87254E5C9700C82F46 /* signal.c */,\n\t\t\t\t497F6C7C254E5C9700C82F46 /* signal.h */,\n\t\t\t\t497F6C78254E5C9700C82F46 /* task.c */,\n\t\t\t\t497F6C9C254E5C9800C82F46 /* task.h */,\n\t\t\t\t497F6C89254E5C9700C82F46 /* time.c */,\n\t\t\t\t497F6C86254E5C9700C82F46 /* time.h */,\n\t\t\t\t497F6C8E254E5C9700C82F46 /* tls.c */,\n\t\t\t\t497F6C93254E5C9800C82F46 /* uname.c */,\n\t\t\t\t497F6C8B254E5C9700C82F46 /* user.c */,\n\t\t\t\t497F6C79254E5C9700C82F46 /* vdso.c */,\n\t\t\t\t497F6C84254E5C9700C82F46 /* vdso.h */,\n\t\t\t);\n\t\t\tpath = kernel;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB7D936A2087C2890008DA78 /* fs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6BFE254E5C0E00C82F46 /* adhoc.c */,\n\t\t\t\t497F6BDC254E5C0D00C82F46 /* dev.c */,\n\t\t\t\t497F6BDE254E5C0D00C82F46 /* dev.h */,\n\t\t\t\t497F6C01254E5C0E00C82F46 /* devices.h */,\n\t\t\t\t497F6BF9254E5C0E00C82F46 /* dir.c */,\n\t\t\t\t497F6C03254E5C0E00C82F46 /* dyndev.c */,\n\t\t\t\t497F6C00254E5C0E00C82F46 /* dyndev.h */,\n\t\t\t\t497F6BEA254E5C0D00C82F46 /* fake-migrate.c */,\n\t\t\t\t497F6BEF254E5C0D00C82F46 /* fake-rebuild.c */,\n\t\t\t\t497F6BE8254E5C0D00C82F46 /* fake.c */,\n\t\t\t\t497F6BEC254E5C0D00C82F46 /* fake.h */,\n\t\t\t\tBBBF9CC227C448D5002A30F7 /* fake-db.c */,\n\t\t\t\tBBBF9CC127C441C0002A30F7 /* fake-db.h */,\n\t\t\t\t497F6BE7254E5C0D00C82F46 /* fd.c */,\n\t\t\t\t497F6BF0254E5C0D00C82F46 /* fd.h */,\n\t\t\t\tBBBF9CC027C441C0002A30F7 /* fix_path.h */,\n\t\t\t\t497F6BE4254E5C0D00C82F46 /* generic.c */,\n\t\t\t\t497F6BE6254E5C0D00C82F46 /* inode.c */,\n\t\t\t\t497F6BFD254E5C0E00C82F46 /* inode.h */,\n\t\t\t\t497F6BE9254E5C0D00C82F46 /* lock.c */,\n\t\t\t\t497F6BFF254E5C0E00C82F46 /* mem.c */,\n\t\t\t\t497F6BDA254E5C0D00C82F46 /* mem.h */,\n\t\t\t\t497F6BE0254E5C0D00C82F46 /* mount.c */,\n\t\t\t\t497F6BDB254E5C0D00C82F46 /* path.c */,\n\t\t\t\t497F6BDF254E5C0D00C82F46 /* path.h */,\n\t\t\t\t497F6BFC254E5C0E00C82F46 /* pipe.c */,\n\t\t\t\t497F6BF6254E5C0E00C82F46 /* poll.c */,\n\t\t\t\t497F6BEE254E5C0D00C82F46 /* poll.h */,\n\t\t\t\t497F6BF1254E5C0D00C82F46 /* proc */,\n\t\t\t\t497F6BDD254E5C0D00C82F46 /* proc.c */,\n\t\t\t\t497F6BF5254E5C0E00C82F46 /* proc.h */,\n\t\t\t\t497F6C06254E5C0E00C82F46 /* pty.c */,\n\t\t\t\t497F6C04254E5C0E00C82F46 /* real.c */,\n\t\t\t\t497F6BF8254E5C0E00C82F46 /* real.h */,\n\t\t\t\t497F6BF7254E5C0E00C82F46 /* sock.c */,\n\t\t\t\t497F6BE1254E5C0D00C82F46 /* sock.h */,\n\t\t\t\t497F6BFB254E5C0E00C82F46 /* sockrestart.c */,\n\t\t\t\t497F6BFA254E5C0E00C82F46 /* sockrestart.h */,\n\t\t\t\t497F6BED254E5C0D00C82F46 /* sqlutil.h */,\n\t\t\t\t497F6BE3254E5C0D00C82F46 /* stat.c */,\n\t\t\t\t497F6C02254E5C0E00C82F46 /* stat.h */,\n\t\t\t\t497F6BEB254E5C0D00C82F46 /* tmp.c */,\n\t\t\t\t497F6C05254E5C0E00C82F46 /* tty-real.c */,\n\t\t\t\t497F6BE2254E5C0D00C82F46 /* tty.c */,\n\t\t\t\t497F6BE5254E5C0D00C82F46 /* tty.h */,\n\t\t\t);\n\t\t\tpath = fs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB8636F82167F4F200E7ADC0 /* emu */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0FC2383B2991FB82004C09EC /* mmx.c */,\n\t\t\t\t497F6C62254E5C7F00C82F46 /* cpu.h */,\n\t\t\t\t497F6C58254E5C7E00C82F46 /* cpuid.h */,\n\t\t\t\t497F6C5E254E5C7E00C82F46 /* decode.h */,\n\t\t\t\t497F6C5A254E5C7E00C82F46 /* float80-test.c */,\n\t\t\t\t497F6C66254E5C7F00C82F46 /* float80.c */,\n\t\t\t\t497F6C5B254E5C7E00C82F46 /* float80.h */,\n\t\t\t\t497F6C5D254E5C7E00C82F46 /* fpu.c */,\n\t\t\t\t497F6C63254E5C7F00C82F46 /* fpu.h */,\n\t\t\t\t497F6C5C254E5C7E00C82F46 /* interrupt.h */,\n\t\t\t\t497F6C60254E5C7F00C82F46 /* memory.c */,\n\t\t\t\t497F6C61254E5C7F00C82F46 /* memory.h */,\n\t\t\t\t497F6C6A254E5C7F00C82F46 /* modrm.h */,\n\t\t\t\t497F6C65254E5C7F00C82F46 /* regid.h */,\n\t\t\t\t497F6C59254E5C7E00C82F46 /* tlb.c */,\n\t\t\t\t497F6C6B254E5C7F00C82F46 /* tlb.h */,\n\t\t\t\t497F6C64254E5C7F00C82F46 /* vec.c */,\n\t\t\t\t497F6C5F254E5C7F00C82F46 /* vec.h */,\n\t\t\t);\n\t\t\tpath = emu;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB88F4712152F75A00A341FD /* asbestos */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497F6C40254E5C4F00C82F46 /* asbestos.c */,\n\t\t\t\t497F6C3D254E5C4F00C82F46 /* gen.c */,\n\t\t\t\t497F6C3B254E5C4F00C82F46 /* helpers.c */,\n\t\t\t\t497F6C3C254E5C4F00C82F46 /* offsets.c */,\n\t\t\t\t497F6C41254E5C4F00C82F46 /* asbestos.h */,\n\t\t\t\t497F6C3F254E5C4F00C82F46 /* frame.h */,\n\t\t\t\t497F6C30254E5C4F00C82F46 /* gadgets-generic.h */,\n\t\t\t\t497F6C3E254E5C4F00C82F46 /* gen.h */,\n\t\t\t\t497F6C31254E5C4F00C82F46 /* gadgets-aarch64 */,\n\t\t\t\t497F6C27254E5C4F00C82F46 /* gadgets-x86_64 */,\n\t\t\t);\n\t\t\tpath = asbestos;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB88F4912154760800A341FD /* FileProvider */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB88F4922154760800A341FD /* FileProviderExtension.h */,\n\t\t\t\tBB88F4932154760800A341FD /* FileProviderExtension.m */,\n\t\t\t\tBB88F4952154760800A341FD /* FileProviderItem.h */,\n\t\t\t\tBB88F4962154760800A341FD /* FileProviderItem.m */,\n\t\t\t\tBB88F4982154760800A341FD /* FileProviderEnumerator.h */,\n\t\t\t\tBB88F4992154760800A341FD /* FileProviderEnumerator.m */,\n\t\t\t\tBB13F4DC21C5770000343E17 /* NSError+ISHErrno.h */,\n\t\t\t\tBB13F4DD21C5770000343E17 /* NSError+ISHErrno.m */,\n\t\t\t\tBB88F49B2154760800A341FD /* Info.plist */,\n\t\t\t\tBB88F49C2154760800A341FD /* iSHFileProvider.entitlements */,\n\t\t\t);\n\t\t\tpath = FileProvider;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBECF39C2691312500DEC937 /* LinuxInterop */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBBECF39B2691311D00DEC937 /* LinuxInterop.h */,\n\t\t\t\tBB123ACD26C9F13500419CDA /* IOSCalls.m */,\n\t\t\t\tBBECF39D2691313B00DEC937 /* LinuxInterop.c */,\n\t\t\t\tBB123ACB26C9EFD900419CDA /* LinuxTTY.c */,\n\t\t\t\tBBEEA9E9277DAB400069495B /* LinuxPTY.c */,\n\t\t\t\tBBEEA9E7277D25090069495B /* LinuxRoot.c */,\n\t\t\t\tBB8C3AFD26B7B8AF00E38DDC /* fakefs.c */,\n\t\t\t\tBBBDDF912CE0122F0071F1F3 /* emu_asbestos.c */,\n\t\t\t);\n\t\t\tname = LinuxInterop;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBFB557221586C6600DFE6DE /* Utilities */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t497A08BC2961168400B323CF /* AccessibilityFixes.m */,\n\t\t\t\tBB9C7B86240A29DE00F5D4F0 /* AppGroup.h */,\n\t\t\t\tBB9C7B84240A240E00F5D4F0 /* AppGroup.m */,\n\t\t\t\tBB455E0F1FB37F6600AFB48B /* DelayedUITask.h */,\n\t\t\t\tBB455E101FB37F6600AFB48B /* DelayedUITask.m */,\n\t\t\t\t49FF844C2A0624AF00850B7A /* ExceptionExfiltrator.h */,\n\t\t\t\t49FF844A2A06249F00850B7A /* ExceptionExfiltrator.m */,\n\t\t\t\t49087C14295DF0490058075B /* hook.c */,\n\t\t\t\t49087C13295DF0490058075B /* hook.h */,\n\t\t\t\t49087C26295DF1350058075B /* mach_exc.defs */,\n\t\t\t\tBBCED895255BB65A00CA0701 /* NSObject+SaneKVO.h */,\n\t\t\t\tBBCED896255BB65A00CA0701 /* NSObject+SaneKVO.m */,\n\t\t\t\tBB149E7E256DC97C00F57815 /* PassthroughView.h */,\n\t\t\t\tBB149E7F256DC97C00F57815 /* PassthroughView.m */,\n\t\t\t\tBBFB557A215878C600DFE6DE /* UIApplication+OpenURL.h */,\n\t\t\t\tBBFB557B215878C600DFE6DE /* UIApplication+OpenURL.m */,\n\t\t\t\tBBFB556F21586C4800DFE6DE /* UIViewController+Extras.h */,\n\t\t\t\tBBFB557021586C4800DFE6DE /* UIViewController+Extras.m */,\n\t\t\t);\n\t\t\tname = Utilities;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBFB557321586C7600DFE6DE /* About */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBBBCE7E521D2F02200CA00B3 /* About.storyboard */,\n\t\t\t\t9A28E4E8219A8B670073D200 /* AboutAppearanceViewController.h */,\n\t\t\t\t9A28E4E9219A8B670073D200 /* AboutAppearanceViewController.m */,\n\t\t\t\tBB82A7FB21B4C2E8006AA5FD /* AboutExternalKeyboardViewController.h */,\n\t\t\t\tBB82A7FC21B4C2E8006AA5FD /* AboutExternalKeyboardViewController.m */,\n\t\t\t\tBB1D9D91234A8FE100F364E8 /* AboutNavigationController.h */,\n\t\t\t\tBB1D9D92234A8FE100F364E8 /* AboutNavigationController.m */,\n\t\t\t\tBBFB5577215876CD00DFE6DE /* AboutViewController.h */,\n\t\t\t\tBBFB5578215876CD00DFE6DE /* AboutViewController.m */,\n\t\t\t\tBB267FA423A48F1500ED7CAF /* AltIconViewController.h */,\n\t\t\t\tBB267FA523A48F1500ED7CAF /* AltIconViewController.m */,\n\t\t\t\tBB101B362364CF57000A93BC /* FontPickerViewController.h */,\n\t\t\t\tBB101B372364CF57000A93BC /* FontPickerViewController.m */,\n\t\t\t\t491C4C1D27C8FB65008B1DFA /* Theme.h */,\n\t\t\t\t491C4C1E27C8FB65008B1DFA /* Theme.m */,\n\t\t\t\t491C4C1527C8F84A008B1DFA /* ThemesViewController.h */,\n\t\t\t\t491C4C1627C8F84A008B1DFA /* ThemesViewController.m */,\n\t\t\t\t491B31E42883BF22008EEFB0 /* ThemeViewController.h */,\n\t\t\t\t491B31E52883BF22008EEFB0 /* ThemeViewController.m */,\n\t\t\t\tBBA5D87C27536E7600B39D77 /* UpgradeRootViewController.h */,\n\t\t\t\tBBA5D87D27536E7600B39D77 /* UpgradeRootViewController.m */,\n\t\t\t\t8632A7BD219A59FB00F02325 /* UserPreferences.h */,\n\t\t\t\t8632A7BE219A59FB00F02325 /* UserPreferences.m */,\n\t\t\t);\n\t\t\tname = About;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBFB557D21587B2B00DFE6DE /* Bar */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBB60F55021573FCA003A4E52 /* BarButton.h */,\n\t\t\t\tBB60F55121573FCA003A4E52 /* BarButton.m */,\n\t\t\t\tBBFB557E21587B6800DFE6DE /* ArrowBarButton.h */,\n\t\t\t\tBBFB557F21587B6800DFE6DE /* ArrowBarButton.m */,\n\t\t\t);\n\t\t\tname = Bar;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXLegacyTarget section */\n\t\tBB13F7CA200ACC31003D1C4D /* Meson */ = {\n\t\t\tisa = PBXLegacyTarget;\n\t\t\tbuildArgumentsString = \"\";\n\t\t\tbuildConfigurationList = BB13F7CB200ACC31003D1C4D /* Build configuration list for PBXLegacyTarget \"Meson\" */;\n\t\t\tbuildPhases = (\n\t\t\t);\n\t\t\tbuildToolPath = \"$(SRCROOT)/app/xcode-meson.sh\";\n\t\t\tbuildWorkingDirectory = \"\";\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Meson;\n\t\t\tpassBuildSettingsInEnvironment = 1;\n\t\t\tproductName = Meson;\n\t\t};\n\t\tBB13F7D0200ACCA2003D1C4D /* Ninja */ = {\n\t\t\tisa = PBXLegacyTarget;\n\t\t\tbuildArgumentsString = \"$(NINJA_TARGETS)\";\n\t\t\tbuildConfigurationList = BB13F7D1200ACCA2003D1C4D /* Build configuration list for PBXLegacyTarget \"Ninja\" */;\n\t\t\tbuildPhases = (\n\t\t\t);\n\t\t\tbuildToolPath = \"$(SRCROOT)/app/xcode-ninja.sh\";\n\t\t\tbuildWorkingDirectory = \"$(MESON_BUILD_DIR)\";\n\t\t\tdependencies = (\n\t\t\t\tBB13F7D5200ACCA8003D1C4D /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Ninja;\n\t\t\tpassBuildSettingsInEnvironment = 1;\n\t\t\tproductName = Ninja;\n\t\t};\n/* End PBXLegacyTarget section */\n\n/* Begin PBXNativeTarget section */\n\t\t497F6CE3254E5E4C00C82F46 /* MakeXcodeAutoCompleteWork */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 497F6CEA254E5E4C00C82F46 /* Build configuration list for PBXNativeTarget \"MakeXcodeAutoCompleteWork\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t497F6CE0254E5E4C00C82F46 /* Sources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MakeXcodeAutoCompleteWork;\n\t\t\tproductName = MakeXcodeAutoCompleteWork;\n\t\t\tproductReference = 497F6CE4254E5E4C00C82F46 /* MakeXcodeAutoCompleteWork */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n\t\t497F6D46254E605F00C82F46 /* ish */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 497F6D4B254E605F00C82F46 /* Build configuration list for PBXNativeTarget \"ish\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t497F6D43254E605F00C82F46 /* Sources */,\n\t\t\t\t497F6D44254E605F00C82F46 /* Frameworks */,\n\t\t\t\t497F6D45254E605F00C82F46 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t497F6D5B254E609000C82F46 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = ish;\n\t\t\tproductName = ish;\n\t\t\tproductReference = 497F6D47254E605F00C82F46 /* ish */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n\t\tBB13F7DB200AD81D003D1C4D /* libish */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB13F7E2200AD81E003D1C4D /* Build configuration list for PBXNativeTarget \"libish\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB13F813200AEFAC003D1C4D /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBB13F7E9200AD967003D1C4D /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = libish;\n\t\t\tproductName = libish;\n\t\t\tproductReference = BB13F7DC200AD81D003D1C4D /* libish.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBB21A1662689041500BD19B4 /* libfakefs */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB21A16A2689041500BD19B4 /* Build configuration list for PBXNativeTarget \"libfakefs\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB21A1692689041500BD19B4 /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBB21A1672689041500BD19B4 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = libfakefs;\n\t\t\tproductName = libish;\n\t\t\tproductReference = BB21A16F2689041500BD19B4 /* libfakefs.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBB41591B255EF9E300E0950C /* iSHUITests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB415925255EF9E300E0950C /* Build configuration list for PBXNativeTarget \"iSHUITests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB415918255EF9E300E0950C /* Sources */,\n\t\t\t\tBB415919255EF9E300E0950C /* Frameworks */,\n\t\t\t\tBB41591A255EF9E300E0950C /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBB415922255EF9E300E0950C /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = iSHUITests;\n\t\t\tproductName = iSHUITests;\n\t\t\tproductReference = BB41591C255EF9E300E0950C /* iSHUITests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.ui-testing\";\n\t\t};\n\t\tBB792B4F1F96D90D00FFB7A4 /* iSH */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB792B661F96D90D00FFB7A4 /* Build configuration list for PBXNativeTarget \"iSH\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB21A17826890A5B00BD19B4 /* Sources */,\n\t\t\t\tBB792B4D1F96D90D00FFB7A4 /* Frameworks */,\n\t\t\t\tBBF1248B1FA7BF530088FB50 /* Download Root */,\n\t\t\t\tBB034E8C263643C10000A498 /* Generate APK Repositories File */,\n\t\t\t\tBB4A53AC1FAA49CA00A72ACE /* Compile JavaScript */,\n\t\t\t\tBB792B4E1F96D90D00FFB7A4 /* Resources */,\n\t\t\t\tBB88F4A32154760800A341FD /* Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBB4A922824ED940C002F5A96 /* PBXTargetDependency */,\n\t\t\t\tBBEF1929268063FD001225BD /* PBXTargetDependency */,\n\t\t\t\tBB10E5CB248DBAB7009C7A74 /* PBXTargetDependency */,\n\t\t\t\tBB13F7E7200AD874003D1C4D /* PBXTargetDependency */,\n\t\t\t\tBB88F49E2154760800A341FD /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = iSH;\n\t\t\tproductName = iSH;\n\t\t\tproductReference = BB792B501F96D90D00FFB7A4 /* iSH.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tBB88F48F2154760800A341FD /* iSHFileProvider */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BB88F4A22154760800A341FD /* Build configuration list for PBXNativeTarget \"iSHFileProvider\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB88F48C2154760800A341FD /* Sources */,\n\t\t\t\tBB88F48D2154760800A341FD /* Frameworks */,\n\t\t\t\tBB88F48E2154760800A341FD /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = iSHFileProvider;\n\t\t\tproductName = iSHFiles;\n\t\t\tproductReference = BB88F4902154760800A341FD /* iSHFileProvider.appex */;\n\t\t\tproductType = \"com.apple.product-type.app-extension\";\n\t\t};\n\t\tBBBDDF802CE00F6A0071F1F3 /* libiSHLinuxUser */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBBDDF8A2CE00F6A0071F1F3 /* Build configuration list for PBXNativeTarget \"libiSHLinuxUser\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBBBDDF832CE00F6A0071F1F3 /* Sources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBBBDDF812CE00F6A0071F1F3 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = libiSHLinuxUser;\n\t\t\tproductName = iSHLinux;\n\t\t\tproductReference = BBBDDF8F2CE00F6B0071F1F3 /* libiSHLinuxUser.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBBECF3A12691314C00DEC937 /* libiSHLinux */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBECF3A82691314C00DEC937 /* Build configuration list for PBXNativeTarget \"libiSHLinux\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBBECF39E2691314C00DEC937 /* Sources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBBECF3BE2691417C00DEC937 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = libiSHLinux;\n\t\t\tproductName = iSHLinux;\n\t\t\tproductReference = BBECF3A22691314C00DEC937 /* libiSHLinux.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBBECF3AF269136E100DEC937 /* liblinux */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBECF3B3269136E100DEC937 /* Build configuration list for PBXNativeTarget \"liblinux\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBBECF3B2269136E100DEC937 /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBBECF3B0269136E100DEC937 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = liblinux;\n\t\t\tproductName = libish;\n\t\t\tproductReference = BBECF3B8269136E100DEC937 /* liblinux.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBBEF191D26806364001225BD /* libiSHApp */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBEF192626806364001225BD /* Build configuration list for PBXNativeTarget \"libiSHApp\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBBEF191A26806364001225BD /* Sources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t\t497A08BE29633FC100B323CF /* PBXBuildRule */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = libiSHApp;\n\t\t\tproductName = libiSH;\n\t\t\tproductReference = BBEF191E26806364001225BD /* libiSHApp.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBBEF1964268066D1001225BD /* iSH+Linux */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBEF1999268066D1001225BD /* Build configuration list for PBXNativeTarget \"iSH+Linux\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBB21A1722689084800BD19B4 /* Sources */,\n\t\t\t\tBBEF196F268066D1001225BD /* Frameworks */,\n\t\t\t\tBBEF1979268066D1001225BD /* Download Root */,\n\t\t\t\tBBEF197A268066D1001225BD /* Generate APK Repositories File */,\n\t\t\t\tBBEF197B268066D1001225BD /* Compile JavaScript */,\n\t\t\t\tBBEF197C268066D1001225BD /* Resources */,\n\t\t\t\tBBEF1997268066D1001225BD /* Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBBEF1965268066D1001225BD /* PBXTargetDependency */,\n\t\t\t\tBBEF1967268066D1001225BD /* PBXTargetDependency */,\n\t\t\t\tBBEF1969268066D1001225BD /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = \"iSH+Linux\";\n\t\t\tproductName = iSH;\n\t\t\tproductReference = BBEF199C268066D1001225BD /* iSH.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tBBFB2C542590257E00545EAB /* libish_emu */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BBFB2C582590257E00545EAB /* Build configuration list for PBXNativeTarget \"libish_emu\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBBFB2C572590257E00545EAB /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBBFB2C552590257E00545EAB /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = libish_emu;\n\t\t\tproductName = libish;\n\t\t\tproductReference = BBFB2C5B2590257E00545EAB /* libish_emu.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tBB792B461F96D8E000FFB7A4 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tCLASSPREFIX = \"\";\n\t\t\t\tLastUpgradeCheck = 1410;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t497F6CE3254E5E4C00C82F46 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.2;\n\t\t\t\t\t};\n\t\t\t\t\t497F6D46254E605F00C82F46 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tBB13F7CA200ACC31003D1C4D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tBB13F7D0200ACCA2003D1C4D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tBB13F7DB200AD81D003D1C4D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tBB41591B255EF9E300E0950C = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 12.2;\n\t\t\t\t\t\tLastSwiftMigration = 1220;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tTestTargetID = BB792B4F1F96D90D00FFB7A4;\n\t\t\t\t\t};\n\t\t\t\t\tBB4A922324ED9402002F5A96 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.5;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tBB792B4F1F96D90D00FFB7A4 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.0;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.ApplicationGroups.iOS = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\tBB88F48F2154760800A341FD = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.0;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.ApplicationGroups.iOS = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\tBBECF3A12691314C00DEC937 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.0;\n\t\t\t\t\t};\n\t\t\t\t\tBBEF191D26806364001225BD = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.0;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = BB792B491F96D8E000FFB7A4 /* Build configuration list for PBXProject \"iSH\" */;\n\t\t\tcompatibilityVersion = \"Xcode 6.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = BB792B451F96D8E000FFB7A4;\n\t\t\tproductRefGroup = BB792B511F96D90D00FFB7A4 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = BB10E5C4248DBAA1009C7A74 /* Products */;\n\t\t\t\t\tProjectRef = BB10E0C1248DA5B0009C7A74 /* libarchive.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tBB792B4F1F96D90D00FFB7A4 /* iSH */,\n\t\t\t\tBBEF191D26806364001225BD /* libiSHApp */,\n\t\t\t\tBBEF1964268066D1001225BD /* iSH+Linux */,\n\t\t\t\tBBECF3A12691314C00DEC937 /* libiSHLinux */,\n\t\t\t\tBBBDDF802CE00F6A0071F1F3 /* libiSHLinuxUser */,\n\t\t\t\tBB88F48F2154760800A341FD /* iSHFileProvider */,\n\t\t\t\tBB41591B255EF9E300E0950C /* iSHUITests */,\n\t\t\t\tBB13F7CA200ACC31003D1C4D /* Meson */,\n\t\t\t\tBB13F7D0200ACCA2003D1C4D /* Ninja */,\n\t\t\t\tBB13F7DB200AD81D003D1C4D /* libish */,\n\t\t\t\tBB21A1662689041500BD19B4 /* libfakefs */,\n\t\t\t\tBBFB2C542590257E00545EAB /* libish_emu */,\n\t\t\t\tBBECF3AF269136E100DEC937 /* liblinux */,\n\t\t\t\tBB4A922324ED9402002F5A96 /* iSH pre */,\n\t\t\t\t497F6CE3254E5E4C00C82F46 /* MakeXcodeAutoCompleteWork */,\n\t\t\t\t497F6D46254E605F00C82F46 /* ish */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\tBB10E5C8248DBAA1009C7A74 /* libarchive.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libarchive.a;\n\t\t\tremoteRef = BB10E5C7248DBAA1009C7A74 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tBB41591A255EF9E300E0950C /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB792B4E1F96D90D00FFB7A4 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB38599627BCEE6B000A1082 /* idollarhash.png in Resources */,\n\t\t\t\tBB3859A227BCEE6B000A1082 /* dollarblock2.png in Resources */,\n\t\t\t\tBB3859A727BCEE6B000A1082 /* uninspired.png in Resources */,\n\t\t\t\tBB38599B27BCEE6B000A1082 /* rgb.png in Resources */,\n\t\t\t\tBB38599927BCEE6B000A1082 /* dollarblock1.png in Resources */,\n\t\t\t\tBB38599C27BCEE6B000A1082 /* sprite64.png in Resources */,\n\t\t\t\tBB792B5D1F96D90D00FFB7A4 /* Assets.xcassets in Resources */,\n\t\t\t\tBB792B601F96D90D00FFB7A4 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tBB3859A427BCEE6B000A1082 /* ishcolontildehash.png in Resources */,\n\t\t\t\tBB38599D27BCEE6B000A1082 /* icon.png in Resources */,\n\t\t\t\tBBBF7B5C2415CDBB00EC4C14 /* Settings.bundle in Resources */,\n\t\t\t\tBB3859A527BCEE6B000A1082 /* reworked.png in Resources */,\n\t\t\t\tBBBCE7E321D2F02200CA00B3 /* About.storyboard in Resources */,\n\t\t\t\tBB3859A627BCEE6B000A1082 /* colontildehash.png in Resources */,\n\t\t\t\tBB38599327BCEE6B000A1082 /* notsurewhatthisis.png in Resources */,\n\t\t\t\tBB792B5B1F96D90D00FFB7A4 /* Terminal.storyboard in Resources */,\n\t\t\t\tBB38599427BCEE6B000A1082 /* pydann1.png in Resources */,\n\t\t\t\tBB0F552E239F8A790032A2A1 /* Icons.plist in Resources */,\n\t\t\t\tBB38599527BCEE6B000A1082 /* pydann2.png in Resources */,\n\t\t\t\tBB2B4DAF231D998300CB578B /* term.html in Resources */,\n\t\t\t\tBB3859A027BCEE6B000A1082 /* 3d.png in Resources */,\n\t\t\t\tBB10E5D0248DC21D009C7A74 /* Roots.storyboard in Resources */,\n\t\t\t\tBB2B4DAC231D94C300CB578B /* hterm_all.js in Resources */,\n\t\t\t\tBB3859A327BCEE6B000A1082 /* ihash1.png in Resources */,\n\t\t\t\tBB2B4DAE231D998300CB578B /* term.css in Resources */,\n\t\t\t\tBB38599827BCEE6B000A1082 /* metal.png in Resources */,\n\t\t\t\tBB2B4DAD231D998300CB578B /* term.js in Resources */,\n\t\t\t\tBB38599A27BCEE6B000A1082 /* is.png in Resources */,\n\t\t\t\tBB38599E27BCEE6B000A1082 /* circular.png in Resources */,\n\t\t\t\tBB38599F27BCEE6B000A1082 /* icon1337.png in Resources */,\n\t\t\t\tBB3859A127BCEE6B000A1082 /* iinhash.png in Resources */,\n\t\t\t\tBB38599727BCEE6B000A1082 /* freeiosterminal.png in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB88F48E2154760800A341FD /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBEF197C268066D1001225BD /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBEF197D268066D1001225BD /* 3d.png in Resources */,\n\t\t\t\tBBEF197E268066D1001225BD /* colontildehash.png in Resources */,\n\t\t\t\tBBEF197F268066D1001225BD /* metal.png in Resources */,\n\t\t\t\tBBEF1980268066D1001225BD /* Assets.xcassets in Resources */,\n\t\t\t\tBBEF1981268066D1001225BD /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tBB38597D27BCEC78000A1082 /* dollarblock1.png in Resources */,\n\t\t\t\tBBEF1982268066D1001225BD /* Settings.bundle in Resources */,\n\t\t\t\tBBEF1983268066D1001225BD /* pydann1.png in Resources */,\n\t\t\t\tBBEF1984268066D1001225BD /* About.storyboard in Resources */,\n\t\t\t\tBBEF1985268066D1001225BD /* Terminal.storyboard in Resources */,\n\t\t\t\tBBEF1986268066D1001225BD /* Icons.plist in Resources */,\n\t\t\t\tBBEF1987268066D1001225BD /* uninspired.png in Resources */,\n\t\t\t\tBB38598527BCEC84000A1082 /* ishcolontildehash.png in Resources */,\n\t\t\t\tBBEF1988268066D1001225BD /* iinhash.png in Resources */,\n\t\t\t\tBBEF1989268066D1001225BD /* pydann2.png in Resources */,\n\t\t\t\tBBEF198A268066D1001225BD /* ihash1.png in Resources */,\n\t\t\t\tBBEF198B268066D1001225BD /* term.html in Resources */,\n\t\t\t\tBBEF198C268066D1001225BD /* notsurewhatthisis.png in Resources */,\n\t\t\t\tBBEF198D268066D1001225BD /* reworked.png in Resources */,\n\t\t\t\tBBEF198E268066D1001225BD /* Roots.storyboard in Resources */,\n\t\t\t\tBBEF198F268066D1001225BD /* idollarhash.png in Resources */,\n\t\t\t\tBB3859A827BCEE6C000A1082 /* is.png in Resources */,\n\t\t\t\tBB38597F27BCEC78000A1082 /* dollarblock2.png in Resources */,\n\t\t\t\tBBEF1990268066D1001225BD /* hterm_all.js in Resources */,\n\t\t\t\tBB38597827BCEC70000A1082 /* circular.png in Resources */,\n\t\t\t\tBBEF1991268066D1001225BD /* icon1337.png in Resources */,\n\t\t\t\tBBEF1992268066D1001225BD /* rgb.png in Resources */,\n\t\t\t\tBBEF1993268066D1001225BD /* term.css in Resources */,\n\t\t\t\tBBEF1994268066D1001225BD /* term.js in Resources */,\n\t\t\t\tBBEF1995268066D1001225BD /* sprite64.png in Resources */,\n\t\t\t\tBBEF1996268066D1001225BD /* icon.png in Resources */,\n\t\t\t\tBB38598127BCEC78000A1082 /* freeiosterminal.png in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tBB034E8C263643C10000A498 /* Generate APK Repositories File */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(SRCROOT)/deps/aports/main/x86/index.txt\",\n\t\t\t\t\"$(SRCROOT)/deps/aports/community/x86/index.txt\",\n\t\t\t\t\"$(SRCROOT)/app/gen_apk_repositories.py\",\n\t\t\t);\n\t\t\tname = \"Generate APK Repositories File\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/repositories.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = \"/bin/sh -e\";\n\t\t\tshellScript = \"python3 $SRCROOT/app/gen_apk_repositories.py\\n\";\n\t\t};\n\t\tBB13F813200AEFAC003D1C4D /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(MESON_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"mkdir -p \\\"$CONFIGURATION_BUILD_DIR\\\"\\nln -sf \\\"$MESON_BUILD_DIR/$PRODUCT_NAME.a\\\" \\\"$CONFIGURATION_BUILD_DIR\\\"\\n\";\n\t\t};\n\t\tBB21A1692689041500BD19B4 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(MESON_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"mkdir -p \\\"$CONFIGURATION_BUILD_DIR\\\"\\nln -sf \\\"$MESON_BUILD_DIR/$PRODUCT_NAME.a\\\" \\\"$CONFIGURATION_BUILD_DIR\\\"\\n\";\n\t\t};\n\t\tBB4A53AC1FAA49CA00A72ACE /* Compile JavaScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Compile JavaScript\";\n\t\t\toutputPaths = (\n\t\t\t\t\"$(SRCROOT)/deps/hterm/dist/js/hterm_all.js\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = \"/bin/sh -e\";\n\t\t\tshellScript = \"cd $SRCROOT/deps/libapps\\n./hterm/bin/mkdist\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tBB4A922A24ED9421002F5A96 /* Add icons to Info.plist */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(SRCROOT)/app/Icons/Icons.plist\",\n\t\t\t);\n\t\t\tname = \"Add icons to Info.plist\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/infoplisticons.h\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"python3 - \\\"${SRCROOT}/app/Icons/Icons.plist\\\" \\\"${BUILT_PRODUCTS_DIR}/infoplisticons.h\\\" <<END\\nimport plistlib\\nimport sys\\nprint(sys.argv)\\nwith open(sys.argv[1], 'rb') as plist:\\n    icons = plistlib.load(plist)\\nalt_icons = '<key>CFBundleAlternateIcons</key><dict>'\\nfor icon in icons.keys():\\n    if icon == '': continue\\n    alt_icons += '''<key>{0}</key>\\n<dict>\\n<key>CFBundleIconFiles</key>\\n<array><string>{0}</string></array>\\n</dict>'''.format(icon)\\nalt_icons += '</dict>'\\nicon_stuff = '''</string>\\n<key>CFBundleIcons</key><dict>{0}</dict>\\n<key>CFBundleIcons~ipad</key><dict>{0}</dict>\\n<key>kxcode</key><string>'''.format(alt_icons)\\nicon_define = '#define ICON_STUFF ' + icon_stuff.replace('\\\\n', '')\\nwith open(sys.argv[2], 'w') as output:\\n    output.write(icon_define)\\nEND\\ntouch \\\"${SRCROOT}/app/Info.plist\\\"\\n\";\n\t\t};\n\t\tBBECF3B2269136E100DEC937 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(MESON_BUILD_DIR)/deps/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"mkdir -p \\\"$CONFIGURATION_BUILD_DIR\\\"\\nln -sf \\\"$MESON_BUILD_DIR/deps/$PRODUCT_NAME.a\\\" \\\"$CONFIGURATION_BUILD_DIR\\\"\\n\";\n\t\t};\n\t\tBBEF1979268066D1001225BD /* Download Root */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download Root\";\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/root.tar.gz\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"curl -L \\\"https://$ROOTFS_URL\\\" -o \\\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/root.tar.gz\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tBBEF197A268066D1001225BD /* Generate APK Repositories File */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(SRCROOT)/deps/aports/main/x86/index.txt\",\n\t\t\t\t\"$(SRCROOT)/deps/aports/community/x86/index.txt\",\n\t\t\t\t\"$(SRCROOT)/app/gen_apk_repositories.py\",\n\t\t\t);\n\t\t\tname = \"Generate APK Repositories File\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/repositories.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = \"/bin/sh -e\";\n\t\t\tshellScript = \"python3 $SRCROOT/app/gen_apk_repositories.py\\n\";\n\t\t};\n\t\tBBEF197B268066D1001225BD /* Compile JavaScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Compile JavaScript\";\n\t\t\toutputPaths = (\n\t\t\t\t\"$(SRCROOT)/deps/hterm/dist/js/hterm_all.js\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = \"/bin/sh -e\";\n\t\t\tshellScript = \"cd $SRCROOT/deps/libapps\\n./hterm/bin/mkdist\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tBBF1248B1FA7BF530088FB50 /* Download Root */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download Root\";\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/root.tar.gz\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"curl -L \\\"https://$ROOTFS_URL\\\" -o \\\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/root.tar.gz\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tBBFB2C572590257E00545EAB /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(MESON_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(CONFIGURATION_BUILD_DIR)/$(PRODUCT_NAME).a\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"mkdir -p \\\"$CONFIGURATION_BUILD_DIR\\\"\\nln -sf \\\"$MESON_BUILD_DIR/$PRODUCT_NAME.a\\\" \\\"$CONFIGURATION_BUILD_DIR\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t497F6CE0254E5E4C00C82F46 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t497F6CF5254E5EA500C82F46 /* float80-test.c in Sources */,\n\t\t\t\t497F6CF6254E5EA500C82F46 /* float80.c in Sources */,\n\t\t\t\t497F6CF7254E5EA500C82F46 /* fpu.c in Sources */,\n\t\t\t\t497F6CF9254E5EA500C82F46 /* memory.c in Sources */,\n\t\t\t\t497F6CFA254E5EA500C82F46 /* tlb.c in Sources */,\n\t\t\t\t497F6CFB254E5EA500C82F46 /* vec.c in Sources */,\n\t\t\t\t497F6CFC254E5EA500C82F46 /* adhoc.c in Sources */,\n\t\t\t\t497F6CFD254E5EA500C82F46 /* dev.c in Sources */,\n\t\t\t\t497F6CFE254E5EA500C82F46 /* dir.c in Sources */,\n\t\t\t\t497F6CFF254E5EA500C82F46 /* dyndev.c in Sources */,\n\t\t\t\t497F6D00254E5EA500C82F46 /* fake-migrate.c in Sources */,\n\t\t\t\t497F6D01254E5EA500C82F46 /* fake-rebuild.c in Sources */,\n\t\t\t\t497F6D02254E5EA500C82F46 /* fake.c in Sources */,\n\t\t\t\t497F6D03254E5EA500C82F46 /* fd.c in Sources */,\n\t\t\t\t497F6D04254E5EA500C82F46 /* generic.c in Sources */,\n\t\t\t\t497F6D05254E5EA600C82F46 /* inode.c in Sources */,\n\t\t\t\t497F6D06254E5EA600C82F46 /* lock.c in Sources */,\n\t\t\t\t497F6D07254E5EA600C82F46 /* mem.c in Sources */,\n\t\t\t\t497F6D08254E5EA600C82F46 /* mount.c in Sources */,\n\t\t\t\t497F6D09254E5EA600C82F46 /* path.c in Sources */,\n\t\t\t\t497F6D0A254E5EA600C82F46 /* pipe.c in Sources */,\n\t\t\t\t497F6D0B254E5EA600C82F46 /* poll.c in Sources */,\n\t\t\t\t497F6D0C254E5EA600C82F46 /* pid.c in Sources */,\n\t\t\t\t497F6D0D254E5EA600C82F46 /* root.c in Sources */,\n\t\t\t\t497F6D0E254E5EA600C82F46 /* entry.c in Sources */,\n\t\t\t\t497F6D0F254E5EA600C82F46 /* proc.c in Sources */,\n\t\t\t\t497F6D10254E5EA600C82F46 /* pty.c in Sources */,\n\t\t\t\t497F6D11254E5EA600C82F46 /* real.c in Sources */,\n\t\t\t\t497F6D12254E5EA600C82F46 /* sock.c in Sources */,\n\t\t\t\t497F6D13254E5EA600C82F46 /* sockrestart.c in Sources */,\n\t\t\t\t497F6D14254E5EA600C82F46 /* stat.c in Sources */,\n\t\t\t\t497F6D15254E5EA600C82F46 /* tmp.c in Sources */,\n\t\t\t\t497F6D16254E5EA600C82F46 /* tty-real.c in Sources */,\n\t\t\t\t497F6D17254E5EA600C82F46 /* tty.c in Sources */,\n\t\t\t\t497F6D18254E5EA600C82F46 /* gen.c in Sources */,\n\t\t\t\t497F6D19254E5EA600C82F46 /* helpers.c in Sources */,\n\t\t\t\t497F6D1A254E5EA600C82F46 /* asbestos.c in Sources */,\n\t\t\t\t497F6D1B254E5EA600C82F46 /* offsets.c in Sources */,\n\t\t\t\t497F6D1C254E5EA600C82F46 /* calls.c in Sources */,\n\t\t\t\t497F6D1D254E5EA600C82F46 /* epoll.c in Sources */,\n\t\t\t\t497F6D1E254E5EA600C82F46 /* errno.c in Sources */,\n\t\t\t\t497F6D1F254E5EA600C82F46 /* eventfd.c in Sources */,\n\t\t\t\t497F6D20254E5EA600C82F46 /* exec.c in Sources */,\n\t\t\t\t497F6D21254E5EA600C82F46 /* exit.c in Sources */,\n\t\t\t\t497F6D22254E5EA600C82F46 /* fork.c in Sources */,\n\t\t\t\t497F6D23254E5EA600C82F46 /* fs_info.c in Sources */,\n\t\t\t\t497F6D24254E5EA600C82F46 /* fs.c in Sources */,\n\t\t\t\t497F6D25254E5EA600C82F46 /* futex.c in Sources */,\n\t\t\t\t497F6D26254E5EA600C82F46 /* getset.c in Sources */,\n\t\t\t\t497F6D27254E5EA600C82F46 /* group.c in Sources */,\n\t\t\t\t0FC2383C2991FBA6004C09EC /* mmx.c in Sources */,\n\t\t\t\t497F6D28254E5EA600C82F46 /* init.c in Sources */,\n\t\t\t\t497F6D29254E5EA600C82F46 /* ipc.c in Sources */,\n\t\t\t\t497F6D2A254E5EA600C82F46 /* log.c in Sources */,\n\t\t\t\t497F6D2B254E5EA600C82F46 /* misc.c in Sources */,\n\t\t\t\t497F6D2C254E5EA600C82F46 /* mmap.c in Sources */,\n\t\t\t\t497F6D2D254E5EA600C82F46 /* poll.c in Sources */,\n\t\t\t\t497F6D2E254E5EA600C82F46 /* ptrace.c in Sources */,\n\t\t\t\t497F6D2F254E5EA600C82F46 /* random.c in Sources */,\n\t\t\t\t497F6D30254E5EA600C82F46 /* resource.c in Sources */,\n\t\t\t\t497F6D31254E5EA600C82F46 /* signal.c in Sources */,\n\t\t\t\t497F6D32254E5EA600C82F46 /* task.c in Sources */,\n\t\t\t\t497F6D33254E5EA600C82F46 /* time.c in Sources */,\n\t\t\t\t497F6D34254E5EA600C82F46 /* tls.c in Sources */,\n\t\t\t\t497F6D35254E5EA600C82F46 /* uname.c in Sources */,\n\t\t\t\t497F6D36254E5EA600C82F46 /* user.c in Sources */,\n\t\t\t\t497F6D37254E5EA600C82F46 /* vdso.c in Sources */,\n\t\t\t\t497F6D38254E5EA600C82F46 /* darwin.c in Sources */,\n\t\t\t\t49302D8E277669D300C9885A /* ish.c in Sources */,\n\t\t\t\t497F6D39254E5EA600C82F46 /* linux.c in Sources */,\n\t\t\t\t497F6D3A254E5EA600C82F46 /* fifo.c in Sources */,\n\t\t\t\t497F6D3B254E5EA600C82F46 /* sync.c in Sources */,\n\t\t\t\t497F6D3C254E5EA600C82F46 /* timer.c in Sources */,\n\t\t\t\t497F6D3D254E5EA600C82F46 /* main.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t497F6D43254E605F00C82F46 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t497F6D5C254E609700C82F46 /* main.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB21A1722689084800BD19B4 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBF06F6C2CC4C134009F5DB5 /* fchdir.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB21A17826890A5B00BD19B4 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB28C7BB268975AE00BDC834 /* LocationDevice.m in Sources */,\n\t\t\t\tBB28C7BA268975AA00BDC834 /* iOSFS.m in Sources */,\n\t\t\t\tBB28C7BC268975B000BDC834 /* PasteboardDevice.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB415918255EF9E300E0950C /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB41591F255EF9E300E0950C /* UITests.m in Sources */,\n\t\t\t\tBBD23D5F258DA450003DCB40 /* Screenshots.m in Sources */,\n\t\t\t\tBBD23D77258DA4BC003DCB40 /* SnapshotHelper.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBB88F48C2154760800A341FD /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB88F49A2154760800A341FD /* FileProviderEnumerator.m in Sources */,\n\t\t\t\tBBBFE94921C5CFF100509DD5 /* NSError+ISHErrno.m in Sources */,\n\t\t\t\tBB9C7B87240A2B1E00F5D4F0 /* AppGroup.m in Sources */,\n\t\t\t\tBB88F4942154760800A341FD /* FileProviderExtension.m in Sources */,\n\t\t\t\tBBE64AC22A198FCC00589372 /* ExceptionExfiltrator.m in Sources */,\n\t\t\t\tBB88F4972154760800A341FD /* FileProviderItem.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBBDDF832CE00F6A0071F1F3 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBBDDF922CE0122F0071F1F3 /* emu_asbestos.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBECF39E2691314C00DEC937 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBBECF3AD269132F400DEC937 /* LinuxInterop.c in Sources */,\n\t\t\t\tBBBF9CBD27C217B6002A30F7 /* PasteboardDeviceLinux.c in Sources */,\n\t\t\t\tBB8C3AFF26B7B8CF00E38DDC /* fakefs.c in Sources */,\n\t\t\t\tBBEEA9E8277D25090069495B /* LinuxRoot.c in Sources */,\n\t\t\t\tBB123ACC26C9EFD900419CDA /* LinuxTTY.c in Sources */,\n\t\t\t\tBBEEA9EA277DAB400069495B /* LinuxPTY.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBBEF191A26806364001225BD /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBB28C79226896B1F00BDC834 /* AboutAppearanceViewController.m in Sources */,\n\t\t\t\tBB28C79326896B1F00BDC834 /* AltIconViewController.m in Sources */,\n\t\t\t\t491B31E62883BF22008EEFB0 /* ThemeViewController.m in Sources */,\n\t\t\t\tBB28C7B226896C4600BDC834 /* main.m in Sources */,\n\t\t\t\t496C7AB227CC697E005D7613 /* Theme.m in Sources */,\n\t\t\t\tBB28C79426896B1F00BDC834 /* AboutNavigationController.m in Sources */,\n\t\t\t\tBB28C79526896B1F00BDC834 /* TerminalViewController.m in Sources */,\n\t\t\t\tBB123ACE26C9F13500419CDA /* IOSCalls.m in Sources */,\n\t\t\t\tBBC3863F276817AD00CC8C2E /* CurrentRoot.m in Sources */,\n\t\t\t\tBB28C79626896B1F00BDC834 /* ProgressReportViewController.m in Sources */,\n\t\t\t\tBB28C79726896B1F00BDC834 /* TerminalView.m in Sources */,\n\t\t\t\t497A08BF29633FF400B323CF /* mach_exc.defs in Sources */,\n\t\t\t\tBB28C79826896B1F00BDC834 /* ScrollbarView.m in Sources */,\n\t\t\t\tBB28C79926896B1F00BDC834 /* DelayedUITask.m in Sources */,\n\t\t\t\t497A08C029633FFA00B323CF /* hook.c in Sources */,\n\t\t\t\tBB28C7BF2689799800BDC834 /* Roots.m in Sources */,\n\t\t\t\tBB28C79A26896B1F00BDC834 /* Terminal.m in Sources */,\n\t\t\t\tBB28C79B26896B1F00BDC834 /* FontPickerViewController.m in Sources */,\n\t\t\t\tBBC3863E276817A900CC8C2E /* UpgradeRootViewController.m in Sources */,\n\t\t\t\tBB28C79D26896B1F00BDC834 /* SceneDelegate.m in Sources */,\n\t\t\t\tBB28C79E26896B1F00BDC834 /* AboutExternalKeyboardViewController.m in Sources */,\n\t\t\t\tBB28C79F26896B1F00BDC834 /* PassthroughView.m in Sources */,\n\t\t\t\tBB28C7A026896B1F00BDC834 /* UserPreferences.m in Sources */,\n\t\t\t\tBB28C7A126896B1F00BDC834 /* RootsTableViewController.m in Sources */,\n\t\t\t\tBB28C7A326896B1F00BDC834 /* BarButton.m in Sources */,\n\t\t\t\tBB28C7A426896B1F00BDC834 /* AppGroup.m in Sources */,\n\t\t\t\tBBECF3BC26913F1600DEC937 /* AppDelegate.m in Sources */,\n\t\t\t\tBB28C7A526896B1F00BDC834 /* UIApplication+OpenURL.m in Sources */,\n\t\t\t\t49FF844B2A06249F00850B7A /* ExceptionExfiltrator.m in Sources */,\n\t\t\t\t497A08C12963401100B323CF /* AccessibilityFixes.m in Sources */,\n\t\t\t\tBB28C7A626896B1F00BDC834 /* AboutViewController.m in Sources */,\n\t\t\t\tBB28C7A726896B1F00BDC834 /* UIViewController+Extras.m in Sources */,\n\t\t\t\tBB28C7A826896B1F00BDC834 /* NSObject+SaneKVO.m in Sources */,\n\t\t\t\t496C7AB327CC734D005D7613 /* ThemesViewController.m in Sources */,\n\t\t\t\tBB77636726E486A7008DB44A /* fakefs.c in Sources */,\n\t\t\t\tBB28C7B826896CE600BDC834 /* ArrowBarButton.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t497F6D5B254E609000C82F46 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7DB200AD81D003D1C4D /* libish */;\n\t\t\ttargetProxy = 497F6D5A254E609000C82F46 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB10E5CB248DBAB7009C7A74 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = libarchive;\n\t\t\ttargetProxy = BB10E5CA248DBAB7009C7A74 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB13F7D5200ACCA8003D1C4D /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7CA200ACC31003D1C4D /* Meson */;\n\t\t\ttargetProxy = BB13F7D4200ACCA8003D1C4D /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB13F7E7200AD874003D1C4D /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7DB200AD81D003D1C4D /* libish */;\n\t\t\ttargetProxy = BB13F7E6200AD874003D1C4D /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB13F7E9200AD967003D1C4D /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7D0200ACCA2003D1C4D /* Ninja */;\n\t\t\ttargetProxy = BB13F7E8200AD967003D1C4D /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB21A1672689041500BD19B4 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7D0200ACCA2003D1C4D /* Ninja */;\n\t\t\ttargetProxy = BB21A1682689041500BD19B4 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB415922255EF9E300E0950C /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB792B4F1F96D90D00FFB7A4 /* iSH */;\n\t\t\ttargetProxy = BB415921255EF9E300E0950C /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB4A922824ED940C002F5A96 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB4A922324ED9402002F5A96 /* iSH pre */;\n\t\t\ttargetProxy = BB4A922724ED940C002F5A96 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBB88F49E2154760800A341FD /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB88F48F2154760800A341FD /* iSHFileProvider */;\n\t\t\ttargetProxy = BB88F49D2154760800A341FD /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBBDDF812CE00F6A0071F1F3 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BBECF3AF269136E100DEC937 /* liblinux */;\n\t\t\ttargetProxy = BBBDDF822CE00F6A0071F1F3 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBECF3B0269136E100DEC937 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7D0200ACCA2003D1C4D /* Ninja */;\n\t\t\ttargetProxy = BBECF3B1269136E100DEC937 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBECF3BE2691417C00DEC937 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BBECF3AF269136E100DEC937 /* liblinux */;\n\t\t\ttargetProxy = BBECF3BD2691417C00DEC937 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBEF1929268063FD001225BD /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BBEF191D26806364001225BD /* libiSHApp */;\n\t\t\ttargetProxy = BBEF1928268063FD001225BD /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBEF1965268066D1001225BD /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB4A922324ED9402002F5A96 /* iSH pre */;\n\t\t\ttargetProxy = BBEF1966268066D1001225BD /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBEF1967268066D1001225BD /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BBEF191D26806364001225BD /* libiSHApp */;\n\t\t\ttargetProxy = BBEF1968268066D1001225BD /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBEF1969268066D1001225BD /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = libarchive;\n\t\t\ttargetProxy = BBEF196A268066D1001225BD /* PBXContainerItemProxy */;\n\t\t};\n\t\tBBFB2C552590257E00545EAB /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BB13F7D0200ACCA2003D1C4D /* Ninja */;\n\t\t\ttargetProxy = BBFB2C562590257E00545EAB /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tBB792B591F96D90D00FFB7A4 /* Terminal.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBB792B5A1F96D90D00FFB7A4 /* Base */,\n\t\t\t);\n\t\t\tname = Terminal.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBB792B5E1F96D90D00FFB7A4 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBB792B5F1F96D90D00FFB7A4 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBBBCE7E521D2F02200CA00B3 /* About.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBBBCE7E421D2F02200CA00B3 /* Base */,\n\t\t\t);\n\t\t\tname = About.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t497F6CE8254E5E4C00C82F46 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\t497F6CE9254E5E4C00C82F46 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t497F6D4C254E605F00C82F46 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 493B378F290F5320000C41ED /* CLI.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\t497F6D4D254E605F00C82F46 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 493B378F290F5320000C41ED /* CLI.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB13F7CC200ACC31003D1C4D /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB13F7CD200ACC31003D1C4D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB13F7D2200ACCA2003D1C4D /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB13F7D3200ACCA2003D1C4D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB13F7E3200AD81E003D1C4D /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB13F7E4200AD81E003D1C4D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB21A16B2689041500BD19B4 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB21A16C2689041500BD19B4 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB21A16D2689041500BD19B4 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB21A16E2689041500BD19B4 /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBB415923255EF9E300E0950C /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tINFOPLIST_FILE = app/UITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).UITests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_TARGET_NAME = iSH;\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB415924255EF9E300E0950C /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tINFOPLIST_FILE = app/UITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).UITests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_TARGET_NAME = iSH;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB4A922524ED9402002F5A96 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB4A922624ED9402002F5A96 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB792B4A1F96D8E000FFB7A4 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B85A42586F1CB00208600 /* ProjectDebug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCURRENT_PROJECT_VERSION = 806;\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB792B4B1F96D8E000FFB7A4 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B85A52586F1E100208600 /* ProjectRelease.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCURRENT_PROJECT_VERSION = 806;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB792B641F96D90D00FFB7A4 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB792B651F96D90D00FFB7A4 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBB7F62052688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB28C7532689522C00BDC834 /* ProjectDebugLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCURRENT_PROJECT_VERSION = 806;\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62062688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62072688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62082688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBEF192C26806546001225BD /* AppLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62092688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = app/FileProvider/iSHFileProvider.entitlements;\n\t\t\t\tINFOPLIST_FILE = app/FileProvider/Info.plist;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).FileProvider\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620A2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tINFOPLIST_FILE = app/UITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).UITests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_TARGET_NAME = iSH;\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620B2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620C2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620D2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620E2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F620F2688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62102688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB7F62112688EAB5003C0220 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 493B378F290F5320000C41ED /* CLI.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBB88F4A02154760800A341FD /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = app/FileProvider/iSHFileProvider.entitlements;\n\t\t\t\tINFOPLIST_FILE = app/FileProvider/Info.plist;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).FileProvider\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBB88F4A12154760800A341FD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = app/FileProvider/iSHFileProvider.entitlements;\n\t\t\t\tINFOPLIST_FILE = app/FileProvider/Info.plist;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).FileProvider\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBBDDF8B2CE00F6A0071F1F3 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBBDDF8C2CE00F6A0071F1F3 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBBBDDF8D2CE00F6A0071F1F3 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBBDDF8E2CE00F6A0071F1F3 /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBBDDF902CE00F8D0071F1F3 /* StaticLibLinuxUser.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBECF3A92691314C00DEC937 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBECF3AA2691314C00DEC937 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBBECF3AB2691314C00DEC937 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBECF3AC2691314C00DEC937 /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBECF3B4269136E100DEC937 /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBECF3B5269136E100DEC937 /* DebugLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = DebugLinux;\n\t\t};\n\t\tBBECF3B6269136E100DEC937 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBECF3B7269136E100DEC937 /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF192426806364001225BD /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBEF192C26806546001225BD /* AppLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBEF192526806364001225BD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBEF192C26806546001225BD /* AppLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBEF199A268066D1001225BD /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBEF199B268066D1001225BD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBBEF19AB26806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB28C7522689522700BDC834 /* ProjectReleaseLinux.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCURRENT_PROJECT_VERSION = 806;\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19AC26806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19AD26806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86CD2586FD8800208600 /* App.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19AE26806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBEF192C26806546001225BD /* AppLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19AF26806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = app/FileProvider/iSHFileProvider.entitlements;\n\t\t\t\tINFOPLIST_FILE = app/FileProvider/Info.plist;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).FileProvider\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B026806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tINFOPLIST_FILE = app/UITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(PRODUCT_BUNDLE_IDENTIFIER).UITests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_TARGET_NAME = iSH;\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B126806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B226806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B326806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B426806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B526806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B626806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BB0B86D52587009D00208600 /* iOS.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBEF19B726806D1F001225BD /* ReleaseLinux */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 493B378F290F5320000C41ED /* CLI.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = ReleaseLinux;\n\t\t};\n\t\tBBFB2C592590257E00545EAB /* Debug-ApplePleaseFixFB19282108 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = \"Debug-ApplePleaseFixFB19282108\";\n\t\t};\n\t\tBBFB2C5A2590257E00545EAB /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = BBFB2CDA259028DC00545EAB /* StaticLib.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t497F6CEA254E5E4C00C82F46 /* Build configuration list for PBXNativeTarget \"MakeXcodeAutoCompleteWork\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t497F6CE8254E5E4C00C82F46 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62102688EAB5003C0220 /* DebugLinux */,\n\t\t\t\t497F6CE9254E5E4C00C82F46 /* Release */,\n\t\t\t\tBBEF19B626806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t497F6D4B254E605F00C82F46 /* Build configuration list for PBXNativeTarget \"ish\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t497F6D4C254E605F00C82F46 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62112688EAB5003C0220 /* DebugLinux */,\n\t\t\t\t497F6D4D254E605F00C82F46 /* Release */,\n\t\t\t\tBBEF19B726806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB13F7CB200ACC31003D1C4D /* Build configuration list for PBXLegacyTarget \"Meson\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB13F7CC200ACC31003D1C4D /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620B2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB13F7CD200ACC31003D1C4D /* Release */,\n\t\t\t\tBBEF19B126806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB13F7D1200ACCA2003D1C4D /* Build configuration list for PBXLegacyTarget \"Ninja\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB13F7D2200ACCA2003D1C4D /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620C2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB13F7D3200ACCA2003D1C4D /* Release */,\n\t\t\t\tBBEF19B226806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB13F7E2200AD81E003D1C4D /* Build configuration list for PBXNativeTarget \"libish\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB13F7E3200AD81E003D1C4D /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620D2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB13F7E4200AD81E003D1C4D /* Release */,\n\t\t\t\tBBEF19B326806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB21A16A2689041500BD19B4 /* Build configuration list for PBXNativeTarget \"libfakefs\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB21A16B2689041500BD19B4 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB21A16C2689041500BD19B4 /* DebugLinux */,\n\t\t\t\tBB21A16D2689041500BD19B4 /* Release */,\n\t\t\t\tBB21A16E2689041500BD19B4 /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB415925255EF9E300E0950C /* Build configuration list for PBXNativeTarget \"iSHUITests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB415923255EF9E300E0950C /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620A2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB415924255EF9E300E0950C /* Release */,\n\t\t\t\tBBEF19B026806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB4A922424ED9402002F5A96 /* Build configuration list for PBXAggregateTarget \"iSH pre\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB4A922524ED9402002F5A96 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620F2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB4A922624ED9402002F5A96 /* Release */,\n\t\t\t\tBBEF19B526806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB792B491F96D8E000FFB7A4 /* Build configuration list for PBXProject \"iSH\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB792B4A1F96D8E000FFB7A4 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62052688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB792B4B1F96D8E000FFB7A4 /* Release */,\n\t\t\t\tBBEF19AB26806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB792B661F96D90D00FFB7A4 /* Build configuration list for PBXNativeTarget \"iSH\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB792B641F96D90D00FFB7A4 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62062688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB792B651F96D90D00FFB7A4 /* Release */,\n\t\t\t\tBBEF19AC26806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBB88F4A22154760800A341FD /* Build configuration list for PBXNativeTarget \"iSHFileProvider\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBB88F4A02154760800A341FD /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62092688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBB88F4A12154760800A341FD /* Release */,\n\t\t\t\tBBEF19AF26806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBBDDF8A2CE00F6A0071F1F3 /* Build configuration list for PBXNativeTarget \"libiSHLinuxUser\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBBDDF8B2CE00F6A0071F1F3 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBBBDDF8C2CE00F6A0071F1F3 /* DebugLinux */,\n\t\t\t\tBBBDDF8D2CE00F6A0071F1F3 /* Release */,\n\t\t\t\tBBBDDF8E2CE00F6A0071F1F3 /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBECF3A82691314C00DEC937 /* Build configuration list for PBXNativeTarget \"libiSHLinux\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBECF3A92691314C00DEC937 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBBECF3AA2691314C00DEC937 /* DebugLinux */,\n\t\t\t\tBBECF3AB2691314C00DEC937 /* Release */,\n\t\t\t\tBBECF3AC2691314C00DEC937 /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBECF3B3269136E100DEC937 /* Build configuration list for PBXNativeTarget \"liblinux\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBECF3B4269136E100DEC937 /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBBECF3B5269136E100DEC937 /* DebugLinux */,\n\t\t\t\tBBECF3B6269136E100DEC937 /* Release */,\n\t\t\t\tBBECF3B7269136E100DEC937 /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBEF192626806364001225BD /* Build configuration list for PBXNativeTarget \"libiSHApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBEF192426806364001225BD /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62082688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBBEF192526806364001225BD /* Release */,\n\t\t\t\tBBEF19AE26806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBEF1999268066D1001225BD /* Build configuration list for PBXNativeTarget \"iSH+Linux\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBEF199A268066D1001225BD /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F62072688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBBEF199B268066D1001225BD /* Release */,\n\t\t\t\tBBEF19AD26806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBBFB2C582590257E00545EAB /* Build configuration list for PBXNativeTarget \"libish_emu\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBBFB2C592590257E00545EAB /* Debug-ApplePleaseFixFB19282108 */,\n\t\t\t\tBB7F620E2688EAB5003C0220 /* DebugLinux */,\n\t\t\t\tBBFB2C5A2590257E00545EAB /* Release */,\n\t\t\t\tBBEF19B426806D1F001225BD /* ReleaseLinux */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = BB792B461F96D8E000FFB7A4 /* Project object */;\n}\n"
  },
  {
    "path": "iSH.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:ish.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "iSH.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iSH.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iSH.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n   uuid = \"6E8E3717-3660-4400-9A7C-07FA89334909\"\n   type = \"4\"\n   version = \"2.0\">\n   <Breakpoints>\n      <BreakpointProxy\n         BreakpointExtensionID = \"Xcode.Breakpoint.SymbolicBreakpoint\">\n         <BreakpointContent\n            uuid = \"035095F5-47C6-49AD-8C66-AFEF7229BBBE\"\n            shouldBeEnabled = \"Yes\"\n            ignoreCount = \"0\"\n            continueAfterRunningActions = \"Yes\"\n            symbolName = \"UIApplicationMain\"\n            moduleName = \"UIKitCore\">\n            <Actions>\n               <BreakpointActionProxy\n                  ActionExtensionID = \"Xcode.BreakpointAction.DebuggerCommand\">\n                  <ActionContent\n                     consoleCommand = \"process handle SIGUSR1 -n true -p true -s false\">\n                  </ActionContent>\n               </BreakpointActionProxy>\n            </Actions>\n            <Locations>\n               <Location\n                  uuid = \"035095F5-47C6-49AD-8C66-AFEF7229BBBE - dc2f8c5e001177f0\"\n                  shouldBeEnabled = \"Yes\"\n                  ignoreCount = \"0\"\n                  continueAfterRunningActions = \"No\"\n                  symbolName = \"UIApplicationMain\"\n                  moduleName = \"UIKitCore\"\n                  usesParentBreakpointCondition = \"Yes\"\n                  offsetFromSymbolStart = \"0\">\n               </Location>\n            </Locations>\n         </BreakpointContent>\n      </BreakpointProxy>\n   </Breakpoints>\n</Bucket>\n"
  },
  {
    "path": "iSH.xcodeproj/xcshareddata/xcschemes/Screenshots.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1410\"\n   version = \"2.1\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BB41591B255EF9E300E0950C\"\n               BuildableName = \"iSHUITests.xctest\"\n               BlueprintName = \"iSHUITests\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"NO\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"NO\">\n            <TestPlanReference\n               reference = \"container:app/UITests/Screenshots.xctestplan\">\n            </TestPlanReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <TestPlans>\n         <TestPlanReference\n            reference = \"container:app/UITests/Screenshots.xctestplan\"\n            default = \"YES\">\n         </TestPlanReference>\n      </TestPlans>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BB41591B255EF9E300E0950C\"\n               BuildableName = \"iSHUITests.xctest\"\n               BlueprintName = \"iSHUITests\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n            <SelectedTests>\n               <Test\n                  Identifier = \"Screenshots/testEmacs\">\n               </Test>\n               <Test\n                  Identifier = \"Screenshots/testExample\">\n               </Test>\n               <Test\n                  Identifier = \"Screenshots/testTakeScreenshots\">\n               </Test>\n               <Test\n                  Identifier = \"Screenshots/testUname\">\n               </Test>\n            </SelectedTests>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BB41591B255EF9E300E0950C\"\n            BuildableName = \"iSHUITests.xctest\"\n            BlueprintName = \"iSHUITests\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iSH.xcodeproj/xcshareddata/xcschemes/iSH+Linux.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1410\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BBEF1964268066D1001225BD\"\n               BuildableName = \"iSH.app\"\n               BlueprintName = \"iSH+Linux\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"DebugLinux\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BB41591B255EF9E300E0950C\"\n               BuildableName = \"iSHUITests.xctest\"\n               BlueprintName = \"iSHUITests\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n            <SkippedTests>\n               <Test\n                  Identifier = \"Screenshots\">\n               </Test>\n            </SkippedTests>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"DebugLinux\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BBEF1964268066D1001225BD\"\n            BuildableName = \"iSH.app\"\n            BlueprintName = \"iSH+Linux\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"ReleaseLinux\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BBEF1964268066D1001225BD\"\n            BuildableName = \"iSH.app\"\n            BlueprintName = \"iSH+Linux\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"DebugLinux\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"ReleaseLinux\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iSH.xcodeproj/xcshareddata/xcschemes/iSH.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1410\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BB792B4F1F96D90D00FFB7A4\"\n               BuildableName = \"iSH.app\"\n               BlueprintName = \"iSH\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug-ApplePleaseFixFB19282108\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BB792B4F1F96D90D00FFB7A4\"\n            BuildableName = \"iSH.app\"\n            BlueprintName = \"iSH\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BB41591B255EF9E300E0950C\"\n               BuildableName = \"iSHUITests.xctest\"\n               BlueprintName = \"iSHUITests\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n            <SkippedTests>\n               <Test\n                  Identifier = \"Screenshots\">\n               </Test>\n            </SkippedTests>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug-ApplePleaseFixFB19282108\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BB792B4F1F96D90D00FFB7A4\"\n            BuildableName = \"iSH.app\"\n            BlueprintName = \"iSH\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BB792B4F1F96D90D00FFB7A4\"\n            BuildableName = \"iSH.app\"\n            BlueprintName = \"iSH\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug-ApplePleaseFixFB19282108\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iSH.xcodeproj/xcshareddata/xcschemes/ish-cli.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1410\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"497F6D46254E605F00C82F46\"\n               BuildableName = \"ish\"\n               BlueprintName = \"ish\"\n               ReferencedContainer = \"container:iSH.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"497F6D46254E605F00C82F46\"\n            BuildableName = \"ish\"\n            BlueprintName = \"ish\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"497F6D46254E605F00C82F46\"\n            BuildableName = \"ish\"\n            BlueprintName = \"ish\"\n            ReferencedContainer = \"container:iSH.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ish-gdb.gdb",
    "content": "handle SIGUSR1 noprint pass\nhandle SIGTTIN noprint pass\nhandle SIGPIPE noprint pass\nset print thread-events off\n\ndefine hook-run\n    python\nimport subprocess\nif subprocess.call('ninja') != 0:\n    raise gdb.CommandError('compilation failed')\n    end\nend\n\ndefine hook-stop\n    python\ntry:\n    symtab = gdb.selected_frame().find_sal().symtab\nexcept:\n    pass\nelse:\n    if symtab is not None and symtab.filename.endswith('.S'):\n        gdb.execute('set disassemble-next-line on')\n    else:\n        gdb.execute('set disassemble-next-line auto')\n    end\nend\n"
  },
  {
    "path": "ish-lldb.lldb",
    "content": "process handle -n 0 -p 1 -s 0 SIGUSR1 SIGTTIN SIGPIPE\n"
  },
  {
    "path": "kernel/calls.c",
    "content": "#include <string.h>\n#include \"debug.h\"\n#include \"kernel/calls.h\"\n#include \"emu/interrupt.h\"\n#include \"kernel/memory.h\"\n#include \"kernel/signal.h\"\n#include \"kernel/task.h\"\n\ndword_t syscall_stub(void) {\n    return _ENOSYS;\n}\n// While identical, this version of the stub doesn't log below. Use this for\n// syscalls that are optional (i.e. fallback on something else) but called\n// frequently.\ndword_t syscall_silent_stub(void) {\n    return _ENOSYS;\n}\ndword_t syscall_success_stub(void) {\n    return 0;\n}\n\n#if is_gcc(8)\n#pragma GCC diagnostic ignored \"-Wcast-function-type\"\n#endif\nsyscall_t syscall_table[] = {\n    [1]   = (syscall_t) sys_exit,\n    [2]   = (syscall_t) sys_fork,\n    [3]   = (syscall_t) sys_read,\n    [4]   = (syscall_t) sys_write,\n    [5]   = (syscall_t) sys_open,\n    [6]   = (syscall_t) sys_close,\n    [7]   = (syscall_t) sys_waitpid,\n    [9]   = (syscall_t) sys_link,\n    [10]  = (syscall_t) sys_unlink,\n    [11]  = (syscall_t) sys_execve,\n    [12]  = (syscall_t) sys_chdir,\n    [13]  = (syscall_t) sys_time,\n    [14]  = (syscall_t) sys_mknod,\n    [15]  = (syscall_t) sys_chmod,\n    [19]  = (syscall_t) sys_lseek,\n    [20]  = (syscall_t) sys_getpid,\n    [21]  = (syscall_t) sys_mount,\n    [23]  = (syscall_t) sys_setuid,\n    [24]  = (syscall_t) sys_getuid,\n    [25]  = (syscall_t) sys_stime,\n    [26]  = (syscall_t) sys_ptrace,\n    [27]  = (syscall_t) sys_alarm,\n    [29]  = (syscall_t) sys_pause,\n    [30]  = (syscall_t) sys_utime,\n    [33]  = (syscall_t) sys_access,\n    [36]  = (syscall_t) syscall_success_stub, // sync\n    [37]  = (syscall_t) sys_kill,\n    [38]  = (syscall_t) sys_rename,\n    [39]  = (syscall_t) sys_mkdir,\n    [40]  = (syscall_t) sys_rmdir,\n    [41]  = (syscall_t) sys_dup,\n    [42]  = (syscall_t) sys_pipe,\n    [43]  = (syscall_t) sys_times,\n    [45]  = (syscall_t) sys_brk,\n    [46]  = (syscall_t) sys_setgid,\n    [47]  = (syscall_t) sys_getgid,\n    [49]  = (syscall_t) sys_geteuid,\n    [50]  = (syscall_t) sys_getegid,\n    [52]  = (syscall_t) sys_umount2,\n    [54]  = (syscall_t) sys_ioctl,\n    [55]  = (syscall_t) sys_fcntl32,\n    [57]  = (syscall_t) sys_setpgid,\n    [60]  = (syscall_t) sys_umask,\n    [61]  = (syscall_t) sys_chroot,\n    [63]  = (syscall_t) sys_dup2,\n    [64]  = (syscall_t) sys_getppid,\n    [65]  = (syscall_t) sys_getpgrp,\n    [66]  = (syscall_t) sys_setsid,\n    [74]  = (syscall_t) sys_sethostname,\n    [75]  = (syscall_t) sys_setrlimit32,\n    [76]  = (syscall_t) sys_old_getrlimit32,\n    [77]  = (syscall_t) sys_getrusage,\n    [78]  = (syscall_t) sys_gettimeofday,\n    [79]  = (syscall_t) sys_settimeofday,\n    [80]  = (syscall_t) sys_getgroups,\n    [81]  = (syscall_t) sys_setgroups,\n    [83]  = (syscall_t) sys_symlink,\n    [85]  = (syscall_t) sys_readlink,\n    [88]  = (syscall_t) sys_reboot,\n    [90]  = (syscall_t) sys_mmap,\n    [91]  = (syscall_t) sys_munmap,\n    [94]  = (syscall_t) sys_fchmod,\n    [96]  = (syscall_t) sys_getpriority,\n    [97]  = (syscall_t) sys_setpriority,\n    [99]  = (syscall_t) sys_statfs,\n    [100] = (syscall_t) sys_fstatfs,\n    [102] = (syscall_t) sys_socketcall,\n    [103] = (syscall_t) sys_syslog,\n    [104] = (syscall_t) sys_setitimer,\n    [114] = (syscall_t) sys_wait4,\n    [116] = (syscall_t) sys_sysinfo,\n    [117] = (syscall_t) sys_ipc,\n    [118] = (syscall_t) sys_fsync,\n    [119] = (syscall_t) sys_sigreturn,\n    [120] = (syscall_t) sys_clone,\n    [122] = (syscall_t) sys_uname,\n    [125] = (syscall_t) sys_mprotect,\n    [132] = (syscall_t) sys_getpgid,\n    [133] = (syscall_t) sys_fchdir,\n    [136] = (syscall_t) sys_personality,\n    [140] = (syscall_t) sys__llseek,\n    [141] = (syscall_t) sys_getdents,\n    [142] = (syscall_t) sys_select,\n    [143] = (syscall_t) sys_flock,\n    [144] = (syscall_t) sys_msync,\n    [145] = (syscall_t) sys_readv,\n    [146] = (syscall_t) sys_writev,\n    [147] = (syscall_t) sys_getsid,\n    [148] = (syscall_t) sys_fsync, // fdatasync\n    [150] = (syscall_t) sys_mlock,\n    [155] = (syscall_t) sys_sched_getparam,\n    [156] = (syscall_t) sys_sched_setscheduler,\n    [157] = (syscall_t) sys_sched_getscheduler,\n    [158] = (syscall_t) sys_sched_yield,\n    [159] = (syscall_t) sys_sched_get_priority_max,\n    [162] = (syscall_t) sys_nanosleep,\n    [163] = (syscall_t) sys_mremap,\n    [168] = (syscall_t) sys_poll,\n    [172] = (syscall_t) sys_prctl,\n    [173] = (syscall_t) sys_rt_sigreturn,\n    [174] = (syscall_t) sys_rt_sigaction,\n    [175] = (syscall_t) sys_rt_sigprocmask,\n    [176] = (syscall_t) sys_rt_sigpending,\n    [177] = (syscall_t) sys_rt_sigtimedwait,\n    [179] = (syscall_t) sys_rt_sigsuspend,\n    [180] = (syscall_t) sys_pread,\n    [181] = (syscall_t) sys_pwrite,\n    [183] = (syscall_t) sys_getcwd,\n    [184] = (syscall_t) sys_capget,\n    [185] = (syscall_t) sys_capset,\n    [186] = (syscall_t) sys_sigaltstack,\n    [187] = (syscall_t) sys_sendfile,\n    [190] = (syscall_t) sys_vfork,\n    [191] = (syscall_t) sys_getrlimit32,\n    [192] = (syscall_t) sys_mmap2,\n    [193] = (syscall_t) sys_truncate64,\n    [194] = (syscall_t) sys_ftruncate64,\n    [195] = (syscall_t) sys_stat64,\n    [196] = (syscall_t) sys_lstat64,\n    [197] = (syscall_t) sys_fstat64,\n    [198] = (syscall_t) sys_lchown,\n    [199] = (syscall_t) sys_getuid32,\n    [200] = (syscall_t) sys_getgid32,\n    [201] = (syscall_t) sys_geteuid32,\n    [202] = (syscall_t) sys_getegid32,\n    [203] = (syscall_t) sys_setreuid,\n    [204] = (syscall_t) sys_setregid,\n    [205] = (syscall_t) sys_getgroups,\n    [206] = (syscall_t) sys_setgroups,\n    [207] = (syscall_t) sys_fchown32,\n    [208] = (syscall_t) sys_setresuid,\n    [209] = (syscall_t) sys_getresuid,\n    [210] = (syscall_t) sys_setresgid,\n    [211] = (syscall_t) sys_getresgid,\n    [212] = (syscall_t) sys_chown32,\n    [213] = (syscall_t) sys_setuid,\n    [214] = (syscall_t) sys_setgid,\n    [215] = (syscall_t) syscall_stub, // setfsuid\n    [216] = (syscall_t) syscall_stub, // setfsgid\n    [219] = (syscall_t) sys_madvise,\n    [220] = (syscall_t) sys_getdents64,\n    [221] = (syscall_t) sys_fcntl,\n    [224] = (syscall_t) sys_gettid,\n    [225] = (syscall_t) syscall_success_stub, // readahead\n    [226 ... 237] = (syscall_t) sys_xattr_stub,\n    [238] = (syscall_t) sys_tkill,\n    [239] = (syscall_t) sys_sendfile64,\n    [240] = (syscall_t) sys_futex,\n    [241] = (syscall_t) sys_sched_setaffinity,\n    [242] = (syscall_t) sys_sched_getaffinity,\n    [243] = (syscall_t) sys_set_thread_area,\n    [245] = (syscall_t) syscall_stub, // io_setup\n    [252] = (syscall_t) sys_exit_group,\n    [254] = (syscall_t) sys_epoll_create0,\n    [255] = (syscall_t) sys_epoll_ctl,\n    [256] = (syscall_t) sys_epoll_wait,\n    [258] = (syscall_t) sys_set_tid_address,\n    [259] = (syscall_t) sys_timer_create,\n    [260] = (syscall_t) sys_timer_settime,\n    [263] = (syscall_t) sys_timer_delete,\n    [264] = (syscall_t) sys_clock_settime,\n    [265] = (syscall_t) sys_clock_gettime,\n    [266] = (syscall_t) sys_clock_getres,\n    [268] = (syscall_t) sys_statfs64,\n    [269] = (syscall_t) sys_fstatfs64,\n    [270] = (syscall_t) sys_tgkill,\n    [271] = (syscall_t) sys_utimes,\n    [272] = (syscall_t) syscall_success_stub,\n    [274] = (syscall_t) sys_mbind,\n    [284] = (syscall_t) sys_waitid,\n    [289] = (syscall_t) sys_ioprio_set,\n    [290] = (syscall_t) sys_ioprio_get,\n    [291] = (syscall_t) syscall_stub, // inotify_init\n    [295] = (syscall_t) sys_openat,\n    [296] = (syscall_t) sys_mkdirat,\n    [297] = (syscall_t) sys_mknodat,\n    [298] = (syscall_t) sys_fchownat,\n    [300] = (syscall_t) sys_fstatat64,\n    [301] = (syscall_t) sys_unlinkat,\n    [302] = (syscall_t) sys_renameat,\n    [303] = (syscall_t) sys_linkat,\n    [304] = (syscall_t) sys_symlinkat,\n    [305] = (syscall_t) sys_readlinkat,\n    [306] = (syscall_t) sys_fchmodat,\n    [307] = (syscall_t) sys_faccessat,\n    [308] = (syscall_t) sys_pselect,\n    [309] = (syscall_t) sys_ppoll,\n    [311] = (syscall_t) sys_set_robust_list,\n    [312] = (syscall_t) sys_get_robust_list,\n    [313] = (syscall_t) sys_splice,\n    [319] = (syscall_t) sys_epoll_pwait,\n    [320] = (syscall_t) sys_utimensat,\n    [322] = (syscall_t) sys_timerfd_create,\n    [323] = (syscall_t) sys_eventfd,\n    [324] = (syscall_t) sys_fallocate,\n    [325] = (syscall_t) sys_timerfd_settime,\n    [328] = (syscall_t) sys_eventfd2,\n    [329] = (syscall_t) sys_epoll_create,\n    [330] = (syscall_t) sys_dup3,\n    [331] = (syscall_t) sys_pipe2,\n    [332] = (syscall_t) syscall_stub, // inotify_init1\n    [340] = (syscall_t) sys_prlimit64,\n    [345] = (syscall_t) sys_sendmmsg,\n    [352] = (syscall_t) syscall_stub, // sched_getattr\n    [353] = (syscall_t) sys_renameat2,\n    [355] = (syscall_t) sys_getrandom,\n    [359] = (syscall_t) sys_socket,\n    [360] = (syscall_t) sys_socketpair,\n    [361] = (syscall_t) sys_bind,\n    [362] = (syscall_t) sys_connect,\n    [363] = (syscall_t) sys_listen,\n    [364] = (syscall_t) syscall_stub, // accept4\n    [365] = (syscall_t) sys_getsockopt,\n    [366] = (syscall_t) sys_setsockopt,\n    [367] = (syscall_t) sys_getsockname,\n    [368] = (syscall_t) sys_getpeername,\n    [369] = (syscall_t) sys_sendto,\n    [370] = (syscall_t) sys_sendmsg,\n    [371] = (syscall_t) sys_recvfrom,\n    [372] = (syscall_t) sys_recvmsg,\n    [373] = (syscall_t) sys_shutdown,\n    [375] = (syscall_t) syscall_silent_stub, // membarrier\n    [377] = (syscall_t) sys_copy_file_range,\n    [383] = (syscall_t) sys_statx,\n    [384] = (syscall_t) sys_arch_prctl,\n    [422] = (syscall_t) syscall_silent_stub, // futex_time64\n    [439] = (syscall_t) syscall_silent_stub, // faccessat2\n};\n\n#define NUM_SYSCALLS (sizeof(syscall_table) / sizeof(syscall_table[0]))\n\nvoid dump_stack(int lines);\n\nvoid handle_interrupt(int interrupt) {\n    struct cpu_state *cpu = &current->cpu;\n    if (interrupt == INT_SYSCALL) {\n        unsigned syscall_num = cpu->eax;\n        if (syscall_num >= NUM_SYSCALLS || syscall_table[syscall_num] == NULL) {\n            printk(\"%d(%s) missing syscall %d\\n\", current->pid, current->comm, syscall_num);\n            cpu->eax = _ENOSYS;\n        } else {\n            if (syscall_table[syscall_num] == (syscall_t) syscall_stub) {\n                printk(\"%d(%s) stub syscall %d\\n\", current->pid, current->comm, syscall_num);\n            }\n            STRACE(\"%d call %-3d \", current->pid, syscall_num);\n            int result = syscall_table[syscall_num](cpu->ebx, cpu->ecx, cpu->edx, cpu->esi, cpu->edi, cpu->ebp);\n            STRACE(\" = 0x%x\\n\", result);\n            cpu->eax = result;\n        }\n    } else if (interrupt == INT_GPF) {\n        // some page faults, such as stack growing or CoW clones, are handled by mem_ptr\n        read_wrlock(&current->mem->lock);\n        void *ptr = mem_ptr(current->mem, cpu->segfault_addr, cpu->segfault_was_write ? MEM_WRITE : MEM_READ);\n        read_wrunlock(&current->mem->lock);\n        if (ptr == NULL) {\n            printk(\"%d page fault on 0x%x at 0x%x\\n\", current->pid, cpu->segfault_addr, cpu->eip);\n            struct siginfo_ info = {\n                .code = mem_segv_reason(current->mem, cpu->segfault_addr),\n                .fault.addr = cpu->segfault_addr,\n            };\n            dump_stack(8);\n            deliver_signal(current, SIGSEGV_, info);\n        }\n    } else if (interrupt == INT_UNDEFINED) {\n        printk(\"%d illegal instruction at 0x%x: \", current->pid, cpu->eip);\n        for (int i = 0; i < 8; i++) {\n            uint8_t b;\n            if (user_get(cpu->eip + i, b))\n                break;\n            printk(\"%02x \", b);\n        }\n        printk(\"\\n\");\n        dump_stack(8);\n        struct siginfo_ info = {\n            .code = SI_KERNEL_,\n            .fault.addr = cpu->eip,\n        };\n        deliver_signal(current, SIGILL_, info);\n    } else if (interrupt == INT_BREAKPOINT) {\n        lock(&pids_lock);\n        send_signal(current, SIGTRAP_, (struct siginfo_) {\n            .sig = SIGTRAP_,\n            .code = SI_KERNEL_,\n        });\n        unlock(&pids_lock);\n    } else if (interrupt == INT_DEBUG) {\n        lock(&pids_lock);\n        send_signal(current, SIGTRAP_, (struct siginfo_) {\n            .sig = SIGTRAP_,\n            .code = TRAP_TRACE_,\n        });\n        unlock(&pids_lock);\n    } else if (interrupt != INT_TIMER) {\n        printk(\"%d unhandled interrupt %d\\n\", current->pid, interrupt);\n        sys_exit(interrupt);\n    }\n\n    receive_signals();\n    struct tgroup *group = current->group;\n    lock(&group->lock);\n    while (group->stopped)\n        wait_for_ignore_signals(&group->stopped_cond, &group->lock, NULL);\n    unlock(&group->lock);\n}\n\nvoid dump_maps(void) {\n    extern void proc_maps_dump(struct task *task, struct proc_data *buf);\n    struct proc_data buf = {};\n    proc_maps_dump(current, &buf);\n    // go a line at a time because it can be fucking enormous\n    char *orig_data = buf.data;\n    while (buf.size > 0) {\n        size_t chunk_size = buf.size;\n        if (chunk_size > 1024)\n            chunk_size = 1024;\n        printk(\"%.*s\", chunk_size, buf.data);\n        buf.data += chunk_size;\n        buf.size -= chunk_size;\n    }\n    free(orig_data);\n}\n\nvoid dump_mem(addr_t start, uint_t len) {\n    const int width = 8;\n    for (addr_t addr = start; addr < start + len; addr += sizeof(dword_t)) {\n        unsigned from_left = (addr - start) / sizeof(dword_t) % width;\n        if (from_left == 0)\n            printk(\"%08x: \", addr);\n        dword_t word;\n        if (user_get(addr, word))\n            break;\n        printk(\"%08x \", word);\n        if (from_left == width - 1)\n            printk(\"\\n\");\n    }\n}\n\nvoid dump_stack(int lines) {\n    printk(\"stack at %x, base at %x, ip at %x\\n\", current->cpu.esp, current->cpu.ebp, current->cpu.eip);\n    dump_mem(current->cpu.esp, lines * sizeof(dword_t) * 8);\n}\n\n// TODO find a home for this\n#ifdef LOG_OVERRIDE\nint log_override = 0;\n#endif\n"
  },
  {
    "path": "kernel/calls.h",
    "content": "#ifndef CALLS_H\n#define CALLS_H\n\n#include \"kernel/task.h\"\n#include \"kernel/errno.h\"\n#include \"fs/fd.h\"\n#include \"fs/dev.h\"\n#include \"kernel/fs.h\"\n#include \"misc.h\"\n\n#include \"kernel/signal.h\"\n#include \"fs/sock.h\"\n#include \"kernel/time.h\"\n#include \"kernel/resource.h\"\n#include \"kernel/ptrace.h\"\n\nvoid handle_interrupt(int interrupt);\n\nint must_check user_read(addr_t addr, void *buf, size_t count);\nint must_check user_write(addr_t addr, const void *buf, size_t count);\nint must_check user_read_task(struct task *task, addr_t addr, void *buf, size_t count);\nint must_check user_write_task(struct task *task, addr_t addr, const void *buf, size_t count);\nint must_check user_write_task_ptrace(struct task *task, addr_t addr, const void *buf, size_t count);\nint must_check user_read_string(addr_t addr, char *buf, size_t max);\nint must_check user_write_string(addr_t addr, const char *buf);\n#define user_get(addr, var) user_read(addr, &(var), sizeof(var))\n#define user_put(addr, var) user_write(addr, &(var), sizeof(var))\n#define user_get_task(task, addr, var) user_read_task(task, addr, &(var), sizeof(var))\n#define user_put_task(task, addr, var) user_write_task(task, addr, &(var), sizeof(var))\n\n// process lifecycle\ndword_t sys_clone(dword_t flags, addr_t stack, addr_t ptid, addr_t tls, addr_t ctid);\ndword_t sys_fork(void);\ndword_t sys_vfork(void);\ndword_t sys_execve(addr_t file, addr_t argv, addr_t envp);\nint do_execve(const char *file, size_t argc, const char *argv, const char *envp);\ndword_t sys_exit(dword_t status);\nnoreturn void do_exit(int status);\nnoreturn void do_exit_group(int status);\ndword_t sys_exit_group(dword_t status);\ndword_t sys_wait4(pid_t_ pid, addr_t status_addr, dword_t options, addr_t rusage_addr);\ndword_t sys_waitid(int_t idtype, pid_t_ id, addr_t info_addr, int_t options);\ndword_t sys_waitpid(pid_t_ pid, addr_t status_addr, dword_t options);\n\n// memory management\naddr_t sys_brk(addr_t new_brk);\n\n#define MMAP_SHARED 0x1\n#define MMAP_PRIVATE 0x2\n#define MMAP_FIXED 0x10\n#define MMAP_ANONYMOUS 0x20\naddr_t sys_mmap(addr_t args_addr);\naddr_t sys_mmap2(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset);\nint_t sys_munmap(addr_t addr, uint_t len);\nint_t sys_mprotect(addr_t addr, uint_t len, int_t prot);\nint_t sys_mremap(addr_t addr, dword_t old_len, dword_t new_len, dword_t flags);\ndword_t sys_madvise(addr_t addr, dword_t len, dword_t advice);\ndword_t sys_mbind(addr_t addr, dword_t len, int_t mode, addr_t nodemask, dword_t maxnode, uint_t flags);\nint_t sys_mlock(addr_t addr, dword_t len);\nint_t sys_msync(addr_t addr, dword_t len, int_t flags);\n\n// file descriptor things\n#define LOCK_SH_ 1\n#define LOCK_EX_ 2\n#define LOCK_NB_ 4\n#define LOCK_UN_ 8\nstruct iovec_ {\n    addr_t base;\n    uint_t len;\n};\ndword_t sys_read(fd_t fd_no, addr_t buf_addr, dword_t size);\ndword_t sys_readv(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count);\ndword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size);\ndword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count);\ndword_t sys__llseek(fd_t f, dword_t off_high, dword_t off_low, addr_t res_addr, dword_t whence);\ndword_t sys_lseek(fd_t f, dword_t off, dword_t whence);\ndword_t sys_pread(fd_t f, addr_t buf_addr, dword_t buf_size, off_t_ off);\ndword_t sys_pwrite(fd_t f, addr_t buf_addr, dword_t size, off_t_ off);\ndword_t sys_ioctl(fd_t f, dword_t cmd, dword_t arg);\ndword_t sys_fcntl(fd_t f, dword_t cmd, dword_t arg);\ndword_t sys_fcntl32(fd_t fd, dword_t cmd, dword_t arg);\ndword_t sys_dup(fd_t fd);\ndword_t sys_dup2(fd_t fd, fd_t new_fd);\ndword_t sys_dup3(fd_t f, fd_t new_f, int_t flags);\ndword_t sys_close(fd_t fd);\ndword_t sys_fsync(fd_t f);\ndword_t sys_flock(fd_t fd, dword_t operation);\nint_t sys_pipe(addr_t pipe_addr);\nint_t sys_pipe2(addr_t pipe_addr, int_t flags);\nstruct pollfd_ {\n    fd_t fd;\n    word_t events;\n    word_t revents;\n};\ndword_t sys_poll(addr_t fds, dword_t nfds, int_t timeout);\ndword_t sys_select(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t exceptfds_addr, addr_t timeout_addr);\ndword_t sys_pselect(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t exceptfds_addr, addr_t timeout_addr, addr_t sigmask_addr);\ndword_t sys_ppoll(addr_t fds, dword_t nfds, addr_t timeout_addr, addr_t sigmask_addr, dword_t sigsetsize);\nfd_t sys_epoll_create(int_t flags);\nfd_t sys_epoll_create0(void);\nint_t sys_epoll_ctl(fd_t epoll, int_t op, fd_t fd, addr_t event_addr);\nint_t sys_epoll_wait(fd_t epoll, addr_t events_addr, int_t max_events, int_t timeout);\nint_t sys_epoll_pwait(fd_t epoll_f, addr_t events_addr, int_t max_events, int_t timeout, addr_t sigmask_addr, dword_t sigsetsize);\n\nint_t sys_eventfd2(uint_t initval, int_t flags);\nint_t sys_eventfd(uint_t initval);\n\n// file management\nfd_t sys_open(addr_t path_addr, dword_t flags, mode_t_ mode);\nfd_t sys_openat(fd_t at, addr_t path_addr, dword_t flags, mode_t_ mode);\ndword_t sys_close(fd_t fd);\ndword_t sys_link(addr_t src_addr, addr_t dst_addr);\ndword_t sys_linkat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr);\ndword_t sys_unlink(addr_t path_addr);\ndword_t sys_unlinkat(fd_t at_f, addr_t path_addr, int_t flags);\ndword_t sys_rmdir(addr_t path_addr);\ndword_t sys_rename(addr_t src_addr, addr_t dst_addr);\ndword_t sys_renameat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr);\ndword_t sys_renameat2(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr, int_t flags);\ndword_t sys_symlink(addr_t target_addr, addr_t link_addr);\ndword_t sys_symlinkat(addr_t target_addr, fd_t at_f, addr_t link_addr);\ndword_t sys_mknod(addr_t path_addr, mode_t_ mode, dev_t_ dev);\ndword_t sys_mknodat(fd_t at_f, addr_t path_addr, mode_t_ mode, dev_t_ dev);\ndword_t sys_access(addr_t path_addr, dword_t mode);\ndword_t sys_faccessat(fd_t at_f, addr_t path, mode_t_ mode, dword_t flags);\ndword_t sys_readlink(addr_t path, addr_t buf, dword_t bufsize);\ndword_t sys_readlinkat(fd_t at_f, addr_t path, addr_t buf, dword_t bufsize);\nint_t sys_getdents(fd_t f, addr_t dirents, dword_t count);\nint_t sys_getdents64(fd_t f, addr_t dirents, dword_t count);\ndword_t sys_stat64(addr_t path_addr, addr_t statbuf_addr);\ndword_t sys_lstat64(addr_t path_addr, addr_t statbuf_addr);\ndword_t sys_fstat64(fd_t fd_no, addr_t statbuf_addr);\ndword_t sys_fstatat64(fd_t at, addr_t path_addr, addr_t statbuf_addr, dword_t flags);\ndword_t sys_fchmod(fd_t f, dword_t mode);\ndword_t sys_fchmodat(fd_t at_f, addr_t path_addr, dword_t mode);\ndword_t sys_chmod(addr_t path_addr, dword_t mode);\ndword_t sys_fchown32(fd_t f, dword_t owner, dword_t group);\ndword_t sys_fchownat(fd_t at_f, addr_t path_addr, dword_t owner, dword_t group, int flags);\ndword_t sys_chown32(addr_t path_addr, uid_t_ owner, uid_t_ group);\ndword_t sys_lchown(addr_t path_addr, uid_t_ owner, uid_t_ group);\ndword_t sys_truncate64(addr_t path_addr, dword_t size_low, dword_t size_high);\ndword_t sys_ftruncate64(fd_t f, dword_t size_low, dword_t size_high);\ndword_t sys_fallocate(fd_t f, dword_t mode, dword_t offset_low, dword_t offset_high, dword_t len_low, dword_t len_high);\ndword_t sys_mkdir(addr_t path_addr, mode_t_ mode);\ndword_t sys_mkdirat(fd_t at_f, addr_t path_addr, mode_t_ mode);\ndword_t sys_utimensat(fd_t at_f, addr_t path_addr, addr_t times_addr, dword_t flags);\ndword_t sys_utimes(addr_t path_addr, addr_t times_addr);\ndword_t sys_utime(addr_t path_addr, addr_t times_addr);\ndword_t sys_times( addr_t tbuf);\ndword_t sys_umask(dword_t mask);\n\ndword_t sys_sendfile(fd_t out_fd, fd_t in_fd, addr_t offset_addr, dword_t count);\ndword_t sys_sendfile64(fd_t out_fd, fd_t in_fd, addr_t offset_addr, dword_t count);\ndword_t sys_splice(fd_t in_fd, addr_t in_off_addr, fd_t out_fd, addr_t out_off_addr, dword_t count, dword_t flags);\ndword_t sys_copy_file_range(fd_t in_fd, addr_t in_off, fd_t out_fd, addr_t out_off, dword_t len, uint_t flags);\n\ndword_t sys_statfs(addr_t path_addr, addr_t buf_addr);\ndword_t sys_statfs64(addr_t path_addr, dword_t buf_size, addr_t buf_addr);\ndword_t sys_fstatfs(fd_t f, addr_t buf_addr);\ndword_t sys_fstatfs64(fd_t f, addr_t buf_addr);\ndword_t sys_statx(fd_t at_f, addr_t path_addr, int_t flags, uint_t mask, addr_t statx_addr);\n\n#define MS_READONLY_ (1 << 0)\n#define MS_NOSUID_ (1 << 1)\n#define MS_NODEV_ (1 << 2)\n#define MS_NOEXEC_ (1 << 3)\n#define MS_SILENT_ (1 << 15)\ndword_t sys_mount(addr_t source_addr, addr_t target_addr, addr_t type_addr, dword_t flags, addr_t data_addr);\ndword_t sys_umount2(addr_t target_addr, dword_t flags);\n\ndword_t sys_xattr_stub(addr_t path_addr, addr_t name_addr, addr_t value_addr, dword_t size, dword_t flags);\n\n// process information\npid_t_ sys_getpid(void);\npid_t_ sys_gettid(void);\npid_t_ sys_getppid(void);\npid_t_ sys_getpgid(pid_t_ pid);\ndword_t sys_setpgid(pid_t_ pid, pid_t_ pgid);\npid_t_ sys_getpgrp(void);\ndword_t sys_setpgrp(void);\nuid_t_ sys_getuid32(void);\nuid_t_ sys_getuid(void);\nint_t sys_setuid(uid_t uid);\nuid_t_ sys_geteuid32(void);\nuid_t_ sys_geteuid(void);\nint_t sys_setgid(uid_t gid);\nuid_t_ sys_getgid32(void);\nuid_t_ sys_getgid(void);\nuid_t_ sys_getegid32(void);\nuid_t_ sys_getegid(void);\ndword_t sys_setresuid(uid_t_ ruid, uid_t_ euid, uid_t_ suid);\ndword_t sys_setresgid(uid_t_ rgid, uid_t_ egid, uid_t_ sgid);\nint_t sys_setreuid(uid_t_ ruid, uid_t_ euid);\nint_t sys_setregid(uid_t_ rgid, uid_t_ egid);\nint_t sys_getresuid(addr_t ruid_addr, addr_t euid_addr, addr_t suid_addr);\nint_t sys_getresgid(addr_t rgid_addr, addr_t egid_addr, addr_t sgid_addr);\nint_t sys_getgroups(dword_t size, addr_t list);\nint_t sys_setgroups(dword_t size, addr_t list);\nint_t sys_capget(addr_t header_addr, addr_t data_addr);\nint_t sys_capset(addr_t header_addr, addr_t data_addr);\ndword_t sys_getcwd(addr_t buf_addr, dword_t size);\ndword_t sys_chdir(addr_t path_addr);\ndword_t sys_chroot(addr_t path_addr);\ndword_t sys_fchdir(fd_t f);\nint_t sys_personality(dword_t pers);\nint task_set_thread_area(struct task *task, addr_t u_info);\nint sys_set_thread_area(addr_t u_info);\nint sys_set_tid_address(addr_t blahblahblah);\ndword_t sys_setsid(void);\ndword_t sys_getsid(void);\n\nint_t sys_sched_yield(void);\nint_t sys_prctl(dword_t option, uint_t arg2, uint_t arg3, uint_t arg4, uint_t arg5);\nint_t sys_arch_prctl(int_t code, addr_t addr);\nint_t sys_reboot(int_t magic, int_t magic2, int_t cmd);\n\n// system information\n#define UNAME_LENGTH 65\nstruct uname {\n    char system[UNAME_LENGTH];   // Linux\n    char hostname[UNAME_LENGTH]; // my-compotar\n    char release[UNAME_LENGTH];  // 1.2.3-ish\n    char version[UNAME_LENGTH];  // SUPER AWESOME\n    char arch[UNAME_LENGTH];     // i686\n    char domain[UNAME_LENGTH];   // lol\n};\nvoid do_uname(struct uname *uts);\ndword_t sys_uname(addr_t uts_addr);\ndword_t sys_sethostname(addr_t hostname_addr, dword_t hostname_len);\n\nstruct sys_info {\n    dword_t uptime;\n    dword_t loads[3];\n    dword_t totalram;\n    dword_t freeram;\n    dword_t sharedram;\n    dword_t bufferram;\n    dword_t totalswap;\n    dword_t freeswap;\n    word_t procs;\n    dword_t totalhigh;\n    dword_t freehigh;\n    dword_t mem_unit;\n    char pad;\n};\ndword_t sys_sysinfo(addr_t info_addr);\n\n// futexes\ndword_t sys_futex(addr_t uaddr, dword_t op, dword_t val, addr_t timeout_or_val2, addr_t uaddr2, dword_t val3);\nint_t sys_set_robust_list(addr_t robust_list, dword_t len);\nint_t sys_get_robust_list(pid_t_ pid, addr_t robust_list_ptr, addr_t len_ptr);\n\n// misc\ndword_t sys_getrandom(addr_t buf_addr, dword_t len, dword_t flags);\nint_t sys_syslog(int_t type, addr_t buf_addr, int_t len);\nint_t sys_ipc(uint_t call, int_t first, int_t second, int_t third, addr_t ptr, int_t fifth);\n\ntypedef int (*syscall_t)(dword_t, dword_t, dword_t, dword_t, dword_t, dword_t);\n\n#endif\n"
  },
  {
    "path": "kernel/elf.h",
    "content": "#ifndef ELF_H\n#define ELF_H\n\n#include \"misc.h\"\n\n#define ELF_MAGIC \"\\177ELF\"\n#define ELF_32BIT 1\n#define ELF_64BIT 2\n#define ELF_LITTLEENDIAN 1\n#define ELF_BIGENDIAN 2\n#define ELF_LINUX_ABI 3\n#define ELF_EXECUTABLE 2\n#define ELF_DYNAMIC 3\n#define ELF_X86 3\n\nstruct elf_header {\n    uint32_t magic;\n    byte_t bitness;\n    byte_t endian;\n    byte_t elfversion1;\n    byte_t abi;\n    byte_t abi_version;\n    byte_t padding[7];\n    uint16_t type; // library or executable or what\n    uint16_t machine;\n    uint32_t elfversion2;\n    dword_t entry_point;\n    dword_t prghead_off;\n    dword_t secthead_off;\n    uint32_t flags;\n    uint16_t header_size;\n    uint16_t phent_size;\n    uint16_t phent_count;\n    uint16_t shent_size;\n    uint16_t shent_count;\n    uint16_t sectname_index;\n};\n\n#define PT_NULL 0\n#define PT_LOAD 1\n#define PT_DYNAMIC 2\n#define PT_INTERP 3\n#define PT_NOTE 4\n#define PT_SHLIB 5\n#define PT_PHDR 6\n#define PT_TLS 7\n#define PT_NUM 8\n\nstruct prg_header {\n    uint32_t type;\n    dword_t offset;\n    dword_t vaddr;\n    dword_t paddr;\n    dword_t filesize;\n    dword_t memsize;\n    uint32_t flags;\n    dword_t alignment; // must be power of 2\n};\n\n#define PH_R (1 << 2)\n#define PH_W (1 << 1)\n#define PH_X (1 << 0)\n\nstruct aux_ent {\n    uint32_t type;\n    uint32_t value;\n};\n\n#define AX_PHDR 3\n#define AX_PHENT 4\n#define AX_PHNUM 5\n#define AX_PAGESZ 6\n#define AX_BASE 7\n#define AX_FLAGS 8\n#define AX_ENTRY 9\n#define AX_UID 11\n#define AX_EUID 12\n#define AX_GID 13\n#define AX_EGID 14\n#define AX_PLATFORM 15\n#define AX_HWCAP 16\n#define AX_CLKTCK 17\n#define AX_SECURE 23\n#define AX_RANDOM 25\n#define AX_HWCAP2 26\n#define AX_EXECFN 31\n#define AX_SYSINFO 32\n#define AX_SYSINFO_EHDR 33\n\nstruct dyn_ent {\n    dword_t tag;\n    dword_t val;\n};\n\n#define DT_NULL 0\n#define DT_HASH 4\n#define DT_STRTAB 5\n#define DT_SYMTAB 6\n\nstruct elf_sym {\n    uint32_t name;\n    addr_t value;\n    dword_t size;\n    byte_t info;\n    byte_t other;\n    uint16_t shndx;\n};\n\n#endif\n"
  },
  {
    "path": "kernel/epoll.c",
    "content": "#include \"kernel/calls.h\"\n#include \"fs/poll.h\"\n\nstatic struct fd_ops epoll_ops;\n\nfd_t sys_epoll_create(int_t flags) {\n    STRACE(\"epoll_create(%#x)\", flags);\n    if (flags & ~(O_CLOEXEC_))\n        return _EINVAL;\n\n    struct fd *fd = adhoc_fd_create(&epoll_ops);\n    if (fd == NULL)\n        return _ENOMEM;\n    struct poll *poll = poll_create();\n    if (IS_ERR(poll))\n        return PTR_ERR(poll);\n    fd->epollfd.poll = poll;\n    return f_install(fd, flags);\n}\nfd_t sys_epoll_create0() {\n    return sys_epoll_create(0);\n}\n\nstruct epoll_event_ {\n    uint32_t events;\n    uint64_t data;\n} __attribute__((packed));\n\n#define EPOLL_CTL_ADD_ 1\n#define EPOLL_CTL_DEL_ 2\n#define EPOLL_CTL_MOD_ 3\n#define EPOLLET_ (1 << 31)\n#define EPOLLONESHOT_ (1 << 30)\n\nint_t sys_epoll_ctl(fd_t epoll_f, int_t op, fd_t f, addr_t event_addr) {\n    STRACE(\"epoll_ctl(%d, %d, %d, %#x)\", epoll_f, op, f, event_addr);\n    struct fd *epoll = f_get(epoll_f);\n    if (epoll == NULL)\n        return _EBADF;\n    if (epoll->ops != &epoll_ops)\n        return _EINVAL;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n\n    if (op == EPOLL_CTL_DEL_)\n        return poll_del_fd(epoll->epollfd.poll, fd);\n\n    struct epoll_event_ event;\n    if (user_get(event_addr, event))\n        return _EFAULT;\n    STRACE(\" {events: %#x, data: %#x}\", event.events, event.data);\n\n    if (op == EPOLL_CTL_ADD_) {\n        if (poll_has_fd(epoll->epollfd.poll, fd))\n            return _EEXIST;\n        return poll_add_fd(epoll->epollfd.poll, fd, event.events, (union poll_fd_info) event.data);\n    } else {\n        return poll_mod_fd(epoll->epollfd.poll, fd, event.events, (union poll_fd_info) event.data);\n    }\n}\n\nstruct epoll_context {\n    struct epoll_event_ *events;\n    int n;\n    int max_events;\n};\n\nstatic int epoll_callback(void *context, int types, union poll_fd_info info) {\n    struct epoll_context *c = context;\n    if (c->n >= c->max_events)\n        return 0;\n    c->events[c->n++] = (struct epoll_event_) {.events = types, .data = info.num};\n    return 1;\n}\n\nint_t sys_epoll_wait(fd_t epoll_f, addr_t events_addr, int_t max_events, int_t timeout) {\n    STRACE(\"epoll_wait(%d, %#x, %d, %d)\", epoll_f, events_addr, max_events, timeout);\n    struct fd *epoll = f_get(epoll_f);\n    if (epoll == NULL)\n        return _EBADF;\n    if (epoll->ops != &epoll_ops)\n        return _EINVAL;\n\n    struct timespec timeout_ts;\n    if (timeout >= 0) {\n        timeout_ts.tv_sec = timeout / 1000;\n        timeout_ts.tv_nsec = (timeout % 1000) * 1000000;\n    }\n    if (max_events <= 0)\n        return _EINVAL;\n    struct epoll_event_ events[max_events];\n\n    struct epoll_context context = {.events = events, .n = 0, .max_events = max_events};\n    STRACE(\"...\\n\");\n    int res = poll_wait(epoll->epollfd.poll, epoll_callback, &context, timeout < 0 ? NULL : &timeout_ts);\n    STRACE(\"%d end epoll_wait\", current->pid);\n    if (res >= 0) {\n        for (int i = 0; i < res; i++) {\n            STRACE(\" {events: %#x, data: %#x}\", events[i].events, events[i].data);\n        }\n        if (user_write(events_addr, events, sizeof(struct epoll_event_) * res))\n            return _EFAULT;\n    }\n    return res;\n}\n\nint_t sys_epoll_pwait(fd_t epoll_f, addr_t events_addr, int_t max_events, int_t timeout, addr_t sigmask_addr, dword_t sigsetsize) {\n    sigset_t_ mask;\n    if (sigmask_addr != 0) {\n        if (sigsetsize != sizeof(sigset_t_))\n            return _EINVAL;\n        if (user_get(sigmask_addr, mask))\n            return _EFAULT;\n        sigmask_set_temp(mask);\n    }\n\n    return sys_epoll_wait(epoll_f, events_addr, max_events, timeout);\n}\n\nstatic int epoll_close(struct fd *fd) {\n    poll_destroy(fd->epollfd.poll);\n    return 0;\n}\n\nstatic struct fd_ops epoll_ops = {\n    .close = epoll_close,\n};\n"
  },
  {
    "path": "kernel/errno.c",
    "content": "#include \"debug.h\"\n#include \"kernel/task.h\"\n#include \"kernel/signal.h\"\n#include \"kernel/errno.h\"\n\nint err_map(int err) {\n#define ERRCASE(err) \\\n        case err: return _##err;\n    switch (err) {\n        ERRCASE(EPERM)\n        ERRCASE(ENOENT)\n        ERRCASE(ESRCH)\n        ERRCASE(EINTR)\n        ERRCASE(EIO)\n        ERRCASE(ENXIO)\n        ERRCASE(E2BIG)\n        ERRCASE(ENOEXEC)\n        ERRCASE(EBADF)\n        ERRCASE(ECHILD)\n        ERRCASE(EAGAIN)\n        ERRCASE(ENOMEM)\n        ERRCASE(EACCES)\n        ERRCASE(EFAULT)\n        ERRCASE(ENOTBLK)\n        ERRCASE(EBUSY)\n        ERRCASE(EEXIST)\n        ERRCASE(EXDEV)\n        ERRCASE(ENODEV)\n        ERRCASE(ENOTDIR)\n        ERRCASE(EISDIR)\n        ERRCASE(EINVAL)\n        ERRCASE(ENFILE)\n        ERRCASE(EMFILE)\n        ERRCASE(ENOTTY)\n        ERRCASE(ETXTBSY)\n        ERRCASE(EFBIG)\n        ERRCASE(ENOSPC)\n        ERRCASE(ESPIPE)\n        ERRCASE(EROFS)\n        ERRCASE(EMLINK)\n        ERRCASE(EPIPE)\n        ERRCASE(EDOM)\n        ERRCASE(ERANGE)\n        ERRCASE(EDEADLK)\n        ERRCASE(ENAMETOOLONG)\n        ERRCASE(ENOLCK)\n        ERRCASE(ENOSYS)\n        ERRCASE(ENOTEMPTY)\n        ERRCASE(ELOOP)\n        ERRCASE(ENOSTR)\n        ERRCASE(ENODATA)\n        ERRCASE(ETIME)\n        ERRCASE(ENOSR)\n        ERRCASE(EREMOTE)\n        ERRCASE(ENOLINK)\n        ERRCASE(EPROTO)\n        ERRCASE(EMULTIHOP)\n        ERRCASE(EBADMSG)\n        ERRCASE(EOVERFLOW)\n        ERRCASE(EILSEQ)\n        ERRCASE(EUSERS)\n        ERRCASE(ENOTSOCK)\n        ERRCASE(EDESTADDRREQ)\n        ERRCASE(EMSGSIZE)\n        ERRCASE(EPROTOTYPE)\n        ERRCASE(ENOPROTOOPT)\n        ERRCASE(EPROTONOSUPPORT)\n        ERRCASE(ESOCKTNOSUPPORT)\n        ERRCASE(EOPNOTSUPP)\n#if EOPNOTSUPP != ENOTSUP\n        ERRCASE(ENOTSUP)\n#endif\n        ERRCASE(EPFNOSUPPORT)\n        ERRCASE(EAFNOSUPPORT)\n        ERRCASE(EADDRINUSE)\n        ERRCASE(EADDRNOTAVAIL)\n        ERRCASE(ENETDOWN)\n        ERRCASE(ENETUNREACH)\n        ERRCASE(ENETRESET)\n        ERRCASE(ECONNABORTED)\n        ERRCASE(ECONNRESET)\n        ERRCASE(ENOBUFS)\n        ERRCASE(EISCONN)\n        ERRCASE(ENOTCONN)\n        ERRCASE(ESHUTDOWN)\n        ERRCASE(ETOOMANYREFS)\n        ERRCASE(ETIMEDOUT)\n        ERRCASE(ECONNREFUSED)\n        ERRCASE(EHOSTDOWN)\n        ERRCASE(EHOSTUNREACH)\n        ERRCASE(EALREADY)\n        ERRCASE(EINPROGRESS)\n        ERRCASE(ESTALE)\n        ERRCASE(EDQUOT)\n    }\n#undef ERRCASE\n    printk(\"unknown error %d\\n\", err);\n    return -(err | 0x1000);\n}\n\nint errno_map() {\n    if (errno == EPIPE)\n        send_signal(current, SIGPIPE_, SIGINFO_NIL);\n    return err_map(errno);\n}\n"
  },
  {
    "path": "kernel/errno.h",
    "content": "#ifndef SYS_ERRNO_H\n#define SYS_ERRNO_H\n\n#include <errno.h>\n\n// stolen from asm-generic/errno-base.h\n\n#define _EPERM          -1 /* Operation not permitted */\n#define _ENOENT         -2 /* No such file or directory */\n#define _ESRCH          -3 /* No such process */\n#define _EINTR          -4 /* Interrupted system call */\n#define _EIO            -5 /* I/O error */\n#define _ENXIO          -6 /* No such device or address */\n#define _E2BIG          -7 /* That's what she said */\n#define _ENOEXEC        -8 /* Exec format error */\n#define _EBADF          -9 /* Bad file number */\n#define _ECHILD        -10 /* No child processes */\n#define _EAGAIN        -11 /* Try again */\n#define _ENOMEM        -12 /* Out of memory */\n#define _EACCES        -13 /* Permission denied */\n#define _EFAULT        -14 /* Bad address */\n#define _ENOTBLK       -15 /* Block device required */\n#define _EBUSY         -16 /* Device or resource busy */\n#define _EEXIST        -17 /* File exists */\n#define _EXDEV         -18 /* Cross-device link */\n#define _ENODEV        -19 /* No such device */\n#define _ENOTDIR       -20 /* Not a directory */\n#define _EISDIR        -21 /* Is a directory */\n#define _EINVAL        -22 /* Invalid argument */\n#define _ENFILE        -23 /* File table overflow */\n#define _EMFILE        -24 /* Too many open files */\n#define _ENOTTY        -25 /* Not a typewriter */\n#define _ETXTBSY       -26 /* Text file busy */\n#define _EFBIG         -27 /* File too large */\n#define _ENOSPC        -28 /* No space left on device */\n#define _ESPIPE        -29 /* Illegal seek */\n#define _EROFS         -30 /* Read-only file system */\n#define _EMLINK        -31 /* Too many links */\n#define _EPIPE         -32 /* Broken pipe */\n#define _EDOM          -33 /* Math argument out of domain of func */\n#define _ERANGE        -34 /* Math result not representable */\n#define _EDEADLK       -35 /* Resource deadlock would occur */\n#define _ENAMETOOLONG  -36 /* File name too long */\n#define _ENOLCK        -37 /* No record locks available */\n#define _ENOSYS        -38 /* Invalid system call number */\n#define _ENOTEMPTY     -39 /* Directory not empty */\n#define _ELOOP         -40 /* Too many symbolic links encountered */\n\n#define _EBFONT        -59 /* Bad font file format */\n#define _ENOSTR        -60 /* Device not a stream */\n#define _ENODATA       -61 /* No data available */\n#define _ETIME         -62 /* Timer expired */\n#define _ENOSR         -63 /* Out of streams resources */\n#define _ENONET        -64 /* Machine is not on the network */\n#define _ENOPKG        -65 /* Package not installed */\n#define _EREMOTE       -66 /* Object is remote */\n#define _ENOLINK       -67 /* Link has been severed */\n#define _EADV          -68 /* Advertise error */\n#define _ESRMNT        -69 /* Srmount error */\n#define _ECOMM         -70 /* Communication error on send */\n#define _EPROTO        -71 /* Protocol error */\n#define _EMULTIHOP     -72 /* Multihop attempted */\n#define _EDOTDOT       -73 /* RFS specific error */\n#define _EBADMSG       -74 /* Not a data message */\n#define _EOVERFLOW     -75 /* Value too large for defined data type */\n#define _ENOTUNIQ      -76 /* Name not unique on network */\n#define _EBADFD        -77 /* File descriptor in bad state */\n#define _EREMCHG       -78 /* Remote address changed */\n#define _ELIBACC       -79 /* Can not access a needed shared library */\n#define _ELIBBAD       -80 /* Accessing a corrupted shared library */\n#define _ELIBSCN       -81 /* .lib section in a.out corrupted */\n#define _ELIBMAX       -82 /* Attempting to link in too many shared libraries */\n#define _ELIBEXEC      -83 /* Cannot exec a shared library directly */\n#define _EILSEQ        -84 /* Illegal byte sequence */\n#define _ERESTART      -85 /* Interrupted system call should be restarted */\n#define _ESTRPIPE      -86 /* Streams pipe error */\n#define _EUSERS        -87 /* Too many users */\n#define _ENOTSOCK      -88 /* Socket operation on non-socket */\n#define _EDESTADDRREQ  -89 /* Destination address required */\n#define _EMSGSIZE      -90 /* Message too long */\n#define _EPROTOTYPE    -91 /* Protocol wrong type for socket */\n#define _ENOPROTOOPT   -92 /* Protocol not available */\n#define _EPROTONOSUPPORT   -93 /* Protocol not supported */\n#define _ESOCKTNOSUPPORT   -94 /* Socket type not supported */\n#define _EOPNOTSUPP    -95 /* Operation not supported on transport endpoint */\n#define _ENOTSUP _EOPNOTSUPP\n#define _EPFNOSUPPORT  -96 /* Protocol family not supported */\n#define _EAFNOSUPPORT  -97 /* Address family not supported by protocol */\n#define _EADDRINUSE    -98 /* Address already in use */\n#define _EADDRNOTAVAIL -99 /* Cannot assign requested address */\n#define _ENETDOWN     -100 /* Network is down */\n#define _ENETUNREACH  -101 /* Network is unreachable */\n#define _ENETRESET    -102 /* Network dropped connection because of reset */\n#define _ECONNABORTED -103 /* Software caused connection abort */\n#define _ECONNRESET   -104 /* Connection reset by peer */\n#define _ENOBUFS      -105 /* No buffer space available */\n#define _EISCONN      -106 /* Transport endpoint is already connected */\n#define _ENOTCONN     -107 /* Transport endpoint is not connected */\n#define _ESHUTDOWN    -108 /* Cannot send after transport endpoint shutdown */\n#define _ETOOMANYREFS -109 /* Too many references: cannot splice */\n#define _ETIMEDOUT    -110 /* Connection timed out */\n#define _ECONNREFUSED -111 /* Connection refused */\n#define _EHOSTDOWN    -112 /* Host is down */\n#define _EHOSTUNREACH -113 /* No route to host */\n#define _EALREADY     -114 /* Operation already in progress */\n#define _EINPROGRESS  -115 /* Operation now in progress */\n#define _ESTALE       -116 /* Stale file handle */\n#define _EUCLEAN      -117 /* Structure needs cleaning */\n#define _ENOTNAM      -118 /* Not a XENIX named type file */\n#define _ENAVAIL      -119 /* No XENIX semaphores available */\n#define _EISNAM       -120 /* Is a named type file */\n#define _EREMOTEIO    -121 /* Remote I/O error */\n#define _EDQUOT       -122 /* Quota exceeded */\n#define _ECANCELED    -125 /* Operation Canceled */\n\n\nint err_map(int err);\nint errno_map(void);\n\n#endif\n"
  },
  {
    "path": "kernel/eventfd.c",
    "content": "#include \"kernel/calls.h\"\n#include \"kernel/fs.h\"\n#include \"fs/poll.h\"\n\nstatic struct fd_ops eventfd_ops;\n\nint_t sys_eventfd2(uint_t initval, int_t flags) {\n    STRACE(\"eventfd(%d, %#x)\", initval, flags);\n    if (flags & ~(O_CLOEXEC_|O_NONBLOCK_))\n        return _EINVAL;\n\n    struct fd *fd = adhoc_fd_create(&eventfd_ops);\n    if (fd == NULL)\n        return _ENOMEM;\n    fd->eventfd.val = initval;\n    return f_install(fd, flags);\n}\nint_t sys_eventfd(uint_t initval) {\n    return sys_eventfd2(initval, 0);\n}\n\nstatic ssize_t eventfd_read(struct fd *fd, void *buf, size_t bufsize) {\n    if (bufsize < sizeof(uint64_t))\n        return _EINVAL;\n\n    lock(&fd->lock);\n    while (fd->eventfd.val == 0) {\n        if (fd->flags & O_NONBLOCK_) {\n            unlock(&fd->lock);\n            return _EAGAIN;\n        }\n        if (wait_for(&fd->cond, &fd->lock, NULL)) {\n            unlock(&fd->lock);\n            return _EINTR;\n        }\n    }\n\n    *(uint64_t *) buf = fd->eventfd.val;\n    fd->eventfd.val = 0;\n    notify(&fd->cond);\n    unlock(&fd->lock);\n    poll_wakeup(fd, POLL_WRITE);\n    return sizeof(uint64_t);\n}\n\nstatic ssize_t eventfd_write(struct fd *fd, const void *buf, size_t bufsize) {\n    if (bufsize < sizeof(uint64_t))\n        return _EINVAL;\n    uint64_t increment = *(uint64_t *) buf;\n    if (increment == UINT64_MAX)\n        return _EINVAL;\n\n    lock(&fd->lock);\n    while (fd->eventfd.val >= UINT64_MAX - increment) {\n        if (fd->flags & O_NONBLOCK_) {\n            unlock(&fd->lock);\n            return _EAGAIN;\n        }\n        if (wait_for(&fd->cond, &fd->lock, NULL)) {\n            unlock(&fd->lock);\n            return _EINTR;\n        }\n    }\n\n    fd->eventfd.val += increment;\n    notify(&fd->cond);\n    unlock(&fd->lock);\n    poll_wakeup(fd, POLL_READ);\n    return sizeof(uint64_t);\n}\n\nstatic int eventfd_poll(struct fd *fd) {\n    lock(&fd->lock);\n    int types = 0;\n    if (fd->eventfd.val > 0)\n        types |= POLL_READ;\n    if (fd->eventfd.val < UINT64_MAX - 1)\n        types |= POLL_WRITE;\n    unlock(&fd->lock);\n    return types;\n}\n\nstatic struct fd_ops eventfd_ops = {\n    .read = eventfd_read,\n    .write = eventfd_write,\n    .poll = eventfd_poll,\n};\n"
  },
  {
    "path": "kernel/exec.c",
    "content": "#include \"kernel/signal.h\"\n#include \"task.h\"\n#define _GNU_SOURCE\n#include <unistd.h>\n#include <fcntl.h>\n#include <pthread.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"misc.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/random.h\"\n#include \"kernel/errno.h\"\n#include \"fs/fd.h\"\n#include \"kernel/elf.h\"\n#include \"kernel/vdso.h\"\n#include \"tools/ptraceomatic-config.h\"\n\n#define ARGV_MAX 32 * PAGE_SIZE\n\nstruct exec_args {\n    // number of arguments\n    size_t count;\n    // series of count null-terminated strings, plus an extra null for good measure\n    const char *args;\n};\n\nstatic inline dword_t align_stack(dword_t sp);\nstatic inline ssize_t user_strlen(dword_t p);\nstatic inline int user_memset(addr_t start, byte_t val, dword_t len);\nstatic inline dword_t copy_string(dword_t sp, const char *string);\nstatic inline dword_t args_copy(dword_t sp, struct exec_args args);\nstatic size_t args_size(struct exec_args args);\n\nstatic int read_header(struct fd *fd, struct elf_header *header) {\n    int err;\n    if (fd->ops->lseek(fd, 0, SEEK_SET))\n        return _EIO;\n    if ((err = fd->ops->read(fd, header, sizeof(*header))) != sizeof(*header)) {\n        if (err < 0)\n            return _EIO;\n        return _ENOEXEC;\n    }\n    if (memcmp(&header->magic, ELF_MAGIC, sizeof(header->magic)) != 0\n            || (header->type != ELF_EXECUTABLE && header->type != ELF_DYNAMIC)\n            || header->bitness != ELF_32BIT\n            || header->endian != ELF_LITTLEENDIAN\n            || header->elfversion1 != 1\n            || header->machine != ELF_X86)\n        return _ENOEXEC;\n    return 0;\n}\n\nstatic int read_prg_headers(struct fd *fd, struct elf_header header, struct prg_header **ph_out) {\n    ssize_t ph_size = sizeof(struct prg_header) * header.phent_count;\n    struct prg_header *ph = malloc(ph_size);\n    if (ph == NULL)\n        return _ENOMEM;\n\n    if (fd->ops->lseek(fd, header.prghead_off, SEEK_SET) < 0) {\n        free(ph);\n        return _EIO;\n    }\n    if (fd->ops->read(fd, ph, ph_size) != ph_size) {\n        free(ph);\n        if (errno != 0)\n            return _EIO;\n        return _ENOEXEC;\n    }\n\n    *ph_out = ph;\n    return 0;\n}\n\nstatic int load_entry(struct prg_header ph, addr_t bias, struct fd *fd) {\n    int err;\n\n    addr_t addr = ph.vaddr + bias;\n    addr_t offset = ph.offset;\n    addr_t memsize = ph.memsize;\n    addr_t filesize = ph.filesize;\n\n    int flags = P_READ;\n    if (ph.flags & PH_W) flags |= P_WRITE;\n\n    if ((err = fd->ops->mmap(fd, current->mem, PAGE(addr),\n                    PAGE_ROUND_UP(filesize + PGOFFSET(addr)),\n                    offset - PGOFFSET(addr), flags, MMAP_PRIVATE)) < 0)\n        return err;\n    // TODO find a better place for these to avoid code duplication\n    mem_pt(current->mem, PAGE(addr))->data->fd = fd_retain(fd);\n    mem_pt(current->mem, PAGE(addr))->data->file_offset = offset - PGOFFSET(addr);\n\n    if (memsize > filesize) {\n        // put zeroes between addr + filesize and addr + memsize, call that bss\n        dword_t bss_size = memsize - filesize;\n\n        // first zero the tail from the end of the file mapping to the end\n        // of the load entry or the end of the page, whichever comes first\n        addr_t file_end = addr + filesize;\n        dword_t tail_size = PAGE_SIZE - PGOFFSET(file_end);\n        if (tail_size == PAGE_SIZE)\n            // if you can calculate tail_size better and not have to do this please let me know\n            tail_size = 0;\n\n        if (tail_size != 0) {\n            // Unlock and lock the mem because the user functions must be\n            // called without locking mem.\n            write_wrunlock(&current->mem->lock);\n            user_memset(file_end, 0, tail_size);\n            write_wrlock(&current->mem->lock);\n        }\n        if (tail_size > bss_size)\n            tail_size = bss_size;\n\n        // then map the pages from after the file mapping up to and including the end of bss\n        if (bss_size - tail_size != 0)\n            if ((err = pt_map_nothing(current->mem, PAGE_ROUND_UP(addr + filesize),\n                    PAGE_ROUND_UP(bss_size - tail_size), flags)) < 0)\n                return err;\n    }\n    return 0;\n}\n\nstatic addr_t find_hole_for_elf(struct elf_header *header, struct prg_header *ph) {\n    struct prg_header *first = NULL, *last = NULL;\n    for (int i = 0; i < header->phent_count; i++) {\n        if (ph[i].type == PT_LOAD) {\n            if (first == NULL)\n                first = &ph[i];\n            last = &ph[i];\n        }\n    }\n    pages_t size = 0;\n    if (first != NULL) {\n        pages_t a = PAGE_ROUND_UP(last->vaddr + last->memsize);\n        pages_t b = PAGE(first->vaddr);\n        size = a - b;\n    }\n    return pt_find_hole(current->mem, size) << PAGE_BITS;\n}\n\nstatic int elf_exec(struct fd *fd, const char *file, struct exec_args argv, struct exec_args envp) {\n    int err = 0;\n\n    // read the headers\n    struct elf_header header;\n    if ((err = read_header(fd, &header)) < 0)\n        return err;\n    struct prg_header *ph;\n    if ((err = read_prg_headers(fd, header, &ph)) < 0)\n        return err;\n\n    // look for an interpreter\n    char *interp_name = NULL;\n    struct fd *interp_fd = NULL;\n    struct elf_header interp_header;\n    struct prg_header *interp_ph = NULL;\n    for (unsigned i = 0; i < header.phent_count; i++) {\n        if (ph[i].type != PT_INTERP)\n            continue;\n        if (interp_name) {\n            // can't have two interpreters\n            err = _EINVAL;\n            goto out_free_interp;\n        }\n\n        interp_name = malloc(ph[i].filesize);\n        err = _ENOMEM;\n        if (interp_name == NULL)\n            goto out_free_ph;\n\n        // read the interpreter name out of the file\n        err = _EIO;\n        if (fd->ops->lseek(fd, ph[i].offset, SEEK_SET) < 0)\n            goto out_free_interp;\n        if (fd->ops->read(fd, interp_name, ph[i].filesize) != ph[i].filesize)\n            goto out_free_interp;\n\n        // open interpreter and read headers\n        interp_fd = generic_open(interp_name, O_RDONLY, 0);\n        if (IS_ERR(interp_fd)) {\n            err = PTR_ERR(interp_fd);\n            goto out_free_interp;\n        }\n        if ((err = read_header(interp_fd, &interp_header)) < 0) {\n            if (err == _ENOEXEC) err = _ELIBBAD;\n            goto out_free_interp;\n        }\n        if ((err = read_prg_headers(interp_fd, interp_header, &interp_ph)) < 0) {\n            if (err == _ENOEXEC) err = _ELIBBAD;\n            goto out_free_interp;\n        }\n    }\n\n    // free the process's memory.\n    // from this point on, if any error occurs the process will have to be\n    // killed before it even starts. please don't be too sad about it, it's\n    // just a process.\n    //\n    // general_lock protects current->mm. otherwise procfs might read the\n    // pointer before it's released and then try to lock it after it's\n    // released.\n    lock(&current->general_lock);\n    mm_release(current->mm);\n    task_set_mm(current, mm_new());\n    unlock(&current->general_lock);\n    write_wrlock(&current->mem->lock);\n\n    current->mm->exefile = fd_retain(fd);\n\n    addr_t load_addr = 0; // used for AX_PHDR\n    bool load_addr_set = false;\n    addr_t bias = 0; // offset for loading shared libraries as executables\n\n    // map dat shit!\n    for (unsigned i = 0; i < header.phent_count; i++) {\n        if (ph[i].type != PT_LOAD)\n            continue;\n\n        if (!load_addr_set && header.type == ELF_DYNAMIC) {\n            // see giant comment in linux/fs/binfmt_elf.c, around line 950\n            if (interp_name)\n                bias = 0x56555000; // I have no idea how this number was arrived at\n            else\n                bias = find_hole_for_elf(&header, ph);\n        }\n\n        if ((err = load_entry(ph[i], bias, fd)) < 0)\n            goto beyond_hope;\n\n        // load_addr is used to get a value for AX_PHDR et al\n        if (!load_addr_set) {\n            load_addr = bias + ph[i].vaddr - ph[i].offset;\n            load_addr_set = true;\n        }\n\n        // we have to know where the brk starts\n        addr_t brk = bias + ph[i].vaddr + ph[i].memsize;\n        if (brk > current->mm->start_brk)\n            current->mm->start_brk = current->mm->brk = BYTES_ROUND_UP(brk);\n    }\n\n    addr_t entry = bias + header.entry_point;\n    addr_t interp_base = 0;\n\n    if (interp_name) {\n        // map dat shit! interpreter edition\n        interp_base = find_hole_for_elf(&interp_header, interp_ph);\n        for (int i = interp_header.phent_count - 1; i >= 0; i--) {\n            if (interp_ph[i].type != PT_LOAD)\n                continue;\n            if ((err = load_entry(interp_ph[i], interp_base, interp_fd)) < 0)\n                goto beyond_hope;\n        }\n        entry = interp_base + interp_header.entry_point;\n    }\n\n    // map vdso\n    err = _ENOMEM;\n    pages_t vdso_pages = sizeof(vdso_data) >> PAGE_BITS;\n    // FIXME disgusting hack: musl's dynamic linker has a one-page hole, and\n    // I'd rather not put the vdso in that hole. so find a two-page hole and\n    // add one.\n    page_t vdso_page = pt_find_hole(current->mem, vdso_pages + 1);\n    if (vdso_page == BAD_PAGE)\n        goto beyond_hope;\n    vdso_page += 1;\n    if ((err = pt_map(current->mem, vdso_page, vdso_pages, (void *) vdso_data, 0, 0)) < 0)\n        goto beyond_hope;\n    mem_pt(current->mem, vdso_page)->data->name = \"[vdso]\";\n    current->mm->vdso = vdso_page << PAGE_BITS;\n    addr_t vdso_entry = current->mm->vdso + ((struct elf_header *) vdso_data)->entry_point;\n\n    // map 3 empty \"vvar\" pages to satisfy ptraceomatic\n    page_t vvar_page = pt_find_hole(current->mem, VVAR_PAGES);\n    if (vvar_page == BAD_PAGE)\n        goto beyond_hope;\n    if ((err = pt_map_nothing(current->mem, vvar_page, VVAR_PAGES, 0)) < 0)\n        goto beyond_hope;\n    mem_pt(current->mem, vvar_page)->data->name = \"[vvar]\";\n\n    // STACK TIME!\n\n    // allocate 1 page of stack at 0xffffd, and let it grow down\n    if ((err = pt_map_nothing(current->mem, 0xffffd, 1, P_WRITE | P_GROWSDOWN)) < 0)\n        goto beyond_hope;\n    // that was the last memory mapping\n    write_wrunlock(&current->mem->lock);\n    dword_t sp = 0xffffe000;\n    // on 32-bit linux, there's 4 empty bytes at the very bottom of the stack.\n    // on 64-bit linux, there's 8. make ptraceomatic happy. (a major theme in this file)\n    sp -= sizeof(void *);\n\n    err = _EFAULT;\n    // first, copy stuff pointed to by argv/envp/auxv\n    // filename, argc, argv\n    addr_t file_addr = sp = copy_string(sp, file);\n    if (sp == 0)\n        goto beyond_hope;\n    addr_t envp_addr = sp = args_copy(sp, envp);\n    if (sp == 0)\n        goto beyond_hope;\n    current->mm->argv_end = sp;\n    addr_t argv_addr = sp = args_copy(sp, argv);\n    if (sp == 0)\n        goto beyond_hope;\n    current->mm->argv_start = sp;\n    sp = align_stack(sp);\n\n    addr_t platform_addr = sp = copy_string(sp, \"i686\");\n    if (sp == 0)\n        goto beyond_hope;\n    // 16 random bytes so no system call is needed to seed a userspace RNG\n    char random[16] = {};\n    get_random(random, sizeof(random)); // if this fails, eh, no one's really using it\n    addr_t random_addr = sp -= sizeof(random);\n    if (user_put(sp, random))\n        goto beyond_hope;\n\n    // the way linux aligns the stack at this point is kinda funky\n    // calculate how much space is needed for argv, envp, and auxv, subtract\n    // that from sp, then align, then copy argv/envp/auxv from that down\n\n    // declare elf aux now so we can know how big it is\n    struct aux_ent aux[] = {\n        {AX_SYSINFO, vdso_entry},\n        {AX_SYSINFO_EHDR, current->mm->vdso},\n        {AX_HWCAP, 0x00000000}, // suck that\n        {AX_PAGESZ, PAGE_SIZE},\n        {AX_CLKTCK, 0x64},\n        {AX_PHDR, load_addr + header.prghead_off},\n        {AX_PHENT, sizeof(struct prg_header)},\n        {AX_PHNUM, header.phent_count},\n        {AX_BASE, interp_base},\n        {AX_FLAGS, 0},\n        {AX_ENTRY, bias + header.entry_point},\n        {AX_UID, 0},\n        {AX_EUID, 0},\n        {AX_GID, 0},\n        {AX_EGID, 0},\n        {AX_SECURE, 0},\n        {AX_RANDOM, random_addr},\n        {AX_HWCAP2, 0}, // suck that too\n        {AX_EXECFN, file_addr},\n        {AX_PLATFORM, platform_addr},\n        {0, 0}\n    };\n    sp -= ((argv.count + 1) + (envp.count + 1) + 1) * sizeof(dword_t);\n    sp -= sizeof(aux);\n    sp &=~ 0xf;\n\n    // now copy down, start using p so sp is preserved\n    addr_t p = sp;\n\n    // argc\n    if (user_put(p, argv.count))\n        return _EFAULT;\n    p += sizeof(dword_t);\n\n    // argv\n    size_t argc = argv.count;\n    while (argc-- > 0) {\n        if (user_put(p, argv_addr))\n            return _EFAULT;\n        argv_addr += user_strlen(argv_addr) + 1;\n        p += sizeof(dword_t); // null terminator\n    }\n    p += sizeof(dword_t); // null terminator\n\n    // envp\n    size_t envc = envp.count;\n    while (envc-- > 0) {\n        if (user_put(p, envp_addr))\n            return _EFAULT;\n        envp_addr += user_strlen(envp_addr) + 1;\n        p += sizeof(dword_t);\n    }\n    p += sizeof(dword_t); // null terminator\n\n    // copy auxv\n    current->mm->auxv_start = p;\n    if (user_put(p, aux))\n        goto beyond_hope;\n    p += sizeof(aux);\n    current->mm->auxv_end = p;\n\n    current->mm->stack_start = sp;\n    current->cpu.esp = sp;\n    current->cpu.eip = entry;\n    current->cpu.fcw = 0x37f;\n\n    // This code was written when I discovered that the glibc entry point\n    // interprets edx as the address of a function to call on exit, as\n    // specified in the ABI. This register is normally set by the dynamic\n    // linker, so everything works fine until you run a static executable.\n    current->cpu.eax = 0;\n    current->cpu.ebx = 0;\n    current->cpu.ecx = 0;\n    current->cpu.edx = 0;\n    current->cpu.esi = 0;\n    current->cpu.edi = 0;\n    current->cpu.ebp = 0;\n    collapse_flags(&current->cpu);\n    current->cpu.eflags = 0;\n\n    err = 0;\nout_free_interp:\n    if (interp_name != NULL)\n        free(interp_name);\n    if (interp_fd != NULL && !IS_ERR(interp_fd))\n        fd_close(interp_fd);\n    if (interp_ph != NULL)\n        free(interp_ph);\nout_free_ph:\n    free(ph);\n    return err;\n\nbeyond_hope:\n    // TODO force sigsegv\n    write_wrunlock(&current->mem->lock);\n    goto out_free_interp;\n}\n\nstatic size_t args_size(struct exec_args args) {\n    const char *args_end = args.args;\n    for (size_t i = 0; i < args.count; i++) {\n        args_end += strlen(args_end) + 1;\n    }\n    // don't forget the very last null terminator\n    assert(args_end[0] == '\\0');\n    args_end++;\n    return args_end - args.args;\n}\n\nstatic inline dword_t align_stack(addr_t sp) {\n    return sp &~ 0xf;\n}\n\nstatic inline dword_t copy_string(addr_t sp, const char *string) {\n    sp -= strlen(string) + 1;\n    if (user_write_string(sp, string))\n        return 0;\n    return sp;\n}\n\nstatic inline dword_t args_copy(addr_t sp, struct exec_args args) {\n    size_t size = args_size(args);\n    sp -= size;\n    if (user_write(sp, args.args, size))\n        return 0;\n    return sp;\n}\n\nstatic inline ssize_t user_strlen(addr_t p) {\n    size_t i = 0;\n    char c;\n    do {\n        if (user_get(p + i, c))\n            return -1;\n        i++;\n    } while (c != '\\0');\n    return i - 1;\n}\n\nstatic inline int user_memset(addr_t start, byte_t val, dword_t len) {\n    while (len--)\n        if (user_put(start++, val))\n            return 1;\n    return 0;\n}\n\nstatic int format_exec(struct fd *fd, const char *file, struct exec_args argv, struct exec_args envp) {\n    int err = elf_exec(fd, file, argv, envp);\n    if (err != _ENOEXEC)\n        return err;\n    // other formats would go here\n    return _ENOEXEC;\n}\n\nstatic int shebang_exec(struct fd *fd, const char *file, struct exec_args argv, struct exec_args envp) {\n    // read the first 128 bytes to get the shebang line out of\n    if (fd->ops->lseek(fd, 0, SEEK_SET))\n        return _EIO;\n    char header[128];\n    int size = fd->ops->read(fd, header, sizeof(header) - 1);\n    if (size < 0)\n        return _EIO;\n    header[size] = '\\0';\n\n    // only look at the first line\n    char *newline = strchr(header, '\\n');\n    if (newline == NULL)\n        return _ENOEXEC;\n    *newline = '\\0';\n\n    // format: #![spaces]interpreter[spaces]argument[spaces]\n    char *p = header;\n    if (p[0] != '#' || p[1] != '!')\n        return _ENOEXEC;\n    p += 2;\n    while (*p == ' ')\n        p++;\n    if (*p == '\\0')\n        return _ENOEXEC;\n\n    char *interpreter = p;\n    while (*p != ' ' && *p != '\\0')\n        p++;\n    if (*p != '\\0') {\n        *p++ = '\\0';\n        while (*p == ' ')\n            p++;\n    }\n\n    char *argument = p;\n    // strip trailing whitespace\n    p = strchr(p, '\\0') - 1;\n    while (*p == ' ')\n        *p-- = '\\0';\n    if (*argument == '\\0')\n        argument = NULL;\n\n    struct exec_args argv_rest = {\n        .count = argv.count - 1,\n        .args = argv.args + strlen(argv.args) + 1,\n    };\n    size_t args_rest_size = args_size(argv_rest);\n    size_t extra_args_size = strlen(interpreter) + 1 + strlen(file) + 1;\n    if (argument)\n        extra_args_size += strlen(argument) + 1;\n    if (args_rest_size + extra_args_size >= ARGV_MAX)\n        return _E2BIG;\n\n    char new_argv_buf[ARGV_MAX];\n    struct exec_args new_argv = {.args = new_argv_buf};\n    size_t n = 0;\n    strcpy(new_argv_buf, interpreter);\n    new_argv.count++;\n    n += strlen(interpreter) + 1;\n    if (argument) {\n        strcpy(new_argv_buf + n, argument);\n        new_argv.count++;\n        n += strlen(argument) + 1;\n    }\n    strcpy(new_argv_buf + n, file);\n    n += strlen(file) + 1;\n    new_argv.count++;\n    memcpy(new_argv_buf + n, argv_rest.args, args_rest_size);\n    new_argv.count += argv_rest.count;\n\n    struct fd *interpreter_fd = generic_open(interpreter, O_RDONLY_, 0);\n    if (IS_ERR(interpreter_fd))\n        return PTR_ERR(interpreter_fd);\n    int err = format_exec(interpreter_fd, interpreter, new_argv, envp);\n    fd_close(interpreter_fd);\n    return err;\n}\n\nint __do_execve(const char *file, struct exec_args argv, struct exec_args envp) {\n    struct fd *fd = generic_open(file, O_RDONLY, 0);\n    if (IS_ERR(fd))\n        return PTR_ERR(fd);\n\n    struct statbuf stat;\n    int err = fd->mount->fs->fstat(fd, &stat);\n    if (err < 0) {\n        fd_close(fd);\n        return err;\n    }\n\n    // if nobody has permission to execute, it should be safe to not execute\n    if (!(stat.mode & 0111)) {\n        fd_close(fd);\n        return _EACCES;\n    }\n\n    err = format_exec(fd, file, argv, envp);\n    if (err == _ENOEXEC)\n        err = shebang_exec(fd, file, argv, envp);\n    fd_close(fd);\n    if (err < 0)\n        return err;\n\n    // setuid/setgid\n    if (stat.mode & S_ISUID) {\n        current->suid = current->euid;\n        current->euid = stat.uid;\n    }\n    if (stat.mode & S_ISGID) {\n        current->sgid = current->egid;\n        current->egid = stat.gid;\n    }\n\n    // save current->comm\n    lock(&current->general_lock);\n    const char *basename = strrchr(file, '/');\n    if (basename == NULL)\n        basename = file;\n    else\n        basename++;\n    strncpy(current->comm, basename, sizeof(current->comm));\n    unlock(&current->general_lock);\n\n    update_thread_name();\n\n    // cloexec\n    // consider putting this in fd.c?\n    fdtable_do_cloexec(current->files);\n\n    // reset signal handlers\n    lock(&current->sighand->lock);\n    for (int sig = 0; sig < NUM_SIGS; sig++) {\n        struct sigaction_ *action = &current->sighand->action[sig];\n        if (action->handler != SIG_IGN_)\n            action->handler = SIG_DFL_;\n    }\n    current->sighand->altstack = 0;\n    unlock(&current->sighand->lock);\n\n    current->did_exec = true;\n    vfork_notify(current);\n\n    if (current->ptrace.traced) {\n        lock(&pids_lock);\n        send_signal(current, SIGTRAP_, (struct siginfo_) {\n            .code = SI_USER_,\n            .kill.pid = current->pid,\n            .kill.uid = current->uid,\n        });\n        unlock(&pids_lock);\n    }\n\n    return 0;\n}\n\nint do_execve(const char *file, size_t argc, const char *argv_p, const char *envp_p) {\n    struct exec_args argv = {.count = argc, .args = argv_p};\n    struct exec_args envp = {.args = envp_p};\n    while (*envp_p != '\\0') {\n        envp_p += strlen(envp_p) + 1;\n        envp.count++;\n    }\n    return __do_execve(file, argv, envp);\n}\n\nstatic ssize_t user_read_string_array(addr_t addr, char *buf, size_t max) {\n    size_t i = 0;\n    size_t p = 0;\n    for (;;) {\n        addr_t str_addr;\n        if (user_get(addr + i * sizeof(addr_t), str_addr))\n            return _EFAULT;\n        if (str_addr == 0)\n            break;\n        size_t str_p = 0;\n        for (;;) {\n            if (p >= max)\n                return _E2BIG;\n            if (user_get(str_addr + str_p, buf[p]))\n                return _EFAULT;\n            str_p++;\n            p++;\n            if (buf[p - 1] == '\\0')\n                break;\n        }\n        i++;\n    }\n    if (p >= max)\n        return _E2BIG;\n    buf[p] = '\\0';\n    return i;\n}\n\ndword_t sys_execve(addr_t filename_addr, addr_t argv_addr, addr_t envp_addr) {\n    char filename[MAX_PATH];\n    if (user_read_string(filename_addr, filename, sizeof(filename)))\n        return _EFAULT;\n\n    int err = _ENOMEM;\n    char *argv = malloc(ARGV_MAX);\n    if (argv == NULL)\n        goto err_free_argv;\n    ssize_t argc = user_read_string_array(argv_addr, argv, ARGV_MAX);\n    if (argc < 0) {\n        err = argc;\n        goto err_free_argv;\n    }\n\n    char *envp = malloc(ARGV_MAX);\n    if (envp == NULL)\n        goto err_free_envp;\n    if (envp_addr != 0) {\n        err = user_read_string_array(envp_addr, envp, ARGV_MAX);\n        if (err < 0)\n            goto err_free_envp;\n    } else {\n        // Do not take advantage of this nonstandard and nonportable misfeature!\n        // - Michael Kerrisk, execve(2)\n        envp[0] = envp[1] = '\\0';\n    }\n\n    STRACE(\"execve(\\\"%.1000s\\\", {\", filename);\n    const char *args = argv;\n    while (*args != '\\0') {\n        STRACE(\"\\\"%.1000s\\\", \", args);\n        args += strlen(args) + 1;\n    }\n    STRACE(\"}, {\");\n    args = envp;\n    while (*args != '\\0') {\n        STRACE(\"\\\"%.1000s\\\", \", args);\n        args += strlen(args) + 1;\n    }\n    STRACE(\"})\");\n\n    err = do_execve(filename, argc, argv, envp);\n\nerr_free_envp:\n    free(envp);\nerr_free_argv:\n    free(argv);\n    return err;\n}\n"
  },
  {
    "path": "kernel/exit.c",
    "content": "#include <pthread.h>\n#include <signal.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"kernel/mm.h\"\n#include \"kernel/futex.h\"\n#include \"kernel/ptrace.h\"\n#include \"fs/fd.h\"\n#include \"fs/tty.h\"\n\nstatic void halt_system(void);\n\nstatic bool exit_tgroup(struct task *task) {\n    struct tgroup *group = task->group;\n    list_remove(&task->group_links);\n    bool group_dead = list_empty(&group->threads);\n    if (group_dead) {\n        // don't need to lock the group since the only pointers to it come from:\n        // - other threads' current->group, but there are none left thanks to that list_empty call\n        // - locking pids_lock first, which do_exit did\n        if (group->itimer)\n            timer_free(group->itimer);\n\n        // The group will be removed from its group and session by reap_if_zombie,\n        // because fish tries to set the pgid to that of an exited but not reaped\n        // task.\n        // https://github.com/Microsoft/WSL/issues/2786\n    }\n    return group_dead;\n}\n\nvoid (*exit_hook)(struct task *task, int code) = NULL;\n\nstatic struct task *find_new_parent(struct task *task) {\n    struct task *new_parent;\n    list_for_each_entry(&task->group->threads, new_parent, group_links) {\n        if (!new_parent->exiting)\n            return new_parent;\n    }\n    return pid_get_task(1);\n}\n\nnoreturn void do_exit(int status) {\n    // has to happen before mm_release\n    addr_t clear_tid = current->clear_tid;\n    if (clear_tid) {\n        pid_t_ zero = 0;\n        if (user_put(clear_tid, zero) == 0)\n            futex_wake(clear_tid, 1);\n    }\n\n    // release all our resources\n    mm_release(current->mm);\n    current->mm = NULL;\n    fdtable_release(current->files);\n    current->files = NULL;\n    fs_info_release(current->fs);\n    current->fs = NULL;\n    // sighand must be released below so it can be protected by pids_lock\n    // since it can be accessed by other threads\n\n    // save things that our parent might be interested in\n    current->exit_code = status; // FIXME locking\n    struct rusage_ rusage = rusage_get_current();\n    lock(&current->group->lock);\n    rusage_add(&current->group->rusage, &rusage);\n    struct rusage_ group_rusage = current->group->rusage;\n    unlock(&current->group->lock);\n\n    // the actual freeing needs pids_lock\n    lock(&pids_lock);\n    current->exiting = true;\n    // release the sighand\n    sighand_release(current->sighand);\n    current->sighand = NULL;\n    struct sigqueue *sigqueue, *sigqueue_tmp;\n    list_for_each_entry_safe(&current->queue, sigqueue, sigqueue_tmp, queue) {\n        list_remove(&sigqueue->queue);\n        free(sigqueue);\n    }\n    struct task *leader = current->group->leader;\n\n    // reparent children\n    struct task *new_parent = find_new_parent(current);\n    struct task *child, *tmp;\n    list_for_each_entry_safe(&current->children, child, tmp, siblings) {\n        child->parent = new_parent;\n        list_remove(&child->siblings);\n        list_add(&new_parent->children, &child->siblings);\n    }\n\n    if (exit_tgroup(current)) {\n        // notify parent that we died\n        struct task *parent = leader->parent;\n        if (parent == NULL) {\n            // init died\n            halt_system();\n        } else {\n            leader->zombie = true;\n            notify(&parent->group->child_exit);\n            struct siginfo_ info = {\n                .code = SI_KERNEL_,\n                .child.pid = current->pid,\n                .child.uid = current->uid,\n                .child.status = current->exit_code,\n                .child.utime = clock_from_timeval(group_rusage.utime),\n                .child.stime = clock_from_timeval(group_rusage.stime),\n            };\n            if (leader->exit_signal != 0)\n                send_signal(parent, leader->exit_signal, info);\n        }\n\n        if (exit_hook != NULL)\n            exit_hook(current, status);\n    }\n\n    vfork_notify(current);\n    if (current != leader)\n        task_destroy(current);\n    unlock(&pids_lock);\n\n    pthread_exit(NULL);\n}\n\nnoreturn void do_exit_group(int status) {\n    struct tgroup *group = current->group;\n    lock(&pids_lock);\n    lock(&group->lock);\n    if (!group->doing_group_exit) {\n        group->doing_group_exit = true;\n        group->group_exit_code = status;\n    } else {\n        status = group->group_exit_code;\n    }\n\n    // kill everyone else in the group\n    struct task *task;\n    list_for_each_entry(&group->threads, task, group_links) {\n        deliver_signal(task, SIGKILL_, SIGINFO_NIL);\n        task->group->stopped = false;\n        notify(&task->group->stopped_cond);\n    }\n\n    unlock(&group->lock);\n    unlock(&pids_lock);\n    do_exit(status);\n}\n\n// always called from init process\nstatic void halt_system(void) {\n    for (int state = 0; state < 3; state++) {\n        int tasks_found = 0;\n        for (int i = 2; i < MAX_PID; i++) {\n            struct task *task = pid_get_task(i);\n            if (task != NULL) {\n                tasks_found++;\n                switch (state) {\n                case 0:\n                    deliver_signal(task, SIGTERM_, SIGINFO_NIL);\n                    break;\n                case 1:\n                    deliver_signal(task, SIGKILL_, SIGINFO_NIL);\n                    break;\n                case 2:\n                    pthread_kill(task->thread, SIGTERM);\n                }\n            }\n        }\n        if (tasks_found == 0)\n            break;\n        if (state != 2)\n            sleep(1);\n    }\n\n    // unmount all filesystems\n    lock(&mounts_lock);\n    struct mount *mount, *tmp;\n    list_for_each_entry_safe(&mounts, mount, tmp, mounts) {\n        mount_remove(mount);\n    }\n    unlock(&mounts_lock);\n}\n\ndword_t sys_exit(dword_t status) {\n    STRACE(\"exit(%d)\\n\", status);\n    do_exit(status << 8);\n}\n\ndword_t sys_exit_group(dword_t status) {\n    STRACE(\"exit_group(%d)\\n\", status);\n    do_exit_group(status << 8);\n}\n\n#define WNOHANG_ (1 << 0)\n#define WUNTRACED_ (1 << 1)\n#define WEXITED_ (1 << 2)\n#define WCONTINUED_ (1 << 3)\n#define WNOWAIT_ (1 << 24)\n#define __WALL_ (1 << 30)\n\n#define P_ALL_ 0\n#define P_PID_ 1\n#define P_PGID_ 2\n\n// returns false if the task cannot be reaped and true if the task was reaped\nstatic bool reap_if_zombie(struct task *task, struct siginfo_ *info_out, struct rusage_ *rusage_out, int options) {\n    if (!task->zombie)\n        return false;\n    lock(&task->group->lock);\n\n    dword_t exit_code = task->exit_code;\n    if (task->group->doing_group_exit)\n        exit_code = task->group->group_exit_code;\n    info_out->child.status = exit_code;\n\n    struct rusage_ rusage = task->group->rusage;\n    if (!(options & WNOWAIT_)) {\n        lock(&current->group->lock);\n        rusage_add(&current->group->children_rusage, &rusage);\n        unlock(&current->group->lock);\n    }\n    if (rusage_out != NULL)\n        *rusage_out = rusage;\n\n    unlock(&task->group->lock);\n\n    // WNOWAIT means don't destroy the child, instead leave it so it could be waited for again.\n    if (options & WNOWAIT_)\n        return true;\n\n    // tear down group\n    cond_destroy(&task->group->child_exit);\n    task_leave_session(task);\n    list_remove(&task->group->pgroup);\n    free(task->group);\n\n    task_destroy(task);\n    return true;\n}\n\nstatic bool notify_if_stopped(struct task *task, struct siginfo_ *info_out) {\n    lock(&task->group->lock);\n    bool stopped = task->group->stopped;\n    unlock(&task->group->lock);\n    if (!stopped || task->group->group_exit_code == 0)\n        return false;\n    dword_t exit_code = task->group->group_exit_code;\n    task->group->group_exit_code = 0;\n    info_out->child.status = exit_code;\n    return true;\n}\n\nstatic bool reap_if_needed(struct task *task, struct siginfo_ *info_out, struct rusage_ *rusage_out, int options) {\n    assert(task_is_leader(task));\n    if ((options & WUNTRACED_ && notify_if_stopped(task, info_out)) ||\n        (options & WEXITED_ && reap_if_zombie(task, info_out, rusage_out, options))) {\n        info_out->sig = SIGCHLD_;\n        return true;\n    }\n    lock(&task->ptrace.lock);\n    if (task->ptrace.stopped && task->ptrace.signal) {\n        // I had this code here because it made something work, but it's now\n        // making GDB think we support events (we don't). I can't remember what\n        // it fixed but until then commenting it out for now.\n        info_out->child.status = /* task->ptrace.trap_event << 16 |*/ task->ptrace.signal << 8 | 0x7f;\n        task->ptrace.signal = 0;\n        unlock(&task->ptrace.lock);\n        return true;\n    }\n    unlock(&task->ptrace.lock);\n    return false;\n}\n\nint do_wait(int idtype, pid_t_ id, struct siginfo_ *info, struct rusage_ *rusage, int options) {\n    if (idtype != P_ALL_ && idtype != P_PID_ && idtype != P_PGID_)\n        return _EINVAL;\n    if (options & ~(WNOHANG_|WUNTRACED_|WEXITED_|WCONTINUED_|WNOWAIT_|__WALL_))\n        return _EINVAL;\n\n    lock(&pids_lock);\n    int err;\n    bool got_signal = false;\n\nretry:\n    if (idtype != P_PID_) {\n        // look for a zombie child\n        bool no_children = true;\n        struct task *parent;\n        list_for_each_entry(&current->group->threads, parent, group_links) {\n            struct task *task;\n            list_for_each_entry(&current->children, task, siblings) {\n                if (!task_is_leader(task))\n                    continue;\n                if (idtype == P_PGID_ && task->group->pgid != id)\n                    continue;\n                no_children = false;\n                info->child.pid = task->pid;\n                if (reap_if_needed(task, info, rusage, options))\n                    goto found_something;\n            }\n        }\n        err = _ECHILD;\n        if (no_children)\n            goto error;\n    } else {\n        // check if this child is a zombie\n        struct task *task = pid_get_task_zombie(id);\n        err = _ECHILD;\n        if (task == NULL || task->parent == NULL || task->parent->group != current->group)\n            goto error;\n        task = task->group->leader;\n        info->child.pid = id;\n        if (reap_if_needed(task, info, rusage, options))\n            goto found_something;\n    }\n\n    // WNOHANG leaves the info in an implementation-defined state. set the pid\n    // to 0 so wait4 can pass that along correctly.\n    info->child.pid = 0;\n    if (options & WNOHANG_) {\n        info->sig = SIGCHLD_;\n        goto found_something;\n    }\n\n    err = _EINTR;\n    if (got_signal)\n        goto error;\n\n    // no matching zombie found, wait for one\n    if (wait_for(&current->group->child_exit, &pids_lock, NULL)) {\n        // maybe we got a SIGCHLD! go through the loop one more time to make\n        // sure the newly exited process is returned in that case.\n        got_signal = true;\n        goto retry;\n    }\n    goto retry;\n\n    info->sig = SIGCHLD_;\nfound_something:\n    unlock(&pids_lock);\n    return 0;\n\nerror:\n    unlock(&pids_lock);\n    return err;\n}\n\ndword_t sys_waitid(int_t idtype, pid_t_ id, addr_t info_addr, int_t options) {\n    STRACE(\"waitid(%d, %d, %#x, %#x)\", idtype, id, info_addr, options);\n    struct siginfo_ info = {};\n    int_t res = do_wait(idtype, id, &info, NULL, options);\n    if (res < 0 || (res == 0 && info.child.pid == 0))\n        return res;\n    if (info_addr != 0 && user_put(info_addr, info))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_wait4(pid_t_ id, addr_t status_addr, dword_t options, addr_t rusage_addr) {\n    STRACE(\"wait4(%d, %#x, %#x, %#x)\", id, status_addr, options, rusage_addr);\n    if (options & WNOWAIT_)\n        return _EINVAL;\n\n    int idtype;\n    if (id > 0)\n        idtype = P_PID_;\n    else if (id == -1)\n        idtype = P_ALL_;\n    else {\n        idtype = P_PGID_;\n        if (id == 0)\n            id = current->group->pgid;\n        else\n            id = -id;\n    }\n\n    struct siginfo_ info = {.child.pid = 0xbaba};\n    struct rusage_ rusage;\n    int_t res = do_wait(idtype, id, &info, &rusage, options | WEXITED_);\n    if (res < 0 || (res == 0 && info.child.pid == 0))\n        return res;\n    if (status_addr != 0 && user_put(status_addr, info.child.status))\n        return _EFAULT;\n    if (rusage_addr != 0 && user_put(rusage_addr, rusage))\n        return _EFAULT;\n    return info.child.pid;\n}\n\ndword_t sys_waitpid(pid_t_ pid, addr_t status_addr, dword_t options) {\n    return sys_wait4(pid, status_addr, options, 0);\n}\n"
  },
  {
    "path": "kernel/fork.c",
    "content": "#include \"debug.h\"\n#include \"kernel/task.h\"\n#include \"fs/fd.h\"\n#include \"kernel/calls.h\"\n#include \"fs/tty.h\"\n#include \"kernel/mm.h\"\n#include \"kernel/ptrace.h\"\n\n#define CSIGNAL_ 0x000000ff\n#define CLONE_VM_ 0x00000100\n#define CLONE_FS_ 0x00000200\n#define CLONE_FILES_ 0x00000400\n#define CLONE_SIGHAND_ 0x00000800\n#define CLONE_PTRACE_ 0x00002000\n#define CLONE_VFORK_ 0x00004000\n#define CLONE_PARENT_ 0x00008000\n#define CLONE_THREAD_ 0x00010000\n#define CLONE_NEWNS_ 0x00020000\n#define CLONE_SYSVSEM_ 0x00040000\n#define CLONE_SETTLS_ 0x00080000\n#define CLONE_PARENT_SETTID_ 0x00100000\n#define CLONE_CHILD_CLEARTID_ 0x00200000\n#define CLONE_DETACHED_ 0x00400000\n#define CLONE_UNTRACED_ 0x00800000\n#define CLONE_CHILD_SETTID_ 0x01000000\n#define CLONE_NEWCGROUP_ 0x02000000\n#define CLONE_NEWUTS_ 0x04000000\n#define CLONE_NEWIPC_ 0x08000000\n#define CLONE_NEWUSER_ 0x10000000\n#define CLONE_NEWPID_ 0x20000000\n#define CLONE_NEWNET_ 0x40000000\n#define CLONE_IO_ 0x80000000\n#define IMPLEMENTED_FLAGS (CLONE_VM_|CLONE_FILES_|CLONE_FS_|CLONE_SIGHAND_|CLONE_SYSVSEM_|CLONE_VFORK_|CLONE_THREAD_|\\\n        CLONE_SETTLS_|CLONE_CHILD_SETTID_|CLONE_PARENT_SETTID_|CLONE_CHILD_CLEARTID_|CLONE_DETACHED_)\n\nstatic struct tgroup *tgroup_copy(struct tgroup *old_group) {\n    struct tgroup *group = malloc(sizeof(struct tgroup));\n    *group = *old_group;\n    list_init(&group->threads);\n    list_add(&old_group->pgroup, &group->pgroup);\n    list_add(&old_group->session, &group->session);\n    if (group->tty) {\n        lock(&group->tty->lock);\n        group->tty->refcount++;\n        unlock(&group->tty->lock);\n    }\n    group->itimer = NULL;\n    group->doing_group_exit = false;\n    group->children_rusage = (struct rusage_) {};\n    cond_init(&group->child_exit);\n    cond_init(&group->stopped_cond);\n    lock_init(&group->lock);\n    return group;\n}\n\nstatic int copy_task(struct task *task, dword_t flags, addr_t stack, addr_t ptid_addr, addr_t tls_addr, addr_t ctid_addr) {\n    task->vfork = NULL;\n    if (stack != 0)\n        task->cpu.esp = stack;\n\n    int err;\n    struct mm *mm = task->mm;\n    if (flags & CLONE_VM_) {\n        mm_retain(mm);\n    } else {\n        task_set_mm(task, mm_copy(mm));\n    }\n\n    if (flags & CLONE_FILES_) {\n        task->files->refcount++;\n    } else {\n        task->files = fdtable_copy(task->files);\n        if (IS_ERR(task->files)) {\n            err = PTR_ERR(task->files);\n            goto fail_free_mem;\n        }\n    }\n\n    err = _ENOMEM;\n    if (flags & CLONE_FS_) {\n        task->fs->refcount++;\n    } else {\n        task->fs = fs_info_copy(task->fs);\n        if (task->fs == NULL)\n            goto fail_free_files;\n    }\n\n    if (flags & CLONE_SIGHAND_) {\n        task->sighand->refcount++;\n    } else {\n        task->sighand = sighand_copy(task->sighand);\n        if (task->sighand == NULL)\n            goto fail_free_fs;\n    }\n\n    struct tgroup *old_group = task->group;\n    lock(&pids_lock);\n    lock(&old_group->lock);\n    if (!(flags & CLONE_THREAD_)) {\n        task->group = tgroup_copy(old_group);\n        task->group->leader = task;\n        task->tgid = task->pid;\n    }\n    list_add(&task->group->threads, &task->group_links);\n    unlock(&old_group->lock);\n    unlock(&pids_lock);\n\n    if (flags & CLONE_SETTLS_) {\n        err = task_set_thread_area(task, tls_addr);\n        if (err < 0)\n            goto fail_free_sighand;\n    }\n\n    err = _EFAULT;\n    if (flags & CLONE_CHILD_SETTID_)\n        if (user_put_task(task, ctid_addr, task->pid))\n            goto fail_free_sighand;\n    if (flags & CLONE_PARENT_SETTID_)\n        if (user_put(ptid_addr, task->pid))\n            goto fail_free_sighand;\n    if (flags & CLONE_CHILD_CLEARTID_)\n        task->clear_tid = ctid_addr;\n    task->exit_signal = flags & CSIGNAL_;\n\n    // remember to do CLONE_SYSVSEM\n    return 0;\n\nfail_free_sighand:\n    sighand_release(task->sighand);\nfail_free_fs:\n    fs_info_release(task->fs);\nfail_free_files:\n    fdtable_release(task->files);\nfail_free_mem:\n    mm_release(task->mm);\n    return err;\n}\n\ndword_t sys_clone(dword_t flags, addr_t stack, addr_t ptid, addr_t tls, addr_t ctid) {\n    STRACE(\"clone(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\", flags, stack, ptid, tls, ctid);\n    if (flags & ~CSIGNAL_ & ~IMPLEMENTED_FLAGS) {\n        FIXME(\"unimplemented clone flags 0x%x\", flags & ~CSIGNAL_ & ~IMPLEMENTED_FLAGS);\n        return _EINVAL;\n    }\n    if (flags & CLONE_SIGHAND_ && !(flags & CLONE_VM_))\n        return _EINVAL;\n    if (flags & CLONE_THREAD_ && !(flags & CLONE_SIGHAND_))\n        return _EINVAL;\n\n    struct task *task = task_create_(current);\n    if (task == NULL)\n        return _ENOMEM;\n    int err = copy_task(task, flags, stack, ptid, tls, ctid);\n    if (err < 0) {\n        // FIXME: there is a window between task_create_ and task_destroy where\n        // some other thread could get a pointer to the task.\n        // FIXME: task_destroy doesn't free all aspects of the task, which\n        // could cause leaks\n        lock(&pids_lock);\n        task_destroy(task);\n        unlock(&pids_lock);\n        return err;\n    }\n    task->cpu.eax = 0;\n\n    struct vfork_info vfork;\n    if (flags & CLONE_VFORK_) {\n        lock_init(&vfork.lock);\n        cond_init(&vfork.cond);\n        vfork.done = false;\n        task->vfork = &vfork;\n    }\n\n    // task might be destroyed by the time we finish, so save the pid\n    pid_t pid = task->pid;\n\n    if (current->ptrace.traced) {\n        current->ptrace.trap_event = PTRACE_EVENT_FORK_;\n        send_signal(current, SIGTRAP_, SIGINFO_NIL);\n    }\n\n    task_start(task);\n\n    if (flags & CLONE_VFORK_) {\n        lock(&vfork.lock);\n        while (!vfork.done)\n            // FIXME this should stop waiting if a fatal signal is received\n            wait_for_ignore_signals(&vfork.cond, &vfork.lock, NULL);\n        unlock(&vfork.lock);\n        lock(&task->general_lock);\n        task->vfork = NULL;\n        unlock(&task->general_lock);\n        cond_destroy(&vfork.cond);\n    }\n\n    return pid;\n}\n\ndword_t sys_fork() {\n    return sys_clone(SIGCHLD_, 0, 0, 0, 0);\n}\n\ndword_t sys_vfork() {\n    return sys_clone(CLONE_VFORK_ | CLONE_VM_ | SIGCHLD_, 0, 0, 0, 0);\n}\n\nvoid vfork_notify(struct task *task) {\n    lock(&task->general_lock);\n    if (task->vfork) {\n        lock(&task->vfork->lock);\n        task->vfork->done = true;\n        notify(&task->vfork->cond);\n        unlock(&task->vfork->lock);\n    }\n    unlock(&task->general_lock);\n}\n"
  },
  {
    "path": "kernel/fs.c",
    "content": "#include \"debug.h\"\n#include <string.h>\n#include <sys/stat.h>\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/task.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"fs/path.h\"\n#include \"fs/dev.h\"\n\nstatic struct fd *at_fd(fd_t f) {\n    if (f == AT_FDCWD_)\n        return AT_PWD;\n    return f_get(f);\n}\n\nstatic void apply_umask(mode_t_ *mode) {\n    struct fs_info *fs = current->fs;\n    lock(&fs->lock);\n    *mode &= ~fs->umask;\n    unlock(&fs->lock);\n}\n\nint access_check(struct statbuf *stat, int check) {\n    if (superuser()) return 0;\n    if (check == 0) return 0;\n    // Align check with the correct bits in mode\n    if (current->euid == stat->uid) {\n        check <<= 6;\n    } else if (current->egid == stat->gid) {\n        check <<= 3;\n    }\n    if (!(stat->mode & check))\n        return _EACCES;\n    return 0;\n}\n\n// TODO ENAMETOOLONG\n\n#define AT_EACCESS_ 0x200\ndword_t sys_access(addr_t path_addr, dword_t mode) {\n    return sys_faccessat(AT_FDCWD_, path_addr, mode, 0);\n}\ndword_t sys_faccessat(fd_t at_f, addr_t path_addr, mode_t_ mode, dword_t flags) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    STRACE(\"faccessat(%d, \\\"%s\\\", 0x%x, %d)\", at_f, path, mode, flags);\n\n    if (flags & AT_EACCESS_)\n        return generic_accessat(at, path, mode);\n\n    uid_t_ uid_tmp = current->euid;\n    uid_t_ gid_tmp = current->egid;\n    current->euid = current->uid;\n    current->egid = current->gid;\n    int err = generic_accessat(at, path, mode);\n    current->euid = uid_tmp;\n    current->egid = gid_tmp;\n    return err;\n}\n\nfd_t sys_openat(fd_t at_f, addr_t path_addr, dword_t flags, mode_t_ mode) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"openat(%d, \\\"%s\\\", 0x%x, 0x%x)\", at_f, path, flags, mode);\n\n    if (flags & O_CREAT_)\n        apply_umask(&mode);\n\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    struct fd *fd = generic_openat(at, path, flags, mode);\n    if (IS_ERR(fd))\n        return PTR_ERR(fd);\n    return f_install(fd, flags);\n}\n\nfd_t sys_open(addr_t path_addr, dword_t flags, mode_t_ mode) {\n    return sys_openat(AT_FDCWD_, path_addr, flags, mode);\n}\n\ndword_t sys_readlinkat(fd_t at_f, addr_t path_addr, addr_t buf_addr, dword_t bufsize) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"readlinkat(%d, \\\"%s\\\", %#x, %#x)\", at_f, path, buf_addr, bufsize);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    char buf[bufsize];\n    ssize_t size = generic_readlinkat(at, path, buf, bufsize);\n    if (size >= 0) {\n        STRACE(\" \\\"%.*s\\\"\", size, buf);\n        if (user_write(buf_addr, buf, size))\n            return _EFAULT;\n    }\n    return size;\n}\n\ndword_t sys_readlink(addr_t path_addr, addr_t buf_addr, dword_t bufsize) {\n    return sys_readlinkat(AT_FDCWD_, path_addr, buf_addr, bufsize);\n}\n\ndword_t sys_linkat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr) {\n    char src[MAX_PATH];\n    if (user_read_string(src_addr, src, sizeof(src)))\n        return _EFAULT;\n    char dst[MAX_PATH];\n    if (user_read_string(dst_addr, dst, sizeof(dst)))\n        return _EFAULT;\n    STRACE(\"linkat(%d, \\\"%s\\\", %d, \\\"%s\\\")\", src_at_f, src, dst_at_f, dst);\n    struct fd *src_at = at_fd(src_at_f);\n    if (src_at == NULL)\n        return _EBADF;\n    struct fd *dst_at = at_fd(dst_at_f);\n    if (dst_at == NULL)\n        return _EBADF;\n    return generic_linkat(src_at, src, dst_at, dst);\n}\n\ndword_t sys_link(addr_t src_addr, addr_t dst_addr) {\n    return sys_linkat(AT_FDCWD_, src_addr, AT_FDCWD_, dst_addr);\n}\n\n#define AT_REMOVEDIR_ 0x200\ndword_t sys_unlinkat(fd_t at_f, addr_t path_addr, int_t flags) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"unlinkat(%d, \\\"%s\\\", %d)\", at_f, path, flags);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    if (flags & AT_REMOVEDIR_)\n        return generic_rmdirat(at, path);\n    else\n        return generic_unlinkat(at, path);\n}\n\ndword_t sys_unlink(addr_t path_addr) {\n    return sys_unlinkat(AT_FDCWD_, path_addr, 0);\n}\n\ndword_t sys_renameat2(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr, int_t flags) {\n    if (flags != 0)\n        return _EINVAL;\n    char src[MAX_PATH];\n    if (user_read_string(src_addr, src, sizeof(src)))\n        return _EFAULT;\n    char dst[MAX_PATH];\n    if (user_read_string(dst_addr, dst, sizeof(dst)))\n        return _EFAULT;\n    STRACE(\"renameat(%d, \\\"%s\\\", %d, \\\"%s\\\")\", src_at_f, src, dst_at_f, dst);\n    struct fd *src_at = at_fd(src_at_f);\n    if (src_at == NULL)\n        return _EBADF;\n    struct fd *dst_at = at_fd(dst_at_f);\n    if (dst_at == NULL)\n        return _EBADF;\n    return generic_renameat(src_at, src, dst_at, dst);\n}\n\ndword_t sys_renameat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr) {\n    return sys_renameat2(src_at_f, src_addr, dst_at_f, dst_addr, 0);\n}\n\ndword_t sys_rename(addr_t src_addr, addr_t dst_addr) {\n    return sys_renameat2(AT_FDCWD_, src_addr, AT_FDCWD_, dst_addr, 0);\n}\n\ndword_t sys_symlinkat(addr_t target_addr, fd_t at_f, addr_t link_addr) {\n    char target[MAX_PATH];\n    if (user_read_string(target_addr, target, sizeof(target)))\n        return _EFAULT;\n    char link[MAX_PATH];\n    if (user_read_string(link_addr, link, sizeof(link)))\n        return _EFAULT;\n    STRACE(\"symlinkat(\\\"%s\\\", %d, \\\"%s\\\")\", target, at_f, link);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    return generic_symlinkat(target, at, link);\n}\n\ndword_t sys_symlink(addr_t target_addr, addr_t link_addr) {\n    return sys_symlinkat(target_addr, AT_FDCWD_, link_addr);\n}\n\ndword_t sys_mknodat(fd_t at_f, addr_t path_addr, mode_t_ mode, dev_t_ dev) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"mknodat(%d, \\\"%s\\\", %#x, %#x)\", at_f, path, mode, dev);\n    apply_umask(&mode);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    return generic_mknodat(at, path, mode, dev);\n}\n\ndword_t sys_mknod(addr_t path_addr, mode_t_ mode, dev_t_ dev) {\n    return sys_mknodat(AT_FDCWD_, path_addr, mode, dev);\n}\n\nstatic ssize_t sys_read_buf(fd_t fd_no, void *buf, size_t size) {\n    struct fd *fd = f_get(fd_no);\n    if (fd == NULL)\n        return _EBADF;\n    if (S_ISDIR(fd->type))\n        return _EISDIR;\n\n    ssize_t res;\n    if (fd->ops->read) {\n        res = fd->ops->read(fd, buf, size);\n    } else if (fd->ops->pread) {\n        res = fd->ops->pread(fd, buf, size, fd->offset);\n        if (res > 0) {\n            fd->ops->lseek(fd, res, LSEEK_CUR);\n        }\n    } else {\n        return _EBADF;\n    }\n\n    if (res >= 0) {\n        size_t print_size = res;\n        if (print_size > 100) print_size = 100;\n        STRACE(\" \\\"%.*s\\\"\", print_size, buf);\n    }\n    return res;\n}\n\ndword_t sys_read(fd_t fd_no, addr_t buf_addr, dword_t size) {\n    STRACE(\"read(%d, 0x%x, %d)\", fd_no, buf_addr, size);\n    char *buf = (char *) malloc(size);\n    if (buf == NULL)\n        return _ENOMEM;\n    int_t res = sys_read_buf(fd_no, buf, size);\n    if (res >= 0) {\n        if (user_write(buf_addr, buf, res))\n            res = _EFAULT;\n    }\n    free(buf);\n    return res;\n}\n\nstatic ssize_t sys_write_buf(fd_t fd_no, void *buf, size_t size) {\n    struct fd *fd = f_get(fd_no);\n    if (fd == NULL)\n        return _EBADF;\n\n    ssize_t res;\n    if (fd->ops->write) {\n        res = fd->ops->write(fd, buf, size);\n    } else if (fd->ops->pwrite) {\n        res = fd->ops->pwrite(fd, buf, size, fd->offset);\n        if (res > 0) {\n            fd->ops->lseek(fd, res, LSEEK_CUR);\n        }\n    } else {\n        return _EBADF;\n    }\n    return res;\n}\n\ndword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size) {\n    // FIXME this is a DOS vector, should ideally use vectorized I/O\n    char *buf = malloc(size);\n    if (buf == NULL)\n        return _ENOMEM;\n    dword_t res = _EFAULT;\n    if (user_read(buf_addr, buf, size))\n        goto out;\n\n    size_t print_size = size;\n    if (print_size > 100) print_size = 100;\n    STRACE(\"write(%d, \\\"%.*s\\\", %d)\", fd_no, print_size, buf, size);\n\n    res = sys_write_buf(fd_no, buf, size);\nout:\n    free(buf);\n    return res;\n}\n\n// The vector operations work by flattening the vector into a malloc buffer.\n// This at least isn't much worse than what it was before, which copied each\n// element of the vector into a malloc buffer. The perfect solution would be to\n// construct a vector with an entry for each page of the buffer. I haven't done\n// that yet because it's more work and the efficiency gain from that is dwarfed\n// by the inefficiency of the emulator.\n\nstatic struct iovec_ *read_iovec(addr_t iovec_addr, unsigned iovec_count) {\n    dword_t iovec_size = sizeof(struct iovec_) * iovec_count;\n    struct iovec_ *iovec = malloc(iovec_size);\n    if (iovec == NULL)\n        return ERR_PTR(_ENOMEM);\n    if (user_read(iovec_addr, iovec, iovec_size)) {\n        free(iovec);\n        return ERR_PTR(_EFAULT);\n    }\n    return iovec;\n}\n\nstatic ssize_t iovec_size(struct iovec_ *iovec, unsigned iovec_count) {\n    size_t size = 0;\n    for (unsigned i = 0; i < iovec_count; i++)\n        size += iovec[i].len;\n    return size;\n}\n\ndword_t sys_readv(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) {\n    STRACE(\"readv(%d, %#x, %d)\", fd_no, iovec_addr, iovec_count);\n    struct iovec_ *iovec = read_iovec(iovec_addr, iovec_count);\n    if (IS_ERR(iovec))\n        return PTR_ERR(iovec);\n    size_t io_size = iovec_size(iovec, iovec_count);\n    char *buf = malloc(io_size);\n    if (buf == NULL) {\n        free(iovec);\n        return _ENOMEM;\n    }\n    ssize_t res = sys_read_buf(fd_no, buf, io_size);\n    if (res < 0)\n        goto error;\n\n    size_t offset = 0;\n    for (unsigned i = 0; i < iovec_count; i++) {\n        size_t print_size = iovec[i].len;\n        if (print_size > 100) print_size = 100;\n        STRACE(\" {\\\"%.*s\\\", %u}\", print_size, buf + offset, iovec[i].len);\n\n        if (user_write(iovec[i].base, buf + offset, iovec[i].len)) {\n            res = _EFAULT;\n            goto error;\n        }\n        offset += iovec[i].len;\n    }\n\nerror:\n    free(buf);\n    free(iovec);\n    return res;\n}\n\ndword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) {\n    STRACE(\"writev(%d, %#x, %d)\", fd_no, iovec_addr, iovec_count);\n    struct iovec_ *iovec = read_iovec(iovec_addr, iovec_count);\n    if (IS_ERR(iovec))\n        return PTR_ERR(iovec);\n    size_t io_size = iovec_size(iovec, iovec_count);\n    char *buf = malloc(io_size);\n    if (buf == NULL) {\n        free(iovec);\n        return _ENOMEM;\n    }\n\n    ssize_t res = 0;\n    size_t offset = 0;\n    for (unsigned i = 0; i < iovec_count; i++) {\n        if (user_read(iovec[i].base, buf + offset, iovec[i].len)) {\n            res = _EFAULT;\n            goto error;\n        }\n\n        size_t print_size = iovec[i].len;\n        if (print_size > 100) print_size = 100;\n        STRACE(\" {\\\"%.*s\\\", %u}\", print_size, buf + offset, iovec[i].len);\n        offset += iovec[i].len;\n    }\n    res = sys_write_buf(fd_no, buf, io_size);\n\nerror:\n    free(buf);\n    free(iovec);\n    return res;\n}\n\ndword_t sys__llseek(fd_t f, dword_t off_high, dword_t off_low, addr_t res_addr, dword_t whence) {\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    if (!fd->ops->lseek)\n        return _ESPIPE;\n    lock(&fd->lock);\n    off_t_ off = ((qword_t) off_high << 32) | off_low;\n    STRACE(\"llseek(%d, %lu, %#x, %d)\", f, off, res_addr, whence);\n    off_t_ res = fd->ops->lseek(fd, off, whence);\n    STRACE(\" -> %lu\", res);\n    unlock(&fd->lock);\n    if (res < 0)\n        return res;\n    if (user_put(res_addr, res))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_lseek(fd_t f, dword_t off, dword_t whence) {\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    if (!fd->ops->lseek)\n        return _ESPIPE;\n    lock(&fd->lock);\n    off_t res = fd->ops->lseek(fd, off, whence);\n    unlock(&fd->lock);\n    if ((dword_t) res != res)\n        return _EOVERFLOW;\n    return res;\n}\n\ndword_t sys_pread(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) {\n    STRACE(\"pread(%d, 0x%x, %d, %d)\", f, buf_addr, size, off);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    char *buf = malloc(size+1);\n    if (buf == NULL)\n        return _ENOMEM;\n    lock(&fd->lock);\n    ssize_t res;\n    if (fd->ops->pread) {\n        res = fd->ops->pread(fd, buf, size, off);\n    } else {\n        off_t_ saved_off = fd->ops->lseek(fd, 0, LSEEK_CUR);\n        if ((res = fd->ops->lseek(fd, off, LSEEK_SET)) < 0) {\n            goto out;\n        }\n        res = fd->ops->read(fd, buf, size);\n        // This really shouldn't fail. The lseek man page lists these reasons:\n        // EBADF, ESPIPE: can't happen because the last lseek wouldn't have succeeded.\n        // EOVERFLOW: can't happen for LSEEK_SET.\n        // EINVAL: can't happen other than typoing LSEEK_SET, because we know saved_off is not negative.\n        off_t_ lseek_res = fd->ops->lseek(fd, saved_off, LSEEK_SET);\n        assert(lseek_res >= 0);\n    }\n    if (res >= 0) {\n        buf[res] = '\\0';\n        STRACE(\" \\\"%.99s\\\"\", buf);\n        if (user_write(buf_addr, buf, res))\n            res = _EFAULT;\n    }\nout:\n    unlock(&fd->lock);\n    free(buf);\n    return res;\n}\n\ndword_t sys_pwrite(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) {\n    STRACE(\"pwrite(%d, 0x%x, %d, %d)\", f, buf_addr, size, off);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    char *buf = malloc(size+1);\n    if (buf == NULL)\n        return _ENOMEM;\n    if (user_read(buf_addr, buf, size))\n        return _EFAULT;\n    lock(&fd->lock);\n    ssize_t res;\n    if (fd->ops->pwrite) {\n        res = fd->ops->pwrite(fd, buf, size, off);\n    } else {\n        off_t_ saved_off = fd->ops->lseek(fd, 0, LSEEK_CUR);\n        if ((res = fd->ops->lseek(fd, off, LSEEK_SET)) >= 0) {\n            res = fd->ops->write(fd, buf, size);\n            // This really shouldn't fail. The lseek man page lists these reasons:\n            // EBADF, ESPIPE: can't happen because the last lseek wouldn't have succeeded.\n            // EOVERFLOW: can't happen for LSEEK_SET.\n            // EINVAL: can't happen other than typoing LSEEK_SET, because we know saved_off is not negative.\n            off_t_ lseek_res = fd->ops->lseek(fd, saved_off, LSEEK_SET);\n            assert(lseek_res >= 0);\n        }\n    }\n    unlock(&fd->lock);\n    free(buf);\n    return res;\n}\n\nstatic int fd_ioctl(struct fd *fd, dword_t cmd, dword_t arg) {\n    ssize_t size = -1;\n    if (fd->ops->ioctl_size)\n        size = fd->ops->ioctl_size(cmd);\n    if (size < 0)\n        return _ENOTTY;\n    if (size == 0)\n        return fd->ops->ioctl(fd, cmd, (void *) (long) arg);\n\n    // praying that this won't break\n    char buf[size];\n    if (user_read(arg, buf, size))\n        return _EFAULT;\n    int res = fd->ops->ioctl(fd, cmd, buf);\n    if (res < 0)\n        return res;\n    if (user_write(arg, buf, size))\n        return _EFAULT;\n    return res;\n}\n\nstatic int set_nonblock(struct fd *fd, addr_t nb_addr) {\n    dword_t nonblock;\n    if (user_get(nb_addr, nonblock))\n        return _EFAULT;\n    int flags = fd_getflags(fd);\n    if (nonblock)\n        flags |= O_NONBLOCK_;\n    else\n        flags &= ~O_NONBLOCK_;\n    return fd_setflags(fd, flags);\n}\n\ndword_t sys_ioctl(fd_t f, dword_t cmd, dword_t arg) {\n    STRACE(\"ioctl(%d, 0x%x, 0x%x)\", f, cmd, arg);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n\n    switch (cmd) {\n        case FIONBIO_:\n            return set_nonblock(fd, arg);\n        case FIOCLEX_:\n            bit_set(f, current->files->cloexec);\n            return 0;\n        case FIONCLEX_:\n            bit_clear(f, current->files->cloexec);\n            return 0;\n    }\n    return fd_ioctl(fd, cmd, arg);\n}\n\ndword_t sys_getcwd(addr_t buf_addr, dword_t size) {\n    STRACE(\"getcwd(%#x, %#x)\", buf_addr, size);\n    lock(&current->fs->lock);\n    struct fd *wd = current->fs->pwd;\n    char pwd[MAX_PATH + 1];\n    int err = generic_getpath(wd, pwd);\n    unlock(&current->fs->lock);\n    if (err < 0)\n        return err;\n\n    if (strlen(pwd) + 1 > size)\n        return _ERANGE;\n    size = strlen(pwd) + 1;\n    char *buf = malloc(size);\n    if (buf == NULL)\n        return _ENOMEM;\n    strcpy(buf, pwd);\n    STRACE(\" \\\"%.*s\\\"\", size, buf);\n    dword_t res = size;\n    if (user_write(buf_addr, buf, size))\n        res = _EFAULT;\n    free(buf);\n    return res;\n}\n\nstatic struct fd *open_dir(const char *path) {\n    struct statbuf stat;\n    int err = generic_statat(AT_PWD, path, &stat, true);\n    if (err < 0)\n        return ERR_PTR(err);\n    if (!(stat.mode & S_IFDIR))\n        return ERR_PTR(_ENOTDIR);\n\n    return generic_open(path, O_RDONLY_, 0);\n}\n\nvoid fs_chdir(struct fs_info *fs, struct fd *fd) {\n    lock(&fs->lock);\n    fd_close(fs->pwd);\n    fs->pwd = fd;\n    unlock(&fs->lock);\n}\n\ndword_t sys_chdir(addr_t path_addr) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"chdir(\\\"%s\\\")\", path);\n\n    struct fd *dir = open_dir(path);\n    if (IS_ERR(dir))\n        return PTR_ERR(dir);\n    fs_chdir(current->fs, dir);\n    return 0;\n}\n\ndword_t sys_fchdir(fd_t f) {\n    STRACE(\"fchdir(%d)\", f);\n    struct fd *dir = f_get(f);\n    if (dir == NULL)\n        return _EBADF;\n    dir->refcount++;\n    fs_chdir(current->fs, dir);\n    return 0;\n}\n\ndword_t sys_chroot(addr_t path_addr) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"chroot(\\\"%s\\\")\", path);\n\n    struct fd *dir = open_dir(path);\n    if (IS_ERR(dir))\n        return PTR_ERR(dir);\n    lock(&current->fs->lock);\n    fd_close(current->fs->root);\n    current->fs->root = dir;\n    unlock(&current->fs->lock);\n    return 0;\n}\n\ndword_t sys_umask(dword_t mask) {\n    STRACE(\"umask(0%o)\", mask);\n    struct fs_info *fs = current->fs;\n    lock(&fs->lock);\n    mode_t_ old_umask = fs->umask;\n    fs->umask = ((mode_t_) mask) & 0777;\n    unlock(&fs->lock);\n    return old_umask;\n}\n\nstatic int mount_statfs(struct mount *mount, struct statfsbuf *stat) {\n    int err = 0;\n    if (mount->fs->statfs)\n        err = mount->fs->statfs(mount, stat);\n    if (stat->type == 0)\n        stat->type = mount->fs->magic;\n    return err;\n}\n\nstatic int_t statfs_mount(struct mount *mount, addr_t buf_addr) {\n    struct statfsbuf buf = {};\n    int err = mount_statfs(mount, &buf);\n    if (err < 0)\n        return err;\n    struct statfs_ out_buf = {\n        .type = buf.type,\n        .bsize = buf.bsize,\n        .blocks = buf.blocks,\n        .bfree = buf.bfree,\n        .bavail = buf.bavail,\n        .files = buf.files,\n        .ffree = buf.ffree,\n        .fsid = buf.fsid,\n        .namelen = buf.namelen,\n        .frsize = buf.frsize,\n        .flags = buf.flags,\n    };\n    if (user_put(buf_addr, out_buf))\n        return _EFAULT;\n    return 0;\n}\n\nstatic int_t statfs64_mount(struct mount *mount, addr_t buf_addr) {\n    struct statfsbuf buf = {};\n    int err = mount_statfs(mount, &buf);\n    if (err < 0)\n        return err;\n    struct statfs64_ out_buf = {\n        .type = buf.type,\n        .bsize = buf.bsize,\n        .blocks = buf.blocks,\n        .bfree = buf.bfree,\n        .bavail = buf.bavail,\n        .files = buf.files,\n        .ffree = buf.ffree,\n        .fsid = buf.fsid,\n        .namelen = buf.namelen,\n        .frsize = buf.frsize,\n        .flags = buf.flags,\n    };\n    if (user_put(buf_addr, out_buf))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_statfs(addr_t path_addr, addr_t buf_addr) {\n    char path_raw[MAX_PATH];\n    if (user_read_string(path_addr, path_raw, sizeof(path_raw)))\n        return _EFAULT;\n    STRACE(\"statfs(\\\"%s\\\", %#x)\", path_raw, buf_addr);\n    char path[MAX_PATH];\n    int err = path_normalize(AT_PWD, path_raw, path, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = mount_find(path);\n    err = statfs_mount(mount, buf_addr);\n    mount_release(mount);\n    return err;\n}\n\ndword_t sys_statfs64(addr_t path_addr, dword_t buf_size, addr_t buf_addr) {\n    char path_raw[MAX_PATH];\n    if (user_read_string(path_addr, path_raw, sizeof(path_raw)))\n        return _EFAULT;\n    STRACE(\"statfs64(\\\"%s\\\", %d, %#x)\", path_raw, buf_size, buf_addr);\n    if (buf_size != sizeof(struct statfs64_))\n        return _EINVAL;\n    char path[MAX_PATH];\n    int err = path_normalize(AT_PWD, path_raw, path, N_SYMLINK_NOFOLLOW);\n    if (err < 0)\n        return err;\n    struct mount *mount = mount_find(path);\n    err = statfs64_mount(mount, buf_addr);\n    mount_release(mount);\n    return err;\n}\n\ndword_t sys_fstatfs(fd_t f, addr_t buf_addr) {\n    return statfs_mount(f_get(f)->mount, buf_addr);\n}\n\ndword_t sys_fstatfs64(fd_t f, addr_t buf_addr) {\n    return statfs64_mount(f_get(f)->mount, buf_addr);\n}\n\ndword_t sys_flock(fd_t f, dword_t operation) {\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    // TODO: POSIX doesn't allow flock to fail in this way. The check is here\n    // because a segfault is worse.\n    if (fd->mount->fs->flock == NULL)\n        return _EBADF;\n    return fd->mount->fs->flock(fd, operation);\n}\n\nstatic dword_t sys_utime_common(fd_t at_f, addr_t path_addr, struct timespec atime, struct timespec mtime, dword_t flags) {\n    char path[MAX_PATH];\n    if (path_addr != 0)\n        if (user_read_string(path_addr, path, sizeof(path)))\n            return _EFAULT;\n    STRACE(\"utimensat(%d, %s, {{%d, %d}, {%d, %d}}, %d)\", at_f, path,\n            atime.tv_sec, atime.tv_nsec, mtime.tv_sec, mtime.tv_nsec, flags);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n\n    bool follow_links = flags & AT_SYMLINK_NOFOLLOW_ ? false : true;\n    return generic_utime(at, path_addr != 0 ? path : \".\", atime, mtime, follow_links);\n}\n\ndword_t sys_utimensat(fd_t at_f, addr_t path_addr, addr_t times_addr, dword_t flags) {\n    struct timespec atime;\n    struct timespec mtime;\n    if (times_addr == 0) {\n        atime = mtime = timespec_now(CLOCK_REALTIME);\n    } else {\n        struct timespec_ times[2];\n        if (user_get(times_addr, times))\n            return _EFAULT;\n        atime = convert_timespec(times[0]);\n        mtime = convert_timespec(times[1]);\n    }\n    return sys_utime_common(at_f, path_addr, atime, mtime, flags);\n}\n\ndword_t sys_utimes(addr_t path_addr, addr_t times_addr) {\n    struct timespec atime;\n    struct timespec mtime;\n    if (times_addr == 0) {\n        atime = mtime = timespec_now(CLOCK_REALTIME);\n    } else {\n        struct timeval_ times[2];\n        if (user_get(times_addr, times))\n            return _EFAULT;\n        atime = convert_timeval(times[0]);\n        mtime = convert_timeval(times[1]);\n    }\n    return sys_utime_common(AT_FDCWD_, path_addr, atime, mtime, 0);\n}\n\ndword_t sys_utime(addr_t path_addr, addr_t times_addr) {\n    struct timespec atime;\n    struct timespec mtime;\n    if (times_addr == 0) {\n        atime = mtime = timespec_now(CLOCK_REALTIME);\n    } else {\n        struct utimbuf_ {\n            time_t_ actime;\n            time_t_ modtime;\n        } times;\n        if (user_get(times_addr, times))\n            return _EFAULT;\n        atime.tv_sec = times.actime;\n        atime.tv_nsec = 0;\n        mtime.tv_sec = times.modtime;\n        mtime.tv_nsec = 0;\n    }\n    return sys_utime_common(AT_FDCWD_, path_addr, atime, mtime, 0);\n}\n\nstatic int generic_fsetattr(struct fd *fd, struct attr attr) {\n    if (fd->mount->fs->fsetattr == NULL)\n        return _EPERM;\n    return fd->mount->fs->fsetattr(fd, attr);\n}\n\ndword_t sys_fchmod(fd_t f, dword_t mode) {\n    STRACE(\"fchmod(%d, %o)\", f, mode);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    mode &= ~S_IFMT;\n    return generic_fsetattr(fd, make_attr(mode, mode));\n}\n\ndword_t sys_fchmodat(fd_t at_f, addr_t path_addr, dword_t mode) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"fchmodat(%d, \\\"%s\\\", %o)\", at_f, path, mode);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    mode &= ~S_IFMT;\n    return generic_setattrat(at, path, make_attr(mode, mode), true);\n}\n\ndword_t sys_chmod(addr_t path_addr, dword_t mode) {\n    return sys_fchmodat(AT_FDCWD_, path_addr, mode);\n}\n\ndword_t sys_fchown32(fd_t f, uid_t_ owner, uid_t_ group) {\n    STRACE(\"fchown(%d, %d, %d)\", f, owner, group);\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    int err;\n    if (owner != (uid_t) -1) {\n        err = generic_fsetattr(fd, make_attr(uid, owner));\n        if (err < 0)\n            return err;\n    }\n    if (group != (uid_t) -1) {\n        err = generic_fsetattr(fd, make_attr(gid, group));\n        if (err < 0)\n            return err;\n    }\n    return 0;\n}\n\ndword_t sys_fchownat(fd_t at_f, addr_t path_addr, dword_t owner, dword_t group, int flags) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"fchownat(%d, \\\"%s\\\", %d, %d, %d)\", at_f, path, owner, group, flags);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    int err;\n    bool follow_links = flags & AT_SYMLINK_NOFOLLOW_ ? false : true;\n    if (owner != (uid_t) -1) {\n        err = generic_setattrat(at, path, make_attr(uid, owner), follow_links);\n        if (err < 0)\n            return err;\n    }\n    if (group != (uid_t) -1) {\n        err = generic_setattrat(at, path, make_attr(gid, group), follow_links);\n        if (err < 0)\n            return err;\n    }\n    return 0;\n}\n\ndword_t sys_chown32(addr_t path_addr, uid_t_ owner, uid_t_ group) {\n    return sys_fchownat(AT_FDCWD_, path_addr, owner, group, 0);\n}\n\ndword_t sys_lchown(addr_t path_addr, uid_t_ owner, uid_t_ group) {\n    return sys_fchownat(AT_FDCWD_, path_addr, owner, group, AT_SYMLINK_NOFOLLOW_);\n}\n\ndword_t sys_truncate64(addr_t path_addr, dword_t size_low, dword_t size_high) {\n    off_t_ size = ((qword_t) size_high << 32) | size_low;\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    return generic_setattrat(NULL, path, make_attr(size, size), true);\n}\n\ndword_t sys_ftruncate64(fd_t f, dword_t size_low, dword_t size_high) {\n    off_t_ size = ((qword_t) size_high << 32) | size_low;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    return generic_fsetattr(fd, make_attr(size, size));\n}\n\ndword_t sys_fallocate(fd_t f, dword_t UNUSED(mode), dword_t offset_low, dword_t offset_high, dword_t len_low, dword_t len_high) {\n    off_t_ offset = ((qword_t) offset_high << 32) | offset_low;\n    off_t_ len = ((qword_t) len_high << 32) | len_low;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    struct statbuf statbuf;\n    int err = fd->mount->fs->fstat(fd, &statbuf);\n    if (err < 0)\n        return err;\n    if ((uint64_t) offset + (uint64_t) len > statbuf.size)\n        return generic_fsetattr(fd, make_attr(size, offset + len));\n    return 0;\n}\n\ndword_t sys_mkdirat(fd_t at_f, addr_t path_addr, mode_t_ mode) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"mkdirat(%d, %s, 0%o)\", at_f, path, mode);\n    struct fd *at = at_fd(at_f);\n    if (at == NULL)\n        return _EBADF;\n    apply_umask(&mode);\n    mode &= 0777;\n    return generic_mkdirat(at, path, mode);\n}\n\ndword_t sys_mkdir(addr_t path_addr, mode_t_ mode) {\n    return sys_mkdirat(AT_FDCWD_, path_addr, mode);\n}\n\ndword_t sys_rmdir(addr_t path_addr) {\n    char path[MAX_PATH];\n    if (user_read_string(path_addr, path, sizeof(path)))\n        return _EFAULT;\n    STRACE(\"rmdir(%s)\", path);\n    return generic_rmdirat(AT_PWD, path);\n}\n\ndword_t sys_fsync(fd_t f) {\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    int err = 0;\n    if (fd->ops->fsync)\n        err = fd->ops->fsync(fd);\n    return err;\n}\n\n// a few stubs\ndword_t sys_sendfile(fd_t UNUSED(out_fd), fd_t UNUSED(in_fd), addr_t UNUSED(offset_addr), dword_t UNUSED(count)) {\n    return _EINVAL;\n}\ndword_t sys_sendfile64(fd_t UNUSED(out_fd), fd_t UNUSED(in_fd), addr_t UNUSED(offset_addr), dword_t UNUSED(count)) {\n    return _EINVAL;\n}\ndword_t sys_splice(fd_t UNUSED(in_fd), addr_t UNUSED(in_off_addr), fd_t UNUSED(out_fd), addr_t UNUSED(out_off_addr), dword_t UNUSED(count), dword_t UNUSED(flags)) {\n    return _EINVAL;\n}\ndword_t sys_copy_file_range(fd_t UNUSED(in_fd), addr_t UNUSED(in_off), fd_t UNUSED(out_fd),\n        addr_t UNUSED(out_off), dword_t UNUSED(len), uint_t UNUSED(flags)) {\n    return _EPERM; // good enough for ruby\n}\n\ndword_t sys_xattr_stub(addr_t UNUSED(path_addr), addr_t UNUSED(name_addr),\n        addr_t UNUSED(value_addr), dword_t UNUSED(size), dword_t UNUSED(flags)) {\n    return _ENOTSUP;\n}\n"
  },
  {
    "path": "kernel/fs.h",
    "content": "#ifndef FS_H\n#define FS_H\n\n#include \"misc.h\"\n#include \"util/list.h\"\n#include \"fs/stat.h\"\n#include \"fs/dev.h\"\n#include \"fs/fake-db.h\"\n#include \"fs/fix_path.h\"\n#include \"kernel/memory.h\"\n#include <dirent.h>\n#include <sqlite3.h>\n\nstruct fs_info {\n    atomic_uint refcount;\n    mode_t_ umask;\n    struct fd *pwd;\n    struct fd *root;\n    lock_t lock;\n};\nstruct fs_info *fs_info_new(void);\nstruct fs_info *fs_info_copy(struct fs_info *fs);\nvoid fs_info_release(struct fs_info *fs);\n\nvoid fs_chdir(struct fs_info *fs, struct fd *pwd);\n\n#define MAX_PATH 4096\n#define MAX_NAME 256\n\nstruct attr {\n    enum attr_type {\n        attr_uid,\n        attr_gid,\n        attr_mode,\n        attr_size,\n    } type;\n    union {\n        uid_t_ uid;\n        uid_t_ gid;\n        mode_t_ mode;\n        off_t_ size;\n    };\n};\n#define make_attr(_type, thing) \\\n    ((struct attr) {.type = attr_##_type, ._type = thing})\n\n#define AT_SYMLINK_NOFOLLOW_ 0x100\n#define AT_EMPTY_PATH_ 0x1000\n\nstruct fd *generic_open(const char *path, int flags, int mode);\nstruct fd *generic_openat(struct fd *at, const char *path, int flags, int mode);\nint generic_getpath(struct fd *fd, char *buf);\nint generic_linkat(struct fd *src_at, const char *src_raw, struct fd *dst_at, const char *dst_raw);\nint generic_unlinkat(struct fd *at, const char *path);\nint generic_rmdirat(struct fd *at, const char *path);\nint generic_renameat(struct fd *src_at, const char *src, struct fd *dst_at, const char *dst);\nint generic_symlinkat(const char *target, struct fd *at, const char *link);\nint generic_mknodat(struct fd *at, const char *path, mode_t_ mode, dev_t_ dev);\nint generic_seek(struct fd *fd, off_t_ off, int whence, size_t size);\n#define AC_R 4\n#define AC_W 2\n#define AC_X 1\n#define AC_F 0\nint generic_accessat(struct fd *dirfd, const char *path, int mode);\nint generic_statat(struct fd *at, const char *path, struct statbuf *stat, bool follow_links);\nint generic_setattrat(struct fd *at, const char *path, struct attr attr, bool follow_links);\nint generic_utime(struct fd *at, const char *path, struct timespec atime, struct timespec mtime, bool follow_links);\nssize_t generic_readlinkat(struct fd *at, const char *path, char *buf, size_t bufsize);\nint generic_mkdirat(struct fd *at, const char *path, mode_t_ mode);\n\nint access_check(struct statbuf *stat, int check);\n\nstruct mount {\n    const char *point;\n    const char *source;\n    const char *info;\n    int flags;\n    const struct fs_ops *fs;\n    unsigned refcount;\n    struct list mounts;\n\n    int root_fd;\n    union {\n        void *data;\n        struct fakefs_db fakefs;\n    };\n};\nextern lock_t mounts_lock;\n\n// returns a reference, which must be released\nstruct mount *mount_find(char *path);\nvoid mount_retain(struct mount *mount);\nvoid mount_release(struct mount *mount);\n\n// must hold mounts_lock while calling these, or traversing mounts\nint do_mount(const struct fs_ops *fs, const char *source, const char *point, const char *info, int flags);\nint do_umount(const char *point);\nint mount_remove(struct mount *mount);\nextern struct list mounts;\n\nbool mount_param_flag(const char *info, const char *flag);\n\n// open flags\n#define O_ACCMODE_ 3\n#define O_RDONLY_ 0\n#define O_WRONLY_ (1 << 0)\n#define O_RDWR_ (1 << 1)\n#define O_CREAT_ (1 << 6)\n#define O_EXCL_ (1 << 7)\n#define O_NOCTTY_ (1 << 8)\n#define O_TRUNC_ (1 << 9)\n#define O_APPEND_ (1 << 10)\n#define O_NONBLOCK_ (1 << 11)\n#define O_DIRECTORY_ (1 << 16)\n#define O_CLOEXEC_ (1 << 19)\n\n// generic ioctls\n#define FIONREAD_ 0x541b\n#define FIONBIO_ 0x5421\n#define FIONCLEX_ 0x5450\n#define FIOCLEX_ 0x5451\n\n// All operations are optional unless otherwise specified\nstruct fs_ops {\n    const char *name;\n    int magic;\n\n    int (*mount)(struct mount *mount);\n    int (*umount)(struct mount *mount);\n    int (*statfs)(struct mount *mount, struct statfsbuf *stat);\n\n    struct fd *(*open)(struct mount *mount, const char *path, int flags, int mode); // required\n    ssize_t (*readlink)(struct mount *mount, const char *path, char *buf, size_t bufsize);\n\n    // These return _EPERM if not present\n    int (*link)(struct mount *mount, const char *src, const char *dst);\n    int (*unlink)(struct mount *mount, const char *path);\n    int (*rmdir)(struct mount *mount, const char *path);\n    int (*rename)(struct mount *mount, const char *src, const char *dst);\n    int (*symlink)(struct mount *mount, const char *target, const char *link);\n    int (*mknod)(struct mount *mount, const char *path, mode_t_ mode, dev_t_ dev);\n    int (*mkdir)(struct mount *mount, const char *path, mode_t_ mode);\n\n    // There's a close function in both the fs and fd to handle device files\n    // where, for instance, there's a real_fd needed for getpath and also a tty\n    // reference, and both need to be released when the fd is closed.\n    // If they are the same function, it will only be called once.\n    int (*close)(struct fd *fd);\n\n    int (*stat)(struct mount *mount, const char *path, struct statbuf *stat); // required\n    int (*fstat)(struct fd *fd, struct statbuf *stat); // required\n    int (*setattr)(struct mount *mount, const char *path, struct attr attr);\n    int (*fsetattr)(struct fd *fd, struct attr attr);\n    int (*utime)(struct mount *mount, const char *path, struct timespec atime, struct timespec mtime);\n    // Returns the path of the file descriptor, null terminated, buf must be at least MAX_PATH+1\n    int (*getpath)(struct fd *fd, char *buf); // required\n\n    int (*flock)(struct fd *fd, int operation);\n\n    // If present, called when all references to an inode_data for this\n    // filesystem go away.\n    void (*inode_orphaned)(struct mount *mount, ino_t inode);\n};\n\nstruct mount *find_mount_and_trim_path(char *path);\n\n// adhoc fs\nstruct fd *adhoc_fd_create(const struct fd_ops *ops);\n// this is for the \"wtf is apple smoking\" section\nbool is_adhoc_fd(struct fd *fd);\n\n// filesystems\nextern const struct fs_ops procfs;\nextern const struct fs_ops fakefs;\nextern const struct fs_ops devptsfs;\nextern const struct fs_ops tmpfs;\nvoid fs_register(const struct fs_ops *fs);\n\n#endif\n"
  },
  {
    "path": "kernel/fs_info.c",
    "content": "#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n\nstruct fs_info *fs_info_new() {\n    struct fs_info *fs = malloc(sizeof(struct fs_info));\n    if (fs == NULL)\n        return NULL;\n    fs->refcount = 1;\n    fs->umask = 0;\n    fs->pwd = fs->root = NULL;\n    lock_init(&fs->lock);\n    return fs;\n}\n\nstruct fs_info *fs_info_copy(struct fs_info *fs) {\n    struct fs_info *new_fs = fs_info_new();\n    new_fs->umask = fs->umask;\n    new_fs->pwd = fd_retain(fs->pwd);\n    new_fs->root = fd_retain(fs->root);\n    return new_fs;\n}\n\nvoid fs_info_release(struct fs_info *fs) {\n    if (--fs->refcount == 0) {\n        fd_close(fs->pwd);\n        fd_close(fs->root);\n        free(fs);\n    }\n}\n"
  },
  {
    "path": "kernel/futex.c",
    "content": "#include \"kernel/calls.h\"\n\n#define FUTEX_WAIT_ 0\n#define FUTEX_WAKE_ 1\n#define FUTEX_REQUEUE_ 3\n#define FUTEX_PRIVATE_FLAG_ 128\n#define FUTEX_CMD_MASK_ ~(FUTEX_PRIVATE_FLAG_)\n\nstruct futex {\n    atomic_uint refcount;\n    struct mem *mem;\n    addr_t addr;\n    struct list queue;\n    struct list chain; // locked by futex_hash_lock\n};\n\nstruct futex_wait {\n    cond_t cond;\n    struct futex *futex; // will be changed by a requeue\n    struct list queue;\n};\n\n#define FUTEX_HASH_BITS 12\n#define FUTEX_HASH_SIZE (1 << FUTEX_HASH_BITS)\nstatic lock_t futex_lock = LOCK_INITIALIZER;\nstatic struct list futex_hash[FUTEX_HASH_SIZE];\n\nstatic void __attribute__((constructor)) init_futex_hash() {\n    for (int i = 0; i < FUTEX_HASH_SIZE; i++)\n        list_init(&futex_hash[i]);\n}\n\nstatic struct futex *futex_get_unlocked(addr_t addr) {\n    int hash = (addr ^ (unsigned long) current->mem) % FUTEX_HASH_SIZE;\n    struct list *bucket = &futex_hash[hash];\n    struct futex *futex;\n    list_for_each_entry(bucket, futex, chain) {\n        if (futex->addr == addr && futex->mem == current->mem) {\n            futex->refcount++;\n            return futex;\n        }\n    }\n\n    futex = malloc(sizeof(struct futex));\n    if (futex == NULL) {\n        unlock(&futex_lock);\n        return NULL;\n    }\n    futex->refcount = 1;\n    futex->mem = current->mem;\n    futex->addr = addr;\n    list_init(&futex->queue);\n    list_add(bucket, &futex->chain);\n    return futex;\n}\n\n// Returns the futex for the current process at the given addr, and locks it\n// Unlocked variant is available for times when you need to get two futexes at once\nstatic struct futex *futex_get(addr_t addr) {\n    lock(&futex_lock);\n    struct futex *futex = futex_get_unlocked(addr);\n    if (futex == NULL)\n        unlock(&futex_lock);\n    return futex;\n}\n\nstatic void futex_put_unlocked(struct futex *futex) {\n    if (--futex->refcount == 0) {\n        assert(list_empty(&futex->queue));\n        list_remove(&futex->chain);\n        free(futex);\n    }\n}\n\n// Must be called on the result of futex_get when you're done with it\n// Also has an unlocked version, for releasing the result of futex_get_unlocked\nstatic void futex_put(struct futex *futex) {\n    futex_put_unlocked(futex);\n    unlock(&futex_lock);\n}\n\nstatic int futex_load(struct futex *futex, dword_t *out) {\n    assert(futex->mem == current->mem);\n    read_wrlock(&current->mem->lock);\n    dword_t *ptr = mem_ptr(current->mem, futex->addr, MEM_READ);\n    read_wrunlock(&current->mem->lock);\n    if (ptr == NULL)\n        return 1;\n    *out = *ptr;\n    return 0;\n}\n\nstatic int futex_wait(addr_t uaddr, dword_t val, struct timespec *timeout) {\n    struct futex *futex = futex_get(uaddr);\n    int err = 0;\n    dword_t tmp;\n    if (futex_load(futex, &tmp))\n        err = _EFAULT;\n    else if (tmp != val)\n        err = _EAGAIN;\n    else {\n        struct futex_wait wait;\n        wait.cond = COND_INITIALIZER;\n        wait.futex = futex;\n        list_add_tail(&futex->queue, &wait.queue);\n        err = wait_for(&wait.cond, &futex_lock, timeout);\n        futex = wait.futex;\n        list_remove_safe(&wait.queue);\n    }\n    futex_put(futex);\n    STRACE(\"%d end futex(FUTEX_WAIT)\", current->pid);\n    return err;\n}\n\nstatic int futex_wakelike(int op, addr_t uaddr, dword_t wake_max, dword_t requeue_max, addr_t requeue_addr) {\n    struct futex *futex = futex_get(uaddr);\n\n    struct futex_wait *wait, *tmp;\n    unsigned woken = 0;\n    list_for_each_entry_safe(&futex->queue, wait, tmp, queue) {\n        if (woken >= wake_max)\n            break;\n        notify(&wait->cond);\n        list_remove(&wait->queue);\n        woken++;\n    }\n\n    if (op == FUTEX_REQUEUE_) {\n        struct futex *futex2 = futex_get_unlocked(requeue_addr);\n        unsigned requeued = 0;\n        list_for_each_entry_safe(&futex->queue, wait, tmp, queue) {\n            if (requeued >= requeue_max)\n                break;\n            // sketchy as hell\n            list_remove(&wait->queue);\n            list_add_tail(&futex2->queue, &wait->queue);\n            assert(futex->refcount > 1); // should be true because this function keeps a reference\n            futex->refcount--;\n            futex2->refcount++;\n            wait->futex = futex2;\n            requeued++;\n        }\n        futex_put_unlocked(futex2);\n        woken += requeued;\n    }\n\n    futex_put(futex);\n    return woken;\n}\n\nint futex_wake(addr_t uaddr, dword_t wake_max) {\n    return futex_wakelike(FUTEX_WAKE_, uaddr, wake_max, 0, 0);\n}\n\ndword_t sys_futex(addr_t uaddr, dword_t op, dword_t val, addr_t timeout_or_val2, addr_t uaddr2, dword_t val3) {\n    if (!(op & FUTEX_PRIVATE_FLAG_)) {\n        STRACE(\"!FUTEX_PRIVATE \");\n    }\n    struct timespec timeout = {0};\n    if ((op & FUTEX_CMD_MASK_) == FUTEX_WAIT_ && timeout_or_val2) {\n        struct timespec_ timeout_;\n        if (user_get(timeout_or_val2, timeout_))\n            return _EFAULT;\n        timeout.tv_sec = timeout_.sec;\n        timeout.tv_nsec = timeout_.nsec;\n    }\n    switch (op & FUTEX_CMD_MASK_) {\n        case FUTEX_WAIT_:\n            STRACE(\"futex(FUTEX_WAIT, %#x, %d, 0x%x {%ds %dns}) = ...\\n\", uaddr, val, timeout_or_val2, timeout.tv_sec, timeout.tv_nsec);\n            return futex_wait(uaddr, val, timeout_or_val2 ? &timeout : NULL);\n        case FUTEX_WAKE_:\n            STRACE(\"futex(FUTEX_WAKE, %#x, %d)\", uaddr, val);\n            return futex_wakelike(op & FUTEX_CMD_MASK_, uaddr, val, 0, 0);\n        case FUTEX_REQUEUE_:\n            STRACE(\"futex(FUTEX_REQUEUE, %#x, %d, %#x)\", uaddr, val, uaddr2);\n            return futex_wakelike(op & FUTEX_CMD_MASK_, uaddr, val, timeout_or_val2, uaddr2);\n    }\n    STRACE(\"futex(%#x, %d, %d, timeout=%#x, %#x, %d) \", uaddr, op, val, timeout_or_val2, uaddr2, val3);\n    FIXME(\"unsupported futex operation %d\", op);\n    return _ENOSYS;\n}\n\nstruct robust_list_head_ {\n    addr_t list;\n    dword_t offset;\n    addr_t list_op_pending;\n};\n\nint_t sys_set_robust_list(addr_t robust_list, dword_t len) {\n    STRACE(\"set_robust_list(%#x, %d)\", robust_list, len);\n    if (len != sizeof(struct robust_list_head_))\n        return _EINVAL;\n    current->robust_list = robust_list;\n    return 0;\n}\n\nint_t sys_get_robust_list(pid_t_ pid, addr_t robust_list_ptr, addr_t len_ptr) {\n    STRACE(\"get_robust_list(%d, %#x, %#x)\", pid, robust_list_ptr, len_ptr);\n\n    lock(&pids_lock);\n    struct task *task = pid_get_task(pid);\n    unlock(&pids_lock);\n    if (task != current)\n        return _EPERM;\n\n    if (user_put(robust_list_ptr, current->robust_list))\n        return _EFAULT;\n    if (user_put(len_ptr, (int[]) {sizeof(struct robust_list_head_)}))\n        return _EFAULT;\n    return 0;\n}\n"
  },
  {
    "path": "kernel/futex.h",
    "content": "#ifndef KERNEL_FUTEX_H\n#define KERNEL_FUTEX_H\n\nint futex_wake(addr_t uaddr, dword_t val);\n\n#endif\n"
  },
  {
    "path": "kernel/getset.c",
    "content": "#include \"kernel/calls.h\"\n#include \"kernel/task.h\"\n#include \"kernel/personality.h\"\n\npid_t_ sys_getpid() {\n    STRACE(\"getpid()\");\n    return current->tgid;\n}\npid_t_ sys_gettid() {\n    STRACE(\"gettid()\");\n    return current->pid;\n}\npid_t_ sys_getppid() {\n    STRACE(\"getppid()\");\n    pid_t_ ppid;\n    lock(&pids_lock);\n    if (current->parent != NULL)\n        ppid = current->parent->pid;\n    else\n        ppid = 0;\n    unlock(&pids_lock);\n    return ppid;\n}\n\ndword_t sys_getuid32() {\n    STRACE(\"getuid32()\");\n    return current->uid;\n}\ndword_t sys_getuid() {\n    STRACE(\"getuid()\");\n    return current->uid & 0xffff;\n}\n\ndword_t sys_geteuid32() {\n    STRACE(\"geteuid32()\");\n    return current->euid;\n}\ndword_t sys_geteuid() {\n    STRACE(\"geteuid()\");\n    return current->euid & 0xffff;\n}\n\nint_t sys_setuid(uid_t_ uid) {\n    STRACE(\"setuid(%d)\", uid);\n    if (superuser()) {\n        current->uid = current->suid = uid;\n    } else {\n        if (uid != current->uid && uid != current->suid)\n            return _EPERM;\n    }\n    current->euid = uid;\n    return 0;\n}\n\ndword_t sys_setresuid(uid_t_ ruid, uid_t_ euid, uid_t_ suid) {\n    STRACE(\"setresuid(%d, %d, %d)\", ruid, euid, suid);\n    if (!superuser()) {\n        if (ruid != (uid_t) -1 && ruid != current->uid && ruid != current->euid && ruid != current->suid)\n            return _EPERM;\n        if (euid != (uid_t) -1 && euid != current->uid && euid != current->euid && euid != current->suid)\n            return _EPERM;\n        if (suid != (uid_t) -1 && suid != current->uid && suid != current->euid && suid != current->suid)\n            return _EPERM;\n    }\n\n    if (ruid != (uid_t) -1)\n        current->uid = ruid;\n    if (euid != (uid_t) -1)\n        current->euid = euid;\n    if (suid != (uid_t) -1)\n        current->suid = suid;\n    return 0;\n}\n\nint_t sys_getresuid(addr_t ruid_addr, addr_t euid_addr, addr_t suid_addr) {\n    STRACE(\"getresuid(%#x, %#x, %#x)\", ruid_addr, euid_addr, suid_addr);\n    if (user_put(ruid_addr, current->uid))\n        return _EFAULT;\n    if (user_put(euid_addr, current->euid))\n        return _EFAULT;\n    if (user_put(suid_addr, current->suid))\n        return _EFAULT;\n    return 0;\n}\n\nint_t sys_setreuid(uid_t_ ruid, uid_t_ euid) {\n    return sys_setresuid(ruid, euid, -1);\n}\n\ndword_t sys_getgid32() {\n    STRACE(\"getgid32()\");\n    return current->gid;\n}\ndword_t sys_getgid() {\n    STRACE(\"getgid()\");\n    return current->gid & 0xffff;\n}\n\ndword_t sys_getegid32() {\n    STRACE(\"getegid32()\");\n    return current->egid;\n}\ndword_t sys_getegid() {\n    STRACE(\"getegid()\");\n    return current->egid & 0xffff;\n}\n\nint_t sys_setgid(uid_t_ gid) {\n    STRACE(\"setgid(%d)\", gid);\n    if (superuser()) {\n        current->gid = current->sgid = gid;\n    } else {\n        if (gid != current->gid && gid != current->sgid)\n            return _EPERM;\n    }\n    current->egid = gid;\n    return 0;\n}\n\ndword_t sys_setresgid(uid_t_ rgid, uid_t_ egid, uid_t_ sgid) {\n    STRACE(\"setresgid(%d, %d, %d)\", rgid, egid, sgid);\n    if (!superuser()) {\n        if (rgid != (uid_t) -1 && rgid != current->gid && rgid != current->egid && rgid != current->sgid)\n            return _EPERM;\n        if (egid != (uid_t) -1 && egid != current->gid && egid != current->egid && egid != current->sgid)\n            return _EPERM;\n        if (sgid != (uid_t) -1 && sgid != current->gid && sgid != current->egid && sgid != current->sgid)\n            return _EPERM;\n    }\n\n    if (rgid != (uid_t) -1)\n        current->gid = rgid;\n    if (egid != (uid_t) -1)\n        current->egid = egid;\n    if (sgid != (uid_t) -1)\n        current->sgid = sgid;\n    return 0;\n}\n\nint_t sys_getresgid(addr_t rgid_addr, addr_t egid_addr, addr_t sgid_addr) {\n    STRACE(\"getresgid(%#x, %#x, %#x)\", rgid_addr, egid_addr, sgid_addr);\n    if (user_put(rgid_addr, current->gid))\n        return _EFAULT;\n    if (user_put(egid_addr, current->egid))\n        return _EFAULT;\n    if (user_put(sgid_addr, current->sgid))\n        return _EFAULT;\n    return 0;\n}\n\nint_t sys_setregid(uid_t_ rgid, uid_t_ egid) {\n    return sys_setresgid(rgid, egid, -1);\n}\n\nint_t sys_getgroups(dword_t size, addr_t list) {\n    STRACE(\"getgroups(%d, %#x)\", size, list);\n    if (size == 0)\n        return current->ngroups;\n    if (size < current->ngroups)\n        return _EINVAL;\n    for (unsigned i = 0; i < current->ngroups; i++)\n        STRACE(\" %d\", current->groups[i]);\n    if (user_write(list, current->groups, current->ngroups * sizeof(uid_t_)))\n        return _EFAULT;\n    return current->ngroups;\n}\n\nint_t sys_setgroups(dword_t size, addr_t list) {\n    STRACE(\"setgroups(%d, %#x)\", size, list);\n    if (size > MAX_GROUPS)\n        return _EINVAL;\n    if (user_read(list, current->groups, size * sizeof(uid_t_)))\n        return _EFAULT;\n    for (unsigned i = 0; i < size; i++)\n        STRACE(\" %d\", current->groups[i]);\n    current->ngroups = size;\n    return 0;\n}\n\n// this does not really work\nint_t sys_capget(addr_t header_addr, addr_t data_addr) {\n    STRACE(\"capget(%#x, %#x)\", header_addr, data_addr);\n    return 0;\n}\nint_t sys_capset(addr_t header_addr, addr_t data_addr) {\n    STRACE(\"capset(%#x, %#x)\", header_addr, data_addr);\n    return 0;\n}\n\n// minimal version according to Linux sys/personality.h\nint_t sys_personality(dword_t persona) {\n    STRACE(\"personality(%#x)\", persona);\n    // Get the personality\n    if (persona == 0xffffffff)\n        return current->group->personality;\n\n    // ADDR_NO_RANDOMIZE is the only thing we support, and you can't turn it off\n    if (persona != ADDR_NO_RANDOMIZE_)\n        return _EINVAL;\n\n    return current->group->personality;\n}\n"
  },
  {
    "path": "kernel/group.c",
    "content": "#include \"util/list.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/task.h\"\n#include \"fs/tty.h\"\n\ndword_t sys_setpgid(pid_t_ id, pid_t_ pgid) {\n    STRACE(\"setpgid(%d, %d)\", id, pgid);\n    int err;\n    if (id == 0)\n        id = current->pid;\n    if (pgid == 0)\n        pgid = id;\n    lock(&pids_lock);\n    struct pid *pid = pid_get(id);\n    err = _ESRCH;\n    if (pid == NULL)\n        goto out;\n    struct task *task = pid->task;\n    if (task == NULL)\n        goto out;\n    struct tgroup *tgroup = task->group;\n\n    // you can only join a process group in the same session\n    if (id != pgid) {\n        // there has to be a process in pgrp that's in the same session as id\n        err = _EPERM;\n        struct pid *group_pid = pid_get(pgid);\n        if (group_pid == NULL || list_empty(&group_pid->pgroup))\n            goto out;\n        struct tgroup *group_first_tgroup = list_first_entry(&group_pid->pgroup, struct tgroup, pgroup);\n        if (tgroup->sid != group_first_tgroup->sid)\n            goto out;\n    }\n\n    // you can only change the process group of yourself or a child\n    err = _ESRCH;\n    if (task != current && task->parent != current)\n        goto out;\n    // a session leader cannot create a process group\n    err = _EPERM;\n    if (tgroup->sid == tgroup->leader->pid)\n        goto out;\n\n    // TODO cannot set process group of a child that has done exec\n\n    if (tgroup->pgid != pgid) {\n        list_remove(&tgroup->pgroup);\n        tgroup->pgid = pgid;\n        list_add(&pid->pgroup, &tgroup->pgroup);\n    }\n\n    err = 0;\nout:\n    unlock(&pids_lock);\n    return err;\n}\n\ndword_t sys_setpgrp() {\n    return sys_setpgid(0, 0);\n}\n\npid_t_ sys_getpgid(pid_t_ pid) {\n    STRACE(\"getpgid(%d)\", pid);\n    lock(&pids_lock);\n    struct task *task = current;\n    if (pid != 0)\n        task = pid_get_task(pid);\n    if (!task) {\n        unlock(&pids_lock);\n        return _ESRCH;\n    }\n    pid = task->group->pgid;\n    unlock(&pids_lock);\n    return pid;\n}\npid_t_ sys_getpgrp() {\n    return sys_getpgid(0);\n}\n\n// Must lock pids_lock and task->group->lock\nvoid task_leave_session(struct task *task) {\n    struct tgroup *group = task->group;\n    list_remove_safe(&group->session);\n    if (group->tty) {\n        lock(&ttys_lock);\n        if (list_empty(&pid_get(group->sid)->session)) {\n            lock(&group->tty->lock);\n            group->tty->session = 0;\n            unlock(&group->tty->lock);\n        }\n        tty_release(group->tty);\n        group->tty = NULL;\n        unlock(&ttys_lock);\n    }\n}\n\npid_t_ task_setsid(struct task *task) {\n    lock(&pids_lock);\n    struct tgroup *group = task->group;\n    pid_t_ new_sid = group->leader->pid;\n    if (group->pgid == new_sid || group->sid == new_sid) {\n        unlock(&pids_lock);\n        return _EPERM;\n    }\n\n    task_leave_session(task);\n    struct pid *pid = pid_get(task->pid);\n    list_add(&pid->session, &group->session);\n    group->sid = new_sid;\n\n    list_remove_safe(&group->pgroup);\n    list_add(&pid->pgroup, &group->pgroup);\n    group->pgid = new_sid;\n\n    unlock(&pids_lock);\n    return new_sid;\n}\n\ndword_t sys_setsid() {\n    STRACE(\"setsid()\");\n    return task_setsid(current);\n}\n\ndword_t sys_getsid() {\n    STRACE(\"getsid()\");\n    lock(&pids_lock);\n    pid_t_ sid = current->group->sid;\n    unlock(&pids_lock);\n    return sid;\n}\n\n"
  },
  {
    "path": "kernel/init.c",
    "content": "#include <signal.h>\n#include <string.h>\n#include <sys/stat.h>\n#include \"fs/devices.h\"\n#include \"fs/fd.h\"\n#include \"fs/real.h\"\n#include \"fs/tty.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/init.h\"\n#include \"kernel/personality.h\"\n\nint mount_root(const struct fs_ops *fs, const char *source) {\n    char source_realpath[MAX_PATH + 1];\n    if (realpath(source, source_realpath) == NULL)\n        return errno_map();\n    int err = do_mount(fs, source_realpath, \"\", \"\", 0);\n    if (err < 0)\n        return err;\n    return 0;\n}\n\nstatic void establish_signal_handlers() {\n    extern void sigusr1_handler(int sig);\n    struct sigaction sigact;\n    sigact.sa_handler = sigusr1_handler;\n    sigact.sa_flags = 0;\n    sigemptyset(&sigact.sa_mask);\n    sigaddset(&sigact.sa_mask, SIGUSR1);\n    sigaction(SIGUSR1, &sigact, NULL);\n    signal(SIGPIPE, SIG_IGN);\n}\n\n// copied from include/asm-generic/resource.h in the kernel\nstatic struct rlimit_ init_rlimits[16] = {\n    [RLIMIT_CPU_]        = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_FSIZE_]      = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_DATA_]       = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_STACK_]      = {8*1024*1024, RLIM_INFINITY_},\n    [RLIMIT_CORE_]       = {0, RLIM_INFINITY_},\n    [RLIMIT_RSS_]        = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_NPROC_]      = {1024, 1024},\n    [RLIMIT_NOFILE_]     = {1024, 4096},\n    [RLIMIT_MEMLOCK_]    = {64*1024, 64*1024},\n    [RLIMIT_AS_]         = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_LOCKS_]      = {RLIM_INFINITY_, RLIM_INFINITY_},\n    [RLIMIT_SIGPENDING_] = {1024, 1024},\n    [RLIMIT_MSGQUEUE_]   = {819200, 819200},\n    [RLIMIT_NICE_]       = {0, 0},\n    [RLIMIT_RTPRIO_]     = {0, 0},\n    [RLIMIT_RTTIME_]     = {RLIM_INFINITY_, RLIM_INFINITY_},\n};\n\n// TODO error propagation\nstatic struct task *construct_task(struct task *parent) {\n    struct task *task = task_create_(parent);\n\n    struct tgroup *group = malloc(sizeof(struct tgroup));\n    *group = (struct tgroup) {};\n    list_init(&group->threads);\n    lock_init(&group->lock);\n    cond_init(&group->child_exit);\n    cond_init(&group->stopped_cond);\n    memcpy(group->limits, init_rlimits, sizeof(init_rlimits));\n    group->leader = task;\n    group->personality = ADDR_NO_RANDOMIZE_;\n    list_add(&group->threads, &task->group_links);\n    task->group = group;\n    task->tgid = task->pid;\n    task_setsid(task);\n\n    task_set_mm(task, mm_new());\n    task->sighand = sighand_new();\n    task->files = fdtable_new(3); // why is there a 3 here\n\n    task->fs = fs_info_new();\n    task->fs->umask = 0022;\n    // we'll need to have current set to do the open call\n    struct task *old_current = current;\n    current = task;\n    task->fs->root = generic_open(\"/\", O_RDONLY_, 0);\n    if (IS_ERR(task->fs->root))\n        return ERR_PTR(task->fs->root);\n    task->fs->pwd = fd_retain(task->fs->root);\n    current = old_current;\n\n    return task;\n}\n\nint become_first_process() {\n    // now seems like a nice time\n    establish_signal_handlers();\n\n    struct task *task = construct_task(NULL);\n    if (IS_ERR(task))\n        return PTR_ERR(task);\n\n    current = task;\n    return 0;\n}\n\nint become_new_init_child() {\n    // locking? who needs locking?!\n    struct task *init = pid_get_task(1);\n    assert(init != NULL);\n\n    struct task *task = construct_task(init);\n    if (IS_ERR(task))\n        return PTR_ERR(task);\n\n    // these are things we definitely don't want to inherit\n    task->clear_tid = 0;\n    task->vfork = NULL;\n    task->blocked = task->pending = task->waiting = 0;\n    list_init(&task->queue);\n    // TODO: think about whether it would be a good idea to inherit fs_info\n\n    current = task;\n    return 0;\n}\n\nextern int console_major;\nextern int console_minor;\nvoid set_console_device(int major, int minor) {\n    console_major = major;\n    console_minor = minor;\n}\n\nint create_stdio(const char *file, int major, int minor) {\n    struct fd *fd = generic_open(file, O_RDWR_, 0);\n    if (IS_ERR(fd)) {\n        // fallback to adhoc files for stdio\n        fd = adhoc_fd_create(NULL);\n        fd->stat.rdev = dev_make(major, minor);\n        fd->stat.mode = S_IFCHR | S_IRUSR;\n        fd->flags = O_RDWR_;\n        int err = dev_open(major, minor, DEV_CHAR, fd);\n        if (err < 0)\n            return err;\n    }\n\n    fd->refcount = 0;\n    current->files->files[0] = fd_retain(fd);\n    current->files->files[1] = fd_retain(fd);\n    current->files->files[2] = fd_retain(fd);\n    return 0;\n}\n\nstatic struct fd *open_fd_from_actual_fd(int fd_no) {\n    struct fd *fd = adhoc_fd_create(&realfs_fdops);\n    if (fd == NULL) {\n        return NULL;\n    }\n    fd->real_fd = fd_no;\n    fd->dir = NULL;\n    return fd;\n}\n\nint create_piped_stdio() {\n    if (!(current->files->files[0] = open_fd_from_actual_fd(STDIN_FILENO))) {\n        return -1;\n    }\n    if (!(current->files->files[1] = open_fd_from_actual_fd(STDOUT_FILENO))) {\n        return -1;\n    }\n    if (!(current->files->files[2] = open_fd_from_actual_fd(STDERR_FILENO))) {\n        return -1;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "kernel/init.h",
    "content": "#ifndef KERNEL_INIT_H\n#define KERNEL_INIT_H\n\n#include \"fs/tty.h\"\n\n// Incredibly sloppy. Please do not reference as an example of good API design.\nint mount_root(const struct fs_ops *fs, const char *source);\nvoid set_console_device(int major, int minor);\nint become_first_process(void);\nint become_new_init_child(void);\nint create_stdio(const char *file, int major, int minor);\nint create_piped_stdio(void);\n\n#endif\n"
  },
  {
    "path": "kernel/ipc.c",
    "content": "#include \"kernel/calls.h\"\n\nint_t sys_ipc(uint_t call, int_t first, int_t second, int_t third, addr_t ptr, int_t fifth) {\n    STRACE(\"ipc(%u, %d, %d, %d, %#x, %d)\", call, first, second, third, ptr, fifth);\n    return _ENOSYS;\n}\n"
  },
  {
    "path": "kernel/log.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <syslog.h>\n#if LOG_HANDLER_NSLOG\n#include <CoreFoundation/CoreFoundation.h>\n#endif\n#if LOG_HANDLER_OS_LOG\n#include <os/log.h>\n#endif\n#include \"kernel/calls.h\"\n#include \"util/sync.h\"\n#include \"util/fifo.h\"\n#include \"kernel/task.h\"\n#include \"misc.h\"\n\n#define LOG_BUF_SHIFT 20\nstatic char log_buffer[1 << LOG_BUF_SHIFT];\nstatic struct fifo log_buf = FIFO_INIT(log_buffer);\nstatic size_t log_max_since_clear = 0;\nstatic lock_t log_lock = LOCK_INITIALIZER;\n\n#define SYSLOG_ACTION_CLOSE_ 0\n#define SYSLOG_ACTION_OPEN_ 1\n#define SYSLOG_ACTION_READ_ 2\n#define SYSLOG_ACTION_READ_ALL_ 3\n#define SYSLOG_ACTION_READ_CLEAR_ 4\n#define SYSLOG_ACTION_CLEAR_ 5\n#define SYSLOG_ACTION_CONSOLE_OFF_ 6\n#define SYSLOG_ACTION_CONSOLE_ON_ 7\n#define SYSLOG_ACTION_CONSOLE_LEVEL_ 8\n#define SYSLOG_ACTION_SIZE_UNREAD_ 9\n#define SYSLOG_ACTION_SIZE_BUFFER_ 10\n\nstatic int syslog_read(addr_t buf_addr, int_t len, int flags) {\n    if (len < 0)\n        return _EINVAL;\n    if (flags & FIFO_LAST) {\n        if ((size_t) len > log_max_since_clear)\n            len = log_max_since_clear;\n    } else {\n        if ((size_t) len > fifo_capacity(&log_buf))\n            len = fifo_capacity(&log_buf);\n    }\n    char *buf = malloc(len);\n    fifo_read(&log_buf, buf, len, flags);\n    int fail = user_write(buf_addr, buf, len);\n    free(buf);\n    if (fail)\n        return _EFAULT;\n    return len;\n}\n\nstatic int do_syslog(int type, addr_t buf_addr, int_t len) {\n    int res;\n    switch (type) {\n        case SYSLOG_ACTION_READ_:\n            return syslog_read(buf_addr, len, 0);\n        case SYSLOG_ACTION_READ_ALL_:\n            return syslog_read(buf_addr, len, FIFO_LAST | FIFO_PEEK);\n\n        case SYSLOG_ACTION_READ_CLEAR_:\n            res = syslog_read(buf_addr, len, FIFO_LAST | FIFO_PEEK);\n            if (res < 0)\n                return res;\n            fallthrough;\n        case SYSLOG_ACTION_CLEAR_:\n            log_max_since_clear = 0;\n            return 0;\n\n        case SYSLOG_ACTION_SIZE_UNREAD_:\n            return fifo_size(&log_buf);\n        case SYSLOG_ACTION_SIZE_BUFFER_:\n            return fifo_capacity(&log_buf);\n\n        case SYSLOG_ACTION_CLOSE_:\n        case SYSLOG_ACTION_OPEN_:\n        case SYSLOG_ACTION_CONSOLE_OFF_:\n        case SYSLOG_ACTION_CONSOLE_ON_:\n        case SYSLOG_ACTION_CONSOLE_LEVEL_:\n            return 0;\n        default:\n            return _EINVAL;\n    }\n}\nint_t sys_syslog(int_t type, addr_t buf_addr, int_t len) {\n    lock(&log_lock);\n    int retval = do_syslog(type, buf_addr, len);\n    unlock(&log_lock);\n    return retval;\n}\n\nstatic void log_buf_append(const char *msg) {\n    fifo_write(&log_buf, msg, strlen(msg), FIFO_OVERWRITE);\n    log_max_since_clear += strlen(msg);\n    if (log_max_since_clear > fifo_capacity(&log_buf))\n        log_max_since_clear = fifo_capacity(&log_buf);\n}\nstatic void log_line(const char *line);\nstatic void output_line(const char *line) {\n    // send it to stdout or wherever\n    log_line(line);\n    // add it to the circular buffer\n    log_buf_append(line);\n    log_buf_append(\"\\n\");\n}\n\nvoid ish_vprintk(const char *msg, va_list args) {\n    // format the message\n    // I'm trusting you to not pass an absurdly long message\n    static __thread char buf[16384] = \"\";\n    static __thread size_t buf_size = 0;\n    buf_size += vsprintf(buf + buf_size, msg, args);\n\n    // output up to the last newline, leave the rest in the buffer\n    lock(&log_lock);\n    char *b = buf;\n    char *p;\n    while ((p = strchr(b, '\\n')) != NULL) {\n        *p = '\\0';\n        output_line(b);\n        *p = '\\n';\n        buf_size -= p + 1 - b;\n        b = p + 1;\n    }\n    unlock(&log_lock);\n    memmove(buf, b, strlen(b) + 1);\n}\nvoid ish_printk(const char *msg, ...) {\n    va_list args;\n    va_start(args, msg);\n    ish_vprintk(msg, args);\n    va_end(args);\n}\n\n#if LOG_HANDLER_DPRINTF\n#define NEWLINE \"\\r\\n\"\nstatic void log_line(const char *line) {\n    struct iovec output[2] = {{(void *) line, strlen(line)}, {\"\\n\", 1}};\n    writev(666, output, 2);\n}\n#elif LOG_HANDLER_NSLOG\nstatic void log_line(const char *line) {\n    extern void NSLog(CFStringRef msg, ...);\n    NSLog(CFSTR(\"%s\"), line);\n}\n#elif LOG_HANDLER_SYSLOG\nstatic void log_line(const char *line) {\n    syslog(LOG_DEBUG, \"%s\", line);\n}\n#elif LOG_HANDLER_OS_LOG\nstatic void log_line(const char *line) {\n    os_log_fault(OS_LOG_DEFAULT, \"%s\", line);\n}\n#elif LOG_HANDLER_STDERR\nstatic void log_line(const char *line) {\n    fprintf(stderr, \"%s\\n\", line);\n}\n#endif\n\nstatic void default_die_handler(const char *msg) {\n    printk(\"%s\\n\", msg);\n}\nvoid (*die_handler)(const char *msg) = default_die_handler;\n_Noreturn void die(const char *msg, ...);\nvoid die(const char *msg, ...) {\n    va_list args;\n    va_start(args, msg);\n    char buf[4096];\n    vsprintf(buf, msg, args);\n    die_handler(buf);\n    abort();\n    va_end(args);\n}\n\n// fun little utility function\nint current_pid() {\n    if (current)\n        return current->pid;\n    return -1;\n}\n"
  },
  {
    "path": "kernel/memory.c",
    "content": "#include <fcntl.h>\n#include <unistd.h>\n#include <sys/mman.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <errno.h>\n\n#define DEFAULT_CHANNEL memory\n#include \"debug.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/signal.h\"\n#include \"kernel/memory.h\"\n#include \"asbestos/asbestos.h\"\n#include \"kernel/vdso.h\"\n#include \"kernel/task.h\"\n#include \"fs/fd.h\"\n\n// increment the change count\nstatic void mem_changed(struct mem *mem);\nstatic struct mmu_ops mem_mmu_ops;\n\nvoid mem_init(struct mem *mem) {\n    mem->pgdir = calloc(MEM_PGDIR_SIZE, sizeof(struct pt_entry *));\n    mem->pgdir_used = 0;\n    mem->mmu.ops = &mem_mmu_ops;\n    mem->mmu.asbestos = asbestos_new(&mem->mmu);\n    mem->mmu.changes = 0;\n    wrlock_init(&mem->lock);\n}\n\nvoid mem_destroy(struct mem *mem) {\n    write_wrlock(&mem->lock);\n    pt_unmap_always(mem, 0, MEM_PAGES);\n    asbestos_free(mem->mmu.asbestos);\n    for (int i = 0; i < MEM_PGDIR_SIZE; i++) {\n        if (mem->pgdir[i] != NULL)\n            free(mem->pgdir[i]);\n    }\n    free(mem->pgdir);\n    write_wrunlock(&mem->lock);\n    wrlock_destroy(&mem->lock);\n}\n\n#define PGDIR_TOP(page) ((page) >> 10)\n#define PGDIR_BOTTOM(page) ((page) & (MEM_PGDIR_SIZE - 1))\n\nstatic struct pt_entry *mem_pt_new(struct mem *mem, page_t page) {\n    struct pt_entry *pgdir = mem->pgdir[PGDIR_TOP(page)];\n    if (pgdir == NULL) {\n        pgdir = mem->pgdir[PGDIR_TOP(page)] = calloc(MEM_PGDIR_SIZE, sizeof(struct pt_entry));\n        mem->pgdir_used++;\n    }\n    return &pgdir[PGDIR_BOTTOM(page)];\n}\n\nstruct pt_entry *mem_pt(struct mem *mem, page_t page) {\n    struct pt_entry *pgdir = mem->pgdir[PGDIR_TOP(page)];\n    if (pgdir == NULL)\n        return NULL;\n    struct pt_entry *entry = &pgdir[PGDIR_BOTTOM(page)];\n    if (entry->data == NULL)\n        return NULL;\n    return entry;\n}\n\nstatic void mem_pt_del(struct mem *mem, page_t page) {\n    struct pt_entry *entry = mem_pt(mem, page);\n    if (entry != NULL)\n        entry->data = NULL;\n}\n\nvoid mem_next_page(struct mem *mem, page_t *page) {\n    (*page)++;\n    if (*page >= MEM_PAGES)\n        return;\n    while (*page < MEM_PAGES && mem->pgdir[PGDIR_TOP(*page)] == NULL)\n        *page = (*page - PGDIR_BOTTOM(*page)) + MEM_PGDIR_SIZE;\n}\n\npage_t pt_find_hole(struct mem *mem, pages_t size) {\n    page_t hole_end = 0; // this can never be used before initializing but gcc doesn't realize\n    bool in_hole = false;\n    for (page_t page = 0xf7ffd; page > 0x40000; page--) {\n        // I don't know how this works but it does\n        if (!in_hole && mem_pt(mem, page) == NULL) {\n            in_hole = true;\n            hole_end = page + 1;\n        }\n        if (mem_pt(mem, page) != NULL)\n            in_hole = false;\n        else if (hole_end - page == size)\n            return page;\n    }\n    return BAD_PAGE;\n}\n\nbool pt_is_hole(struct mem *mem, page_t start, pages_t pages) {\n    for (page_t page = start; page < start + pages; page++) {\n        if (mem_pt(mem, page) != NULL)\n            return false;\n    }\n    return true;\n}\n\nint pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, size_t offset, unsigned flags) {\n    if (memory == MAP_FAILED)\n        return errno_map();\n\n    // If this fails, the munmap in pt_unmap would probably fail.\n    assert((uintptr_t) memory % real_page_size == 0 || memory == vdso_data);\n\n    struct data *data = malloc(sizeof(struct data));\n    if (data == NULL)\n        return _ENOMEM;\n    *data = (struct data) {\n        .data = memory,\n        .size = pages * PAGE_SIZE + offset,\n\n#if LEAK_DEBUG\n        .pid = current ? current->pid : 0,\n        .dest = start << PAGE_BITS,\n#endif\n    };\n\n    for (page_t page = start; page < start + pages; page++) {\n        if (mem_pt(mem, page) != NULL)\n            pt_unmap(mem, page, 1);\n        data->refcount++;\n        struct pt_entry *pt = mem_pt_new(mem, page);\n        pt->data = data;\n        pt->offset = ((page - start) << PAGE_BITS) + offset;\n        pt->flags = flags;\n    }\n    return 0;\n}\n\nint pt_unmap(struct mem *mem, page_t start, pages_t pages) {\n    for (page_t page = start; page < start + pages; page++)\n        if (mem_pt(mem, page) == NULL)\n            return -1;\n    return pt_unmap_always(mem, start, pages);\n}\n\nint pt_unmap_always(struct mem *mem, page_t start, pages_t pages) {\n    for (page_t page = start; page < start + pages; mem_next_page(mem, &page)) {\n        struct pt_entry *pt = mem_pt(mem, page);\n        if (pt == NULL)\n            continue;\n        asbestos_invalidate_page(mem->mmu.asbestos, page);\n        struct data *data = pt->data;\n        mem_pt_del(mem, page);\n        if (--data->refcount == 0) {\n            // vdso wasn't allocated with mmap, it's just in our data segment\n            if (data->data != vdso_data) {\n                int err = munmap(data->data, data->size);\n                if (err != 0)\n                    die(\"munmap(%p, %lu) failed: %s\", data->data, data->size, strerror(errno));\n            }\n            if (data->fd != NULL) {\n                fd_close(data->fd);\n            }\n            free(data);\n        }\n    }\n    mem_changed(mem);\n    return 0;\n}\n\nint pt_map_nothing(struct mem *mem, page_t start, pages_t pages, unsigned flags) {\n    if (pages == 0) return 0;\n    void *memory = mmap(NULL, pages * PAGE_SIZE,\n            PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);\n    return pt_map(mem, start, pages, memory, 0, flags | P_ANONYMOUS);\n}\n\nint pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags) {\n    for (page_t page = start; page < start + pages; page++)\n        if (mem_pt(mem, page) == NULL)\n            return _ENOMEM;\n    for (page_t page = start; page < start + pages; page++) {\n        struct pt_entry *entry = mem_pt(mem, page);\n        int old_flags = entry->flags;\n        entry->flags = flags;\n        // check if protection is increasing\n        if ((flags & ~old_flags) & (P_READ|P_WRITE)) {\n            void *data = (char *) entry->data->data + entry->offset;\n            // force to be page aligned\n            data = (void *) ((uintptr_t) data & ~(real_page_size - 1));\n            int prot = PROT_READ;\n            if (flags & P_WRITE) prot |= PROT_WRITE;\n            if (mprotect(data, real_page_size, prot) < 0)\n                return errno_map();\n        }\n    }\n    mem_changed(mem);\n    return 0;\n}\n\nint pt_copy_on_write(struct mem *src, struct mem *dst, page_t start, page_t pages) {\n    for (page_t page = start; page < start + pages; mem_next_page(src, &page)) {\n        struct pt_entry *entry = mem_pt(src, page);\n        if (entry == NULL)\n            continue;\n        if (pt_unmap_always(dst, page, 1) < 0)\n            return -1;\n        if (!(entry->flags & P_SHARED))\n            entry->flags |= P_COW;\n        entry->data->refcount++;\n        struct pt_entry *dst_entry = mem_pt_new(dst, page);\n        dst_entry->data = entry->data;\n        dst_entry->offset = entry->offset;\n        dst_entry->flags = entry->flags;\n    }\n    mem_changed(src);\n    mem_changed(dst);\n    return 0;\n}\n\nstatic void mem_changed(struct mem *mem) {\n    mem->mmu.changes++;\n}\n\n// This version will return NULL instead of making necessary pagetable changes.\n// Used by the emulator to avoid deadlocks.\nstatic void *mem_ptr_nofault(struct mem *mem, addr_t addr, int type) {\n    struct pt_entry *entry = mem_pt(mem, PAGE(addr));\n    if (entry == NULL)\n        return NULL;\n    if (type == MEM_WRITE && !P_WRITABLE(entry->flags))\n        return NULL;\n    return entry->data->data + entry->offset + PGOFFSET(addr);\n}\n\nvoid *mem_ptr(struct mem *mem, addr_t addr, int type) {\n    void *old_ptr = mem_ptr_nofault(mem, addr, type); // just for an assert\n\n    page_t page = PAGE(addr);\n    struct pt_entry *entry = mem_pt(mem, page);\n\n    if (entry == NULL) {\n        // page does not exist\n        // look to see if the next VM region is willing to grow down\n        page_t p = page + 1;\n        while (p < MEM_PAGES && mem_pt(mem, p) == NULL)\n            p++;\n        if (p >= MEM_PAGES)\n            return NULL;\n        if (!(mem_pt(mem, p)->flags & P_GROWSDOWN))\n            return NULL;\n\n        // Changing memory maps must be done with the write lock. But this is\n        // called with the read lock.\n        // This locking stuff is copy/pasted for all the code in this function\n        // which changes memory maps.\n        // TODO: factor the lock/unlock code here into a new function. Do this\n        // next time you touch this function.\n        read_wrunlock(&mem->lock);\n        write_wrlock(&mem->lock);\n        pt_map_nothing(mem, page, 1, P_WRITE | P_GROWSDOWN);\n        write_wrunlock(&mem->lock);\n        read_wrlock(&mem->lock);\n\n        entry = mem_pt(mem, page);\n    }\n\n    if (entry != NULL && (type == MEM_WRITE || type == MEM_WRITE_PTRACE)) {\n        // if page is unwritable, well tough luck\n        if (type != MEM_WRITE_PTRACE && !(entry->flags & P_WRITE))\n            return NULL;\n        if (type == MEM_WRITE_PTRACE) {\n            // TODO: Is P_WRITE really correct? The page shouldn't be writable without ptrace.\n            entry->flags |= P_WRITE | P_COW;\n        }\n        // get rid of any compiled blocks in this page\n        asbestos_invalidate_page(mem->mmu.asbestos, page);\n        // if page is cow, ~~milk~~ copy it\n        if (entry->flags & P_COW) {\n            void *data = (char *) entry->data->data + entry->offset;\n            void *copy = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,\n                    MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);\n\n            // copy/paste from above\n            read_wrunlock(&mem->lock);\n            write_wrlock(&mem->lock);\n            memcpy(copy, data, PAGE_SIZE);\n            pt_map(mem, page, 1, copy, 0, entry->flags &~ P_COW);\n            write_wrunlock(&mem->lock);\n            read_wrlock(&mem->lock);\n        }\n    }\n\n    void *ptr = mem_ptr_nofault(mem, addr, type);\n    assert(old_ptr == NULL || old_ptr == ptr || type == MEM_WRITE_PTRACE);\n    return ptr;\n}\n\nstatic void *mem_mmu_translate(struct mmu *mmu, addr_t addr, int type) {\n    return mem_ptr_nofault(container_of(mmu, struct mem, mmu), addr, type);\n}\n\nstatic struct mmu_ops mem_mmu_ops = {\n    .translate = mem_mmu_translate,\n};\n\nint mem_segv_reason(struct mem *mem, addr_t addr) {\n    struct pt_entry *pt = mem_pt(mem, PAGE(addr));\n    if (pt == NULL)\n        return SEGV_MAPERR_;\n    return SEGV_ACCERR_;\n}\n\nsize_t real_page_size;\n__attribute__((constructor)) static void get_real_page_size() {\n    real_page_size = sysconf(_SC_PAGESIZE);\n}\n\nvoid mem_coredump(struct mem *mem, const char *file) {\n    int fd = open(file, O_CREAT | O_RDWR | O_TRUNC, 0666);\n    if (fd < 0) {\n        perror(\"open\");\n        return;\n    }\n    if (ftruncate(fd, 0xffffffff) < 0) {\n        perror(\"ftruncate\");\n        return;\n    }\n\n    int pages = 0;\n    for (page_t page = 0; page < MEM_PAGES; page++) {\n        struct pt_entry *entry = mem_pt(mem, page);\n        if (entry == NULL)\n            continue;\n        pages++;\n        if (lseek(fd, page << PAGE_BITS, SEEK_SET) < 0) {\n            perror(\"lseek\");\n            return;\n        }\n        if (write(fd, entry->data->data, PAGE_SIZE) < 0) {\n            perror(\"write\");\n            return;\n        }\n    }\n    printk(\"dumped %d pages\\n\", pages);\n    close(fd);\n}\n"
  },
  {
    "path": "kernel/memory.h",
    "content": "#ifndef MEMORY_H\n#define MEMORY_H\n\n#include <stdatomic.h>\n#include <unistd.h>\n#include <stdbool.h>\n#include \"emu/mmu.h\"\n#include \"util/list.h\"\n#include \"util/sync.h\"\n#include \"misc.h\"\n\nstruct mem {\n    struct pt_entry **pgdir;\n    int pgdir_used;\n\n    struct mmu mmu;\n\n    wrlock_t lock;\n};\n#define MEM_PGDIR_SIZE (1 << 10)\n\n// Initialize the address space\nvoid mem_init(struct mem *mem);\n// Uninitialize the address space\nvoid mem_destroy(struct mem *mem);\n// Return the pagetable entry for the given page\nstruct pt_entry *mem_pt(struct mem *mem, page_t page);\n// Increment *page, skipping over unallocated page directories. Intended to be\n// used as the incremenent in a for loop to traverse mappings.\nvoid mem_next_page(struct mem *mem, page_t *page);\n\n#define BYTES_ROUND_DOWN(bytes) (PAGE(bytes) << PAGE_BITS)\n#define BYTES_ROUND_UP(bytes) (PAGE_ROUND_UP(bytes) << PAGE_BITS)\n\n#define LEAK_DEBUG 0\n\nstruct data {\n    void *data; // immutable\n    size_t size; // also immutable\n    atomic_uint refcount;\n\n    // for display in /proc/pid/maps\n    struct fd *fd;\n    size_t file_offset;\n    const char *name;\n#if LEAK_DEBUG\n    int pid;\n    addr_t dest;\n#endif\n};\nstruct pt_entry {\n    struct data *data;\n    size_t offset;\n    unsigned flags;\n    struct list blocks[2];\n};\n// page flags\n// P_READ and P_EXEC are ignored for now\n#define P_READ (1 << 0)\n#define P_WRITE (1 << 1)\n#undef P_EXEC // defined in sys/proc.h on darwin\n#define P_EXEC (1 << 2)\n#define P_RWX (P_READ | P_WRITE | P_EXEC)\n#define P_GROWSDOWN (1 << 3)\n#define P_COW (1 << 4)\n#define P_WRITABLE(flags) (flags & P_WRITE && !(flags & P_COW))\n\n// mapping was created with pt_map_nothing\n#define P_ANONYMOUS (1 << 6)\n// mapping was created with MAP_SHARED, should not CoW\n#define P_SHARED (1 << 7)\n\nbool pt_is_hole(struct mem *mem, page_t start, pages_t pages);\npage_t pt_find_hole(struct mem *mem, pages_t size);\n\n// Map memory + offset into fake memory, unmapping existing mappings. Takes\n// ownership of memory. It will be freed with:\n// munmap(memory, pages * PAGE_SIZE)\nint pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, size_t offset, unsigned flags);\n// Map empty space into fake memory\nint pt_map_nothing(struct mem *mem, page_t page, pages_t pages, unsigned flags);\n// Unmap fake memory, return -1 if any part of the range isn't mapped and 0 otherwise\nint pt_unmap(struct mem *mem, page_t start, pages_t pages);\n// like pt_unmap but doesn't care if part of the range isn't mapped\nint pt_unmap_always(struct mem *mem, page_t start, pages_t pages);\n// Set the flags on memory\nint pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags);\n// Copy pages from src memory to dst memory using copy-on-write\nint pt_copy_on_write(struct mem *src, struct mem *dst, page_t start, page_t pages);\n\n// Must call with mem read-locked.\nvoid *mem_ptr(struct mem *mem, addr_t addr, int type);\nint mem_segv_reason(struct mem *mem, addr_t addr);\n\nextern size_t real_page_size;\n\n#endif\n"
  },
  {
    "path": "kernel/misc.c",
    "content": "#include <string.h>\n#include \"kernel/calls.h\"\n\n#define PRCTL_SET_KEEPCAPS_ 8\n#define PRCTL_SET_NAME_ 15\n\nint_t sys_prctl(dword_t option, uint_t arg2, uint_t UNUSED(arg3), uint_t UNUSED(arg4), uint_t UNUSED(arg5)) {\n    switch (option) {\n        case PRCTL_SET_KEEPCAPS_:\n            // stub\n            return 0;\n        case PRCTL_SET_NAME_: {\n            char name[16];\n            if (user_read_string(arg2, name, sizeof(name) - 1))\n                return _EFAULT;\n            name[sizeof(name) - 1] = '\\0';\n            STRACE(\"prctl(PRCTL_SET_NAME, \\\"%s\\\")\", name);\n            strcpy(current->comm, name);\n            return 0;\n        }\n        default:\n            STRACE(\"prctl(%#x)\", option);\n            return _EINVAL;\n    }\n}\n\nint_t sys_arch_prctl(int_t code, addr_t addr) {\n    STRACE(\"arch_prctl(%#x, %#x)\", code, addr);\n    return _EINVAL;\n}\n\n#define REBOOT_MAGIC1 0xfee1dead\n#define REBOOT_MAGIC2 672274793\n#define REBOOT_MAGIC2A 85072278\n#define REBOOT_MAGIC2B 369367448\n#define REBOOT_MAGIC2C 537993216\n\n#define REBOOT_CMD_CAD_OFF 0\n#define REBOOT_CMD_CAD_ON 0x89abcdef\n\nint_t sys_reboot(int_t magic, int_t magic2, int_t cmd) {\n    STRACE(\"reboot(%#x, %d, %d)\", magic, magic2, cmd);\n    if (!superuser())\n        return _EPERM;\n    if (magic != (int) REBOOT_MAGIC1 ||\n            (magic2 != REBOOT_MAGIC2 &&\n             magic2 != REBOOT_MAGIC2A &&\n             magic2 != REBOOT_MAGIC2B &&\n             magic2 != REBOOT_MAGIC2C))\n        return _EINVAL;\n\n    switch (cmd) {\n        case REBOOT_CMD_CAD_ON:\n        case REBOOT_CMD_CAD_OFF:\n            return 0;\n        default:\n            return _EPERM;\n    }\n}\n"
  },
  {
    "path": "kernel/mm.h",
    "content": "#ifndef KERNEL_MM_H\n#define KERNEL_MM_H\n\n#include \"kernel/memory.h\"\n#include \"misc.h\"\n\n// uses mem.lock instead of having a lock of its own\nstruct mm {\n    atomic_uint refcount;\n    struct mem mem;\n\n    addr_t vdso; // immutable\n    addr_t start_brk; // immutable\n    addr_t brk;\n\n    // crap for procfs\n    addr_t argv_start;\n    addr_t argv_end;\n    addr_t env_start;\n    addr_t env_end;\n    addr_t auxv_start;\n    addr_t auxv_end;\n    addr_t stack_start;\n    struct fd *exefile;\n};\n\n// Create a new address space\nstruct mm *mm_new(void);\n// Clone (COW) the address space\nstruct mm *mm_copy(struct mm *mm);\n// Increment the refcount\nvoid mm_retain(struct mm *mem);\n// Decrement the refcount, destroy everything in the space if 0\nvoid mm_release(struct mm *mem);\n\n#endif\n"
  },
  {
    "path": "kernel/mmap.c",
    "content": "#include <string.h>\n#include \"debug.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/task.h\"\n#include \"fs/fd.h\"\n#include \"kernel/memory.h\"\n#include \"kernel/mm.h\"\n\nstruct mm *mm_new() {\n    struct mm *mm = malloc(sizeof(struct mm));\n    if (mm == NULL)\n        return NULL;\n    mem_init(&mm->mem);\n    mm->start_brk = mm->brk = 0; // should get overwritten by exec\n    mm->exefile = NULL;\n    mm->refcount = 1;\n    return mm;\n}\n\nstruct mm *mm_copy(struct mm *mm) {\n    struct mm *new_mm = malloc(sizeof(struct mm));\n    if (new_mm == NULL)\n        return NULL;\n    *new_mm = *mm;\n    // Fix wrlock_init failing because it thinks it's reinitializing the same lock\n    memset(&new_mm->mem.lock, 0, sizeof(new_mm->mem.lock));\n    new_mm->refcount = 1;\n    mem_init(&new_mm->mem);\n    fd_retain(new_mm->exefile);\n    write_wrlock(&mm->mem.lock);\n    pt_copy_on_write(&mm->mem, &new_mm->mem, 0, MEM_PAGES);\n    write_wrunlock(&mm->mem.lock);\n    return new_mm;\n}\n\nvoid mm_retain(struct mm *mm) {\n    mm->refcount++;\n}\n\nvoid mm_release(struct mm *mm) {\n    if (--mm->refcount == 0) {\n        if (mm->exefile != NULL)\n            fd_close(mm->exefile);\n        mem_destroy(&mm->mem);\n        free(mm);\n    }\n}\n\nstatic addr_t do_mmap(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset) {\n    int err;\n    pages_t pages = PAGE_ROUND_UP(len);\n    if (!pages) return _EINVAL;\n    page_t page;\n    if (addr != 0) {\n        if (PGOFFSET(addr) != 0)\n            return _EINVAL;\n        page = PAGE(addr);\n        if (!(flags & MMAP_FIXED) && !pt_is_hole(current->mem, page, pages)) {\n            addr = 0;\n        }\n    }\n    if (addr == 0) {\n        page = pt_find_hole(current->mem, pages);\n        if (page == BAD_PAGE)\n            return _ENOMEM;\n    }\n\n    if (flags & MMAP_SHARED)\n        prot |= P_SHARED;\n\n    if (flags & MMAP_ANONYMOUS) {\n        if ((err = pt_map_nothing(current->mem, page, pages, prot)) < 0)\n            return err;\n    } else {\n        // fd must be valid\n        struct fd *fd = f_get(fd_no);\n        if (fd == NULL)\n            return _EBADF;\n        if (fd->ops->mmap == NULL)\n            return _ENODEV;\n        if ((err = fd->ops->mmap(fd, current->mem, page, pages, offset, prot, flags)) < 0)\n            return err;\n        mem_pt(current->mem, page)->data->fd = fd_retain(fd);\n        mem_pt(current->mem, page)->data->file_offset = offset;\n    }\n    return page << PAGE_BITS;\n}\n\nstatic addr_t mmap_common(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset) {\n    STRACE(\"mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)\", addr, len, prot, flags, fd_no, offset);\n    if (len == 0)\n        return _EINVAL;\n    if (prot & ~P_RWX)\n        return _EINVAL;\n    if ((flags & MMAP_PRIVATE) && (flags & MMAP_SHARED))\n        return _EINVAL;\n\n    write_wrlock(&current->mem->lock);\n    addr_t res = do_mmap(addr, len, prot, flags, fd_no, offset);\n    write_wrunlock(&current->mem->lock);\n    return res;\n}\n\naddr_t sys_mmap2(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset) {\n    return mmap_common(addr, len, prot, flags, fd_no, offset << PAGE_BITS);\n}\n\nstruct mmap_arg_struct {\n    dword_t addr, len, prot, flags, fd, offset;\n};\n\naddr_t sys_mmap(addr_t args_addr) {\n    struct mmap_arg_struct args;\n    if (user_get(args_addr, args))\n        return _EFAULT;\n    return mmap_common(args.addr, args.len, args.prot, args.flags, args.fd, args.offset);\n}\n\nint_t sys_munmap(addr_t addr, uint_t len) {\n    STRACE(\"munmap(0x%x, 0x%x)\", addr, len);\n    if (PGOFFSET(addr) != 0)\n        return _EINVAL;\n    if (len == 0)\n        return _EINVAL;\n    write_wrlock(&current->mem->lock);\n    int err = pt_unmap_always(current->mem, PAGE(addr), PAGE_ROUND_UP(len));\n    write_wrunlock(&current->mem->lock);\n    if (err < 0)\n        return _EINVAL;\n    return 0;\n}\n\n#define MREMAP_MAYMOVE_ 1\n#define MREMAP_FIXED_ 2\n\nint_t sys_mremap(addr_t addr, dword_t old_len, dword_t new_len, dword_t flags) {\n    STRACE(\"mremap(%#x, %#x, %#x, %d)\", addr, old_len, new_len, flags);\n    if (PGOFFSET(addr) != 0)\n        return _EINVAL;\n    if (flags & ~(MREMAP_MAYMOVE_ | MREMAP_FIXED_))\n        return _EINVAL;\n    if (flags & MREMAP_FIXED_) {\n        FIXME(\"missing MREMAP_FIXED\");\n        return _EINVAL;\n    }\n    pages_t old_pages = PAGE(old_len);\n    pages_t new_pages = PAGE(new_len);\n\n    // shrinking always works\n    if (new_pages <= old_pages) {\n        int err = pt_unmap(current->mem, PAGE(addr) + new_pages, old_pages - new_pages);\n        if (err < 0)\n            return _EFAULT;\n        return addr;\n    }\n\n    struct pt_entry *entry = mem_pt(current->mem, PAGE(addr));\n    if (entry == NULL)\n        return _EFAULT;\n    dword_t pt_flags = entry->flags;\n    for (page_t page = PAGE(addr); page < PAGE(addr) + old_pages; page++) {\n        entry = mem_pt(current->mem, page);\n        if (entry == NULL && entry->flags != pt_flags)\n            return _EFAULT;\n    }\n    if (!(pt_flags & P_ANONYMOUS)) {\n        FIXME(\"mremap grow on file mappings\");\n        return _EFAULT;\n    }\n    page_t extra_start = PAGE(addr) + old_pages;\n    pages_t extra_pages = new_pages - old_pages;\n    if (!pt_is_hole(current->mem, extra_start, extra_pages))\n        return _ENOMEM;\n    int err = pt_map_nothing(current->mem, extra_start, extra_pages, pt_flags);\n    if (err < 0)\n        return err;\n    return addr;\n}\n\nint_t sys_mprotect(addr_t addr, uint_t len, int_t prot) {\n    STRACE(\"mprotect(0x%x, 0x%x, 0x%x)\", addr, len, prot);\n    if (PGOFFSET(addr) != 0)\n        return _EINVAL;\n    if (prot & ~P_RWX)\n        return _EINVAL;\n    pages_t pages = PAGE_ROUND_UP(len);\n    write_wrlock(&current->mem->lock);\n    int err = pt_set_flags(current->mem, PAGE(addr), pages, prot);\n    write_wrunlock(&current->mem->lock);\n    return err;\n}\n\ndword_t sys_madvise(addr_t UNUSED(addr), dword_t UNUSED(len), dword_t UNUSED(advice)) {\n    // portable applications should not rely on linux's destructive semantics for MADV_DONTNEED.\n    return 0;\n}\n\ndword_t sys_mbind(addr_t UNUSED(addr), dword_t UNUSED(len), int_t UNUSED(mode),\n        addr_t UNUSED(nodemask), dword_t UNUSED(maxnode), uint_t UNUSED(flags)) {\n    return 0;\n}\n\nint_t sys_mlock(addr_t UNUSED(addr), dword_t UNUSED(len)) {\n    return 0;\n}\n\nint_t sys_msync(addr_t UNUSED(addr), dword_t UNUSED(len), int_t UNUSED(flags)) {\n    return 0;\n}\n\naddr_t sys_brk(addr_t new_brk) {\n    STRACE(\"brk(0x%x)\", new_brk);\n    struct mm *mm = current->mm;\n\n    write_wrlock(&mm->mem.lock);\n    if (new_brk < mm->start_brk)\n        goto out;\n    addr_t old_brk = mm->brk;\n\n    if (new_brk > old_brk) {\n        // expand heap: map region from old_brk to new_brk\n        // round up because of the definition of brk: \"the first location after the end of the uninitialized data segment.\" (brk(2))\n        // if the brk is 0x2000, page 0x2000 shouldn't be mapped, but it should be if the brk is 0x2001.\n        page_t start = PAGE_ROUND_UP(old_brk);\n        pages_t size = PAGE_ROUND_UP(new_brk) - PAGE_ROUND_UP(old_brk);\n        if (!pt_is_hole(&mm->mem, start, size))\n            goto out;\n        int err = pt_map_nothing(&mm->mem, start, size, P_WRITE);\n        if (err < 0)\n            goto out;\n    } else if (new_brk < old_brk) {\n        // shrink heap: unmap region from new_brk to old_brk\n        // first page to unmap is PAGE(new_brk)\n        // last page to unmap is PAGE(old_brk)\n        pt_unmap_always(&mm->mem, PAGE(new_brk), PAGE(old_brk) - PAGE(new_brk));\n    }\n\n    mm->brk = new_brk;\nout:;\n    addr_t brk = mm->brk;\n    write_wrunlock(&mm->mem.lock);\n    return brk;\n}\n"
  },
  {
    "path": "kernel/personality.h",
    "content": "#ifndef KERNEL_PERSONALITY_H\n#define KERNEL_PERSONALITY_H\n\n#define ADDR_NO_RANDOMIZE_ 0x0040000\n\n#endif /* KERNEL_PERSONALITY_H */\n"
  },
  {
    "path": "kernel/poll.c",
    "content": "#include <string.h>\n#include \"debug.h\"\n#include \"kernel/fs.h\"\n#include \"fs/fd.h\"\n#include \"fs/poll.h\"\n#include \"kernel/calls.h\"\n\nstatic int user_read_or_zero(addr_t addr, void *data, size_t size) {\n    if (addr == 0)\n        memset(data, 0, size);\n    else if (user_read(addr, data, size))\n        return _EFAULT;\n    return 0;\n}\n\n#define SELECT_READ (POLL_READ | POLL_HUP | POLL_ERR)\n#define SELECT_WRITE (POLL_WRITE | POLL_ERR)\n#define SELECT_EX (POLL_PRI)\nstruct select_context {\n    char *readfds;\n    char *writefds;\n    char *exceptfds;\n};\nstatic int select_event_callback(void *context, int types, union poll_fd_info info) {\n    struct select_context *c = context;\n    if (types & SELECT_READ)\n        bit_set(info.fd, c->readfds);\n    if (types & SELECT_WRITE)\n        bit_set(info.fd, c->writefds);\n    if (types & SELECT_EX)\n        bit_set(info.fd, c->exceptfds);\n    if (!(types & (SELECT_READ | SELECT_WRITE | SELECT_EX)))\n        return 0;\n    return 1;\n}\n\nstatic dword_t select_common(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t exceptfds_addr, struct timespec *timeout_addr, const char *name) {\n    size_t fdset_size = BITS_SIZE(nfds);\n    char readfds[fdset_size];\n    if (user_read_or_zero(readfds_addr, readfds, fdset_size))\n        return _EFAULT;\n    char writefds[fdset_size];\n    if (user_read_or_zero(writefds_addr, writefds, fdset_size))\n        return _EFAULT;\n    char exceptfds[fdset_size];\n    if (user_read_or_zero(exceptfds_addr, exceptfds, fdset_size))\n        return _EFAULT;\n\n    STRACE(\"%s(%d, 0x%x, 0x%x, 0x%x, 0x%x {%lus %luns}) \",\n           name, nfds, readfds_addr, writefds_addr, exceptfds_addr,\n           timeout_addr, timeout_addr ? timeout_addr->tv_sec : 0,\n           timeout_addr ? timeout_addr->tv_nsec : 0);\n\n    struct poll *poll = poll_create();\n    if (IS_ERR(poll))\n        return PTR_ERR(poll);\n\n    for (fd_t i = 0; i < nfds; i++) {\n        int events = 0;\n        if (bit_test(i, readfds))\n            events |= SELECT_READ;\n        if (bit_test(i, writefds))\n            events |= SELECT_WRITE;\n        if (bit_test(i, exceptfds))\n            events |= SELECT_EX;\n        if (events != 0) {\n            STRACE(\"%d{%s%s%s} \", i,\n                    bit_test(i, readfds) ? \"r\" : \"\",\n                    bit_test(i, writefds) ? \"w\" : \"\",\n                    bit_test(i, exceptfds) ? \"x\" : \"\");\n            struct fd *fd = f_get(i);\n            if (fd == NULL) {\n                poll_destroy(poll);\n                return _EBADF;\n            }\n            poll_add_fd(poll, fd, events, (union poll_fd_info) i);\n        }\n    }\n    STRACE(\"...\\n\");\n\n    memset(readfds, 0, fdset_size);\n    memset(writefds, 0, fdset_size);\n    memset(exceptfds, 0, fdset_size);\n    struct select_context context = {readfds, writefds, exceptfds};\n    int err = poll_wait(poll, select_event_callback, &context, timeout_addr);\n    STRACE(\"%d end %s \", current->pid, name);\n    for (fd_t i = 0; i < nfds; i++) {\n        if (bit_test(i, readfds) || bit_test(i, writefds) || bit_test(i, exceptfds)) {\n            STRACE(\"%d{%s%s%s} \", i,\n                    bit_test(i, readfds) ? \"r\" : \"\",\n                    bit_test(i, writefds) ? \"w\" : \"\",\n                    bit_test(i, exceptfds) ? \"x\" : \"\");\n        }\n    }\n    poll_destroy(poll);\n    if (err < 0)\n        return err;\n\n    if (readfds_addr && user_write(readfds_addr, readfds, fdset_size))\n        return _EFAULT;\n    if (writefds_addr && user_write(writefds_addr, writefds, fdset_size))\n        return _EFAULT;\n    if (exceptfds_addr && user_write(exceptfds_addr, exceptfds, fdset_size))\n        return _EFAULT;\n    return err;\n}\n\ndword_t sys_select(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t exceptfds_addr, addr_t timeout_addr) {\n    struct timespec timeout_ts = {};\n    struct timespec *timeout_ts_addr = NULL;\n    if (timeout_addr != 0) {\n        struct timeval_ timeout_timeval;\n        if (user_get(timeout_addr, timeout_timeval))\n            return _EFAULT;\n        timeout_ts = convert_timeval(timeout_timeval);\n        timeout_ts_addr = &timeout_ts;\n    }\n    // XXX we do not implement the Linux behavior of writing the timeout with remaining time here.\n    return select_common(nfds, readfds_addr, writefds_addr, exceptfds_addr, timeout_ts_addr, \"select\");\n}\n\nstruct poll_context {\n    struct pollfd_ *polls;\n    struct fd **files;\n    int nfds;\n};\n#define POLL_ALWAYS_LISTENING (POLL_ERR|POLL_HUP|POLL_NVAL)\nstatic int poll_event_callback(void *context, int types, union poll_fd_info info) {\n    struct poll_context *c = context;\n    struct pollfd_ *polls = c->polls;\n    int nfds = c->nfds;\n    int res = 0;\n    for (int i = 0; i < nfds; i++) {\n        if (c->files[i] == info.ptr) {\n            polls[i].revents = types & (polls[i].events | POLL_ALWAYS_LISTENING);\n            res = 1;\n        }\n    }\n    return res;\n}\ndword_t sys_poll(addr_t fds, dword_t nfds, int_t timeout) {\n    STRACE(\"poll(0x%x, %d, %d)\", fds, nfds, timeout);\n    struct pollfd_ polls[nfds];\n    if (fds != 0 || nfds != 0)\n        if (user_read(fds, polls, sizeof(struct pollfd_) * nfds))\n            return _EFAULT;\n    struct poll *poll = poll_create();\n    if (IS_ERR(poll))\n        return PTR_ERR(poll);\n\n    for (unsigned i = 0; i < nfds; i++)\n        STRACE(\" {%d, %#x}\", polls[i].fd, polls[i].events);\n    STRACE(\"...\\n\");\n\n    struct fd *files[nfds];\n    for (unsigned i = 0; i < nfds; i++) {\n        files[i] = f_get(polls[i].fd);\n        if (files[i] != NULL)\n            // FIXME it might have been closed by now by another thread\n            fd_retain(files[i]);\n        // clear revents, which is reused to mark whether a pollfd has been added or not\n        polls[i].revents = 0;\n    }\n\n    // convert polls array into poll_add_fd calls\n    // FIXME this is quadratic\n    for (unsigned i = 0; i < nfds; i++) {\n        if (polls[i].fd < 0 || polls[i].revents)\n            continue;\n\n        // if the same fd is listed more than once, merge the events bits together\n        int events = polls[i].events;\n        polls[i].revents = 1;\n        if (files[i] == NULL)\n            continue;\n        for (unsigned j = 0; j < nfds; j++) {\n            if (polls[j].revents)\n                continue;\n            if (files[i] == files[j]) {\n                events |= polls[j].events;\n                polls[j].revents = 1;\n            }\n        }\n\n        poll_add_fd(poll, files[i], events | POLL_ALWAYS_LISTENING, (union poll_fd_info) (void *) files[i]);\n    }\n\n    for (unsigned i = 0; i < nfds; i++) {\n        polls[i].revents = 0;\n        if (polls[i].fd >= 0 && files[i] == NULL)\n            polls[i].revents = POLL_NVAL;\n    }\n    struct poll_context context = {polls, files, nfds};\n    struct timespec timeout_ts;\n    if (timeout >= 0) {\n        timeout_ts.tv_sec = timeout / 1000;\n        timeout_ts.tv_nsec = (timeout % 1000) * 1000000;\n    }\n    int res = poll_wait(poll, poll_event_callback, &context, timeout < 0 ? NULL : &timeout_ts);\n    poll_destroy(poll);\n    for (unsigned i = 0; i < nfds; i++) {\n        if (files[i] != NULL)\n            fd_close(files[i]);\n    }\n    STRACE(\"%d end poll\", current->pid);\n    for (unsigned i = 0; i < nfds; i++)\n        STRACE(\" {%d, %#x}\", polls[i].fd, polls[i].revents);\n\n    if (res < 0)\n        return res;\n    if (fds != 0 || nfds != 0)\n        if (user_write(fds, polls, sizeof(struct pollfd_) * nfds))\n            return _EFAULT;\n    return res;\n}\n\ndword_t sys_pselect(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t exceptfds_addr, addr_t timeout_addr, addr_t sigmask_addr) {\n    struct timespec_ timeout_timespec;\n    struct timespec timeout_ts;\n    struct timespec *timeout_ts_addr = NULL;\n    if (timeout_addr != 0) {\n        if (user_get(timeout_addr, timeout_timespec))\n            return _EFAULT;\n        timeout_ts = convert_timespec(timeout_timespec);\n        timeout_ts_addr = &timeout_ts;\n    }\n    // a system call can only take 6 parameters, so the last two need to be passed as a pointer to a struct\n    struct {\n        addr_t mask_addr;\n        dword_t mask_size;\n    } sigmask;\n    if (user_get(sigmask_addr, sigmask))\n        return _EFAULT;\n    sigset_t_ mask;\n\n    if (sigmask.mask_addr != 0) {\n        if (sigmask.mask_size != sizeof(sigset_t_))\n            return _EINVAL;\n        if (user_get(sigmask.mask_addr, mask))\n            return _EFAULT;\n        sigmask_set_temp(mask);\n    }\n    return select_common(nfds, readfds_addr, writefds_addr, exceptfds_addr, timeout_ts_addr, \"pselect\");\n}\n\ndword_t sys_ppoll(addr_t fds, dword_t nfds, addr_t timeout_addr, addr_t sigmask_addr, dword_t sigsetsize) {\n    int timeout = -1;\n    if (timeout_addr != 0) {\n        struct timespec_ timeout_timespec;\n        if (user_get(timeout_addr, timeout_timespec))\n            return _EFAULT;\n        timeout = timeout_timespec.sec * 1000 + timeout_timespec.nsec / 1000000;\n    }\n\n    sigset_t_ mask;\n    if (sigmask_addr != 0) {\n        if (sigsetsize != sizeof(sigset_t_))\n            return _EINVAL;\n        if (user_get(sigmask_addr, mask))\n            return _EFAULT;\n        sigmask_set_temp(mask);\n    }\n\n    return sys_poll(fds, nfds, timeout);\n}\n"
  },
  {
    "path": "kernel/ptrace.c",
    "content": "#include \"ptrace.h\"\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/signal.h\"\n#include \"task.h\"\n#include <string.h>\n\n// Returns stopped child with the given pid, locked with the ptrace lock\nstatic struct task *find_child(pid_t_ pid) {\n    struct task *child = NULL;\n    list_for_each_entry(&current->children, child, siblings) {\n        if (child->pid == pid) {\n            lock(&child->ptrace.lock);\n            if (child->ptrace.stopped) {\n                goto found;\n            }\n\n            unlock(&child->ptrace.lock);\n        }\n    }\n    child = NULL;\nfound:\n    return child;\n}\n\n// Ensure stopped, ptrace locked, etc. before calling this\nstatic void get_user_regs(struct cpu_state *cpu, struct user_regs_struct_ *user_regs_) {\n    user_regs_->ebx = cpu->ebx;\n    user_regs_->ecx = cpu->ecx;\n    user_regs_->edx = cpu->edx;\n    user_regs_->esi = cpu->esi;\n    user_regs_->edi = cpu->edi;\n    user_regs_->ebp = cpu->ebp;\n    user_regs_->eax = cpu->eax;\n//  user_regs_->xds = cpu->xds;\n//  user_regs_->xes = cpu->xes;\n//  user_regs_->xfs = cpu->xfs;\n//  user_regs_->xgs = cpu->xgs;\n    user_regs_->orig_eax = cpu->eax;\n    user_regs_->eip = cpu->eip;\n//  user_regs_->xcs = cpu->xcs;\n    user_regs_->eflags = cpu->eflags;\n    user_regs_->esp = cpu->esp;\n//  user_regs_->xss = cpu->xss;\n}\n\n// Ensure stopped, ptrace locked, etc. before calling this\nstatic void set_user_regs(struct cpu_state *cpu, struct user_regs_struct_ *user_regs_) {\n    cpu->ebx = user_regs_->ebx;\n    cpu->ecx = user_regs_->ecx;\n    cpu->edx = user_regs_->edx;\n    cpu->esi = user_regs_->esi;\n    cpu->edi = user_regs_->edi;\n    cpu->ebp = user_regs_->ebp;\n    cpu->eax = user_regs_->eax;\n//  cpu->xds = user_regs_->xds;\n//  cpu->xes = user_regs_->xes;\n//  cpu->xfs = user_regs_->xfs;\n//  cpu->xgs = user_regs_->xgs;\n//  cpu->eax = user_regs_->orig_eax;\n    cpu->eip = user_regs_->eip;\n//  cpu->xcs = user_regs_->xcs;\n    cpu->eflags = user_regs_->eflags;\n    cpu->esp = user_regs_->esp;\n//  cpu->xss = user_regs_->xss;\n}\n\ndword_t sys_ptrace(dword_t request, dword_t pid, addr_t addr, dword_t data) {\n    switch (request) {\n        case PTRACE_TRACEME_:\n            STRACE(\"ptrace(PTRACE_TRACEME, %d, %#x, %#x)\", pid, addr, data);\n            current->ptrace.traced = true;\n            return 0;\n\n        case PTRACE_PEEKTEXT_:\n        case PTRACE_PEEKDATA_: {\n            STRACE(\"ptrace(PTRACE_PEEKDATA, %d, %#x, %#x)\", pid, addr, data);\n            dword_t peek;\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            if (user_get_task(child, addr, peek)) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            } else if (user_put(data, peek)) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_PEEKUSER_: {\n            STRACE(\"ptrace(PTRACE_PEEKUSER, %d, %#x, %#x)\", pid, addr, data);\n            dword_t peek;\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            struct user_ user_ = {};\n            get_user_regs(&child->cpu, &user_.user_regs);\n\n            if (addr & (sizeof(peek) - 1) || addr >= sizeof(struct user_))\n                return _EIO;\n\n            memcpy(&peek, (char *)&user_ + addr, sizeof(peek));\n            if (user_put(data, peek)) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_POKETEXT_:\n        case PTRACE_POKEDATA_: {\n            STRACE(\"ptrace(PTRACE_POKEDATA, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            if (user_write_task_ptrace(child, addr, &data, sizeof(data))) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_CONT_: {\n            STRACE(\"ptrace(PTRACE_CONT, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            child->cpu.tf = false;\n            child->ptrace.stopped = false;\n            notify(&child->ptrace.cond);\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_KILL_: {\n            STRACE(\"ptrace(PTRACE_KILL, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            child->ptrace.stopped = false;\n            send_signal(child, SIGKILL_, SIGINFO_NIL);\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_SINGLESTEP_: {\n            STRACE(\"ptrace(PTRACE_SINGLESTEP, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            child->cpu.tf = true;\n            child->ptrace.stopped = false;\n            notify(&child->ptrace.cond);\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_GETREGS_: {\n            STRACE(\"ptrace(PTRACE_GETREGS, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            struct user_regs_struct_ user_regs_ = {};\n            get_user_regs(&child->cpu, &user_regs_);\n            if (user_put(data, user_regs_)) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_SETREGS_: {\n            STRACE(\"ptrace(PTRACE_SETREGS, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            struct user_regs_struct_ user_regs_;\n            if (user_get(data, user_regs_)) {\n                return _EFAULT;\n            } else {\n                set_user_regs(&child->cpu, &user_regs_);\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        // GDB needs the fpregs functions to exist if you want to evaluate things\n        case PTRACE_GETFPREGS_: {\n            STRACE(\"ptrace(PTRACE_GETFPREGS, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            struct user_fpregs_struct_ user_fpregs_ = {};\n            if (user_put(data, user_fpregs_)) {\n                unlock(&child->ptrace.lock);\n                return _EFAULT;\n            }\n            // TODO get float point registers\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_SETFPREGS_: {\n            STRACE(\"ptrace(PTRACE_SETFPREGS, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            struct user_fpregs_struct_ user_fpregs_;\n            if (user_get(data, user_fpregs_)) {\n                return _EFAULT;\n            } else {\n                // TODO set floating point registers\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        case PTRACE_SETOPTIONS_:\n            STRACE(\"ptrace(PTRACE_SETOPTIONS, %d, %#x, %#x)\", pid, addr, data);\n            return _EINVAL;\n\n        case PTRACE_GETSIGINFO_: {\n            STRACE(\"ptrace(PTRACE_GETSIGINFO, %d, %#x, %#x)\", pid, addr, data);\n            struct task *child = find_child(pid);\n            if (!child) return _EPERM;\n\n            if (data && user_put(data, child->ptrace.info)) {\n                return _EFAULT;\n            }\n            unlock(&child->ptrace.lock);\n\n            return 0;\n        }\n\n        default:\n            STRACE(\"ptrace(%d, %d, %#x, %#x)\", request, pid, addr, data);\n            return _EPERM;\n    }\n}\n"
  },
  {
    "path": "kernel/ptrace.h",
    "content": "#ifndef KERNEL_PTRACE_H\n#define KERNEL_PTRACE_H\n\n#include \"misc.h\"\n\n#define PTRACE_TRACEME_ 0\n#define PTRACE_PEEKTEXT_ 1\n#define PTRACE_PEEKDATA_ 2\n#define PTRACE_PEEKUSER_ 3\n#define PTRACE_POKETEXT_ 4\n#define PTRACE_POKEDATA_ 5\n#define PTRACE_CONT_ 7\n#define PTRACE_KILL_ 8\n#define PTRACE_SINGLESTEP_ 9\n#define PTRACE_GETREGS_ 12\n#define PTRACE_SETREGS_ 13\n#define PTRACE_GETFPREGS_ 14\n#define PTRACE_SETFPREGS_ 15\n#define PTRACE_SETOPTIONS_ 0x4200\n#define PTRACE_GETSIGINFO_ 0x4202\n\n#define PTRACE_EVENT_FORK_ 1\n\nstruct user_regs_struct_ {\n    dword_t ebx;\n    dword_t ecx;\n    dword_t edx;\n    dword_t esi;\n    dword_t edi;\n    dword_t ebp;\n    dword_t eax;\n    dword_t xds;\n    dword_t xes;\n    dword_t xfs;\n    dword_t xgs;\n    dword_t orig_eax;\n    dword_t eip;\n    dword_t xcs;\n    dword_t eflags;\n    dword_t esp;\n    dword_t xss;\n};\n\nstruct user_fpregs_struct_ {\n    dword_t cwd;\n    dword_t swd;\n    dword_t twd;\n    dword_t fip;\n    dword_t fcs;\n    dword_t foo;\n    dword_t fos;\n    dword_t st_space[20];\n};\n\nstruct user_ {\n    struct user_regs_struct_ user_regs;\n    char padding[286 - sizeof(struct user_regs_struct_)];\n};\n\ndword_t sys_ptrace(dword_t request, dword_t pid, addr_t addr, dword_t data);\n\n#endif /* KERNEL_PTRACE_H */\n"
  },
  {
    "path": "kernel/random.c",
    "content": "#include <fcntl.h>\n#include \"kernel/calls.h\"\n\n#ifdef __APPLE__\n#include <CommonCrypto/CommonCrypto.h>\n#include <CommonCrypto/CommonRandom.h>\n#else\n#include <unistd.h>\n#include <sys/syscall.h>\n#include <linux/random.h>\n#endif\n\nint get_random(char *buf, size_t len) {\n#ifdef __APPLE__\n    return CCRandomGenerateBytes(buf, len) != kCCSuccess;\n#else\n    return syscall(SYS_getrandom, buf, len, 0) < 0;\n#endif\n}\n\ndword_t sys_getrandom(addr_t buf_addr, dword_t len, dword_t UNUSED(flags)) {\n    if (len > 1 << 20)\n        return _EIO;\n    char *buf = malloc(len);\n    if (get_random(buf, len) != 0) {\n        free(buf);\n        return _EIO;\n    }\n    if (user_write(buf_addr, buf, len)) {\n        free(buf);\n        return _EFAULT;\n    }\n    free(buf);\n    return len;\n}\n"
  },
  {
    "path": "kernel/random.h",
    "content": "#ifndef KERNEL_RANDOM_H\n#define KERNEL_RANDOM_H\n\n#include <stdlib.h>\n\nint get_random(char *buf, size_t len);\n\n#endif\n"
  },
  {
    "path": "kernel/resource.c",
    "content": "#if __linux__\n// pull in RUSAGE_THREAD\n#define _GNU_SOURCE\n#include <sys/resource.h>\n#elif __APPLE__\n// pull in thread_info and friends\n#include <mach/mach.h>\n#else\n#error\n#endif\n\n#include <limits.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n\nstatic bool resource_valid(int resource) {\n    return resource >= 0 && resource < RLIMIT_NLIMITS_;\n}\n\nstatic int rlimit_get(struct task *task, int resource, struct rlimit_ *limit) {\n    if (!resource_valid(resource))\n        return _EINVAL;\n    struct tgroup *group = task->group;\n    lock(&group->lock);\n    *limit = group->limits[resource];\n    unlock(&group->lock);\n    return 0;\n}\n\nstatic int rlimit_set(struct task *task, int resource, struct rlimit_ limit) {\n    if (!resource_valid(resource))\n        return _EINVAL;\n    struct tgroup *group = task->group;\n    lock(&group->lock);\n    group->limits[resource] = limit;\n    unlock(&group->lock);\n    return 0;\n}\n\nrlim_t_ rlimit(int resource) {\n    struct rlimit_ limit;\n    if (rlimit_get(current, resource, &limit) != 0)\n        die(\"invalid resource %d\", resource);\n    return limit.cur;\n}\n\nstatic int do_getrlimit32(int resource, struct rlimit32_ *rlimit32) {\n    STRACE(\"getlimit(%d)\", resource);\n    struct rlimit_ rlimit;\n    int err = rlimit_get(current, resource, &rlimit);\n    if (err < 0)\n        return err;\n    STRACE(\" {cur=%#x, max=%#x}\", rlimit.cur, rlimit.max);\n\n    rlimit32->max = rlimit.max;\n    rlimit32->cur = rlimit.cur;\n    return 0;\n}\n\ndword_t sys_getrlimit32(dword_t resource, addr_t rlim_addr) {\n    struct rlimit32_ rlimit;\n    int err = do_getrlimit32(resource, &rlimit);\n    if (err < 0)\n        return err;\n    if (user_put(rlim_addr, rlimit))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_old_getrlimit32(dword_t resource, addr_t rlim_addr) {\n    struct rlimit32_ rlimit;\n    int err = do_getrlimit32(resource, &rlimit);\n    if (err < 0)\n        return err;\n\n    // This version of the call is for programs that aren't aware of rlim_t\n    // being 64 bit. RLIM_INFINITY looks like -1 when truncated to 32 bits.\n    if (rlimit.cur > INT_MAX)\n        rlimit.cur = INT_MAX;\n    if (rlimit.max > INT_MAX)\n        rlimit.max = INT_MAX;\n\n    if (user_put(rlim_addr, rlimit))\n        return _EFAULT;\n    return 0;\n}\n\nstatic int check_setrlimit(int resource, struct rlimit_ new_limit) {\n    if (superuser())\n        return 0;\n    struct rlimit_ old_limit;\n    int err = rlimit_get(current, resource, &old_limit);\n    if (err < 0)\n        return err;\n    if (new_limit.max > old_limit.max)\n        return _EPERM;\n    return 0;\n}\n\ndword_t sys_setrlimit32(dword_t resource, addr_t rlim_addr) {\n    struct rlimit_ rlimit;\n    if (user_get(rlim_addr, rlimit))\n        return _EFAULT;\n    STRACE(\"setrlimit(%d, {cur=%#x, max=%#x})\", resource, rlimit.cur, rlimit.max);\n    int err = check_setrlimit(resource, rlimit);\n    if (err < 0)\n        return err;\n    return rlimit_set(current, resource, rlimit);\n}\n\ndword_t sys_prlimit64(pid_t_ pid, dword_t resource, addr_t new_limit_addr, addr_t old_limit_addr) {\n    STRACE(\"prlimit64(%d, %d)\", pid, resource);\n    if (pid != 0)\n        return _EINVAL;\n\n    if (old_limit_addr != 0) {\n        struct rlimit_ rlimit;\n        int err = rlimit_get(current, resource, &rlimit);\n        if (err < 0)\n            return err;\n        STRACE(\" old={cur=%#x, max=%#x}\", rlimit.cur, rlimit.max);\n        if (user_put(old_limit_addr, rlimit))\n            return _EFAULT;\n    }\n\n    if (new_limit_addr != 0) {\n        struct rlimit_ rlimit;\n        if (user_get(new_limit_addr, rlimit))\n            return _EFAULT;\n        STRACE(\" new={cur=%#x, max=%#x}\", rlimit.cur, rlimit.max);\n        int err = check_setrlimit(resource, rlimit);\n        if (err < 0)\n            return err;\n        return rlimit_set(current, resource, rlimit);\n    }\n    return 0;\n}\n\nstruct rusage_ rusage_get_current() {\n    // only the time fields are currently implemented\n    struct rusage_ rusage;\n#if __linux__\n    struct rusage usage;\n    int err = getrusage(RUSAGE_THREAD, &usage);\n    assert(err == 0);\n    rusage.utime.sec = usage.ru_utime.tv_sec;\n    rusage.utime.usec = usage.ru_utime.tv_usec;\n    rusage.stime.sec = usage.ru_stime.tv_sec;\n    rusage.stime.usec = usage.ru_stime.tv_usec;\n#elif __APPLE__\n    thread_basic_info_data_t info;\n    mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;\n    thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t) &info, &count);\n    rusage.utime.sec = info.user_time.seconds;\n    rusage.utime.usec = info.user_time.microseconds;\n    rusage.stime.sec = info.system_time.seconds;\n    rusage.stime.usec = info.system_time.microseconds;\n#endif\n    return rusage;\n}\n\nstatic void timeval_add(struct timeval_ *dst, struct timeval_ *src) {\n    dst->sec += src->sec;\n    dst->usec += src->usec;\n    if (dst->usec >= 1000000) {\n        dst->usec -= 1000000;\n        dst->sec++;\n    }\n}\n\nvoid rusage_add(struct rusage_ *dst, struct rusage_ *src) {\n    timeval_add(&dst->utime, &src->utime);\n    timeval_add(&dst->stime, &src->stime);\n}\n\ndword_t sys_getrusage(dword_t who, addr_t rusage_addr) {\n    struct rusage_ rusage;\n    switch (who) {\n        case RUSAGE_SELF_:\n            rusage = rusage_get_current();\n            break;\n        case RUSAGE_CHILDREN_:\n            lock(&current->group->lock);\n            rusage = current->group->children_rusage;\n            unlock(&current->group->lock);\n            break;\n        default:\n            return _EINVAL;\n    }\n    if (user_put(rusage_addr, rusage))\n        return _EFAULT;\n    return 0;\n}\n\nint_t sys_sched_getaffinity(pid_t_ pid, dword_t cpusetsize, addr_t cpuset_addr) {\n    STRACE(\"sched_getaffinity(%d, %d, %#x)\", pid, cpusetsize, cpuset_addr);\n    if (pid != 0) {\n        lock(&pids_lock);\n        struct task *task = pid_get_task(pid);\n        unlock(&pids_lock);\n        if (task == NULL)\n            return _ESRCH;\n    }\n\n    unsigned cpus = sysconf(_SC_NPROCESSORS_ONLN);\n    char cpuset[cpus / 8 + 1];\n    if (cpusetsize < sizeof(cpuset))\n        return _EINVAL;\n    memset(cpuset, 0, sizeof(cpuset));\n    for (unsigned i = 0; i < cpus; i++)\n        bit_set(i, cpuset);\n    if (user_write(cpuset_addr, cpuset, sizeof(cpuset)))\n        return _EFAULT;\n    // return the number of bytes written\n    return sizeof(cpuset);\n}\nint_t sys_sched_setaffinity(pid_t_ UNUSED(pid), dword_t UNUSED(cpusetsize), addr_t UNUSED(cpuset_addr)) {\n    // meh\n    return 0;\n}\n\nint_t sys_getpriority(int_t which, pid_t_ who) {\n    STRACE(\"getpriority(%d, %d)\", which, who);\n    return 20;\n}\nint_t sys_setpriority(int_t which, pid_t_ who, int_t prio) {\n    STRACE(\"setpriority(%d, %d, %d)\", which, who, prio);\n    return 0;\n}\n\n// realtime scheduling stubs\nint_t sys_sched_getparam(pid_t_ UNUSED(pid), addr_t param_addr) {\n    int_t sched_priority = 0;\n    if (user_put(param_addr, sched_priority))\n        return _EFAULT;\n    return 0;\n}\n#define SCHED_OTHER_ 0\nint_t sys_sched_getscheduler(pid_t_ UNUSED(pid)) {\n    return SCHED_OTHER_;\n}\nint_t sys_sched_setscheduler(pid_t_ UNUSED(pid), int_t policy, addr_t param_addr) {\n    if (policy != SCHED_OTHER_)\n        return _EINVAL;\n    int_t sched_priority;\n    if (user_get(param_addr, sched_priority))\n        return _EFAULT;\n    if (sched_priority != 0)\n        return _EINVAL;\n    return 0;\n}\n\nint_t sys_sched_get_priority_max(int_t policy) {\n    STRACE(\"sched_get_priority_max(%d)\", policy);\n    if (policy == 0)\n        return 0;\n    return _EINVAL;\n}\n\nint_t sys_ioprio_get(int_t UNUSED(which), int_t UNUSED(who), int_t UNUSED(ioprio)) {\n    return 0;\n}\nint_t sys_ioprio_set(int_t UNUSED(which), int_t UNUSED(who), int_t UNUSED(ioprio)) {\n    return 0;\n}\n"
  },
  {
    "path": "kernel/resource.h",
    "content": "#ifndef RESOURCE_H\n#define RESOURCE_H\n#include \"kernel/time.h\"\n\ntypedef qword_t rlim_t_;\ntypedef dword_t rlim32_t_;\n#define RLIM_INFINITY_ ((rlim_t_) -1)\n\nstruct rlimit_ {\n    rlim_t_ cur;\n    rlim_t_ max;\n};\n\nstruct rlimit32_ {\n    rlim32_t_ cur;\n    rlim32_t_ max;\n};\n\n#define RLIMIT_CPU_ 0\n#define RLIMIT_FSIZE_ 1\n#define RLIMIT_DATA_ 2\n#define RLIMIT_STACK_ 3\n#define RLIMIT_CORE_ 4\n#define RLIMIT_RSS_ 5\n#define RLIMIT_NPROC_ 6\n#define RLIMIT_NOFILE_ 7\n#define RLIMIT_MEMLOCK_ 8\n#define RLIMIT_AS_ 9\n#define RLIMIT_LOCKS_ 10\n#define RLIMIT_SIGPENDING_ 11\n#define RLIMIT_MSGQUEUE_ 12\n#define RLIMIT_NICE_ 13\n#define RLIMIT_RTPRIO_ 14\n#define RLIMIT_RTTIME_ 15\n#define RLIMIT_NLIMITS_ 16\n\ndword_t sys_getrlimit32(dword_t resource, addr_t rlim_addr);\ndword_t sys_setrlimit32(dword_t resource, addr_t rlim_addr);\ndword_t sys_prlimit64(pid_t_ pid, dword_t resource, addr_t new_limit_addr, addr_t old_limit_addr);\ndword_t sys_old_getrlimit32(dword_t resource, addr_t rlim_addr);\n\nrlim_t_ rlimit(int resource);\n\nstruct rusage_ {\n    struct timeval_ utime;\n    struct timeval_ stime;\n    dword_t maxrss;\n    dword_t ixrss;\n    dword_t idrss;\n    dword_t isrss;\n    dword_t minflt;\n    dword_t majflt;\n    dword_t nswap;\n    dword_t inblock;\n    dword_t oublock;\n    dword_t msgsnd;\n    dword_t msgrcv;\n    dword_t nsignals;\n    dword_t nvcsw;\n    dword_t nivcsw;\n};\n\nstruct rusage_ rusage_get_current(void);\nvoid rusage_add(struct rusage_ *dst, struct rusage_ *src);\n#define RUSAGE_SELF_ 0\n#define RUSAGE_CHILDREN_ -1\ndword_t sys_getrusage(dword_t who, addr_t rusage_addr);\n\nint_t sys_sched_getaffinity(pid_t_ pid, dword_t cpusetsize, addr_t cpuset_addr);\nint_t sys_sched_setaffinity(pid_t_ pid, dword_t cpusetsize, addr_t cpuset_addr);\nint_t sys_getpriority(int_t which, pid_t_ who);\nint_t sys_setpriority(int_t which, pid_t_ who, int_t prio);\n\nint_t sys_sched_getparam(pid_t_ pid, addr_t param_addr);\nint_t sys_sched_getscheduler(pid_t_ UNUSED(pid));\nint_t sys_sched_setscheduler(pid_t_ UNUSED(pid), int_t policy, addr_t param_addr);\nint_t sys_sched_get_priority_max(int_t policy);\n\nint_t sys_ioprio_set(int_t UNUSED(which), int_t UNUSED(who), int_t UNUSED(ioprio));\nint_t sys_ioprio_get(int_t UNUSED(which), int_t UNUSED(who), int_t UNUSED(ioprio));\n\n#endif\n"
  },
  {
    "path": "kernel/signal.c",
    "content": "#include \"debug.h\"\n#include <string.h>\n#include <signal.h>\n#include \"kernel/calls.h\"\n#include \"kernel/signal.h\"\n#include \"kernel/task.h\"\n#include \"kernel/vdso.h\"\n#include \"emu/interrupt.h\"\n\n#if is_gcc(9)\n#pragma GCC diagnostic ignored \"-Waddress-of-packed-member\"\n#endif\n\nint xsave_extra = 0;\nint fxsave_extra = 0;\nstatic void sigmask_set(sigset_t_ set);\nstatic void altstack_to_user(struct sighand *sighand, struct stack_t_ *user_stack);\nstatic bool is_on_altstack(dword_t sp, struct sighand *sighand);\n\nstatic int signal_is_blockable(int sig) {\n    return sig != SIGKILL_ && sig != SIGSTOP_;\n}\n\n#define UNBLOCKABLE_MASK (sig_mask(SIGKILL_) | sig_mask(SIGSTOP_))\n\n#define SIGNAL_IGNORE 0\n#define SIGNAL_KILL 1\n#define SIGNAL_CALL_HANDLER 2\n#define SIGNAL_STOP 3\n\nstatic int signal_action(struct sighand *sighand, int sig) {\n    if (signal_is_blockable(sig)) {\n        struct sigaction_ *action = &sighand->action[sig];\n        if (action->handler == SIG_IGN_)\n            return SIGNAL_IGNORE;\n        if (action->handler != SIG_DFL_)\n            return SIGNAL_CALL_HANDLER;\n    }\n\n    switch (sig) {\n        case SIGURG_: case SIGCONT_: case SIGCHLD_:\n        case SIGIO_: case SIGWINCH_:\n            return SIGNAL_IGNORE;\n\n        case SIGSTOP_: case SIGTSTP_: case SIGTTIN_: case SIGTTOU_:\n            return SIGNAL_STOP;\n\n        default:\n            return SIGNAL_KILL;\n    }\n}\n\nstatic void deliver_signal_unlocked(struct task *task, int sig, struct siginfo_ info) {\n    if (sigset_has(task->pending, sig))\n        return;\n\n    sigset_add(&task->pending, sig);\n    struct sigqueue *sigqueue = malloc(sizeof(struct sigqueue));\n    sigqueue->info = info;\n    sigqueue->info.sig = sig;\n    list_add_tail(&task->queue, &sigqueue->queue);\n\n    if (sigset_has(task->blocked & ~task->waiting, sig) && signal_is_blockable(sig))\n        return;\n\n    if (task != current) {\n        pthread_kill(task->thread, SIGUSR1);\n\n        // wake up any pthread condition waiters\n        // actual madness, I hope to god it's correct\n        // must release the sighand lock while going insane, to avoid a deadlock\n        unlock(&task->sighand->lock);\nretry:\n        lock(&task->waiting_cond_lock);\n        if (task->waiting_cond != NULL) {\n            bool mine = false;\n            if (trylock(task->waiting_lock) == EBUSY) {\n                if (pthread_equal(task->waiting_lock->owner, pthread_self()))\n                    mine = true;\n                if (!mine) {\n                    unlock(&task->waiting_cond_lock);\n                    goto retry;\n                }\n            }\n            notify(task->waiting_cond);\n            if (!mine)\n                unlock(task->waiting_lock);\n        }\n        unlock(&task->waiting_cond_lock);\n        lock(&task->sighand->lock);\n    }\n}\n\nvoid deliver_signal(struct task *task, int sig, struct siginfo_ info) {\n    lock(&task->sighand->lock);\n    deliver_signal_unlocked(task, sig, info);\n    unlock(&task->sighand->lock);\n}\n\nvoid send_signal(struct task *task, int sig, struct siginfo_ info) {\n    // signal zero is for testing whether a process exists\n    if (sig == 0)\n        return;\n    if (task->zombie || task->exiting)\n        return;\n\n    struct sighand *sighand = task->sighand;\n    lock(&sighand->lock);\n    if (signal_action(sighand, sig) != SIGNAL_IGNORE) {\n        deliver_signal_unlocked(task, sig, info);\n    }\n    unlock(&sighand->lock);\n\n    if (sig == SIGCONT_ || sig == SIGKILL_) {\n        lock(&task->group->lock);\n        task->group->stopped = false;\n        notify(&task->group->stopped_cond);\n        unlock(&task->group->lock);\n    }\n}\n\nbool try_self_signal(int sig) {\n    assert(sig == SIGTTIN_ || sig == SIGTTOU_);\n\n    struct sighand *sighand = current->sighand;\n    lock(&sighand->lock);\n    bool can_send = signal_action(sighand, sig) != SIGNAL_IGNORE &&\n        !sigset_has(current->blocked, sig);\n    if (can_send)\n        deliver_signal_unlocked(current, sig, SIGINFO_NIL);\n    unlock(&sighand->lock);\n    return can_send;\n}\n\nint send_group_signal(dword_t pgid, int sig, struct siginfo_ info) {\n    lock(&pids_lock);\n    struct pid *pid = pid_get(pgid);\n    if (pid == NULL) {\n        unlock(&pids_lock);\n        return _ESRCH;\n    }\n    struct tgroup *tgroup;\n    list_for_each_entry(&pid->pgroup, tgroup, pgroup) {\n        send_signal(tgroup->leader, sig, info);\n    }\n    unlock(&pids_lock);\n    return 0;\n}\n\nstatic addr_t sigreturn_trampoline(const char *name) {\n    addr_t sigreturn_addr = vdso_symbol(name);\n    if (sigreturn_addr == 0) {\n        die(\"sigreturn not found in vdso, this should never happen\");\n    }\n    return current->mm->vdso + sigreturn_addr;\n}\n\nstatic void setup_sigcontext(struct sigcontext_ *sc, struct cpu_state *cpu) {\n    sc->ax = cpu->eax;\n    sc->bx = cpu->ebx;\n    sc->cx = cpu->ecx;\n    sc->dx = cpu->edx;\n    sc->di = cpu->edi;\n    sc->si = cpu->esi;\n    sc->bp = cpu->ebp;\n    sc->sp = sc->sp_at_signal = cpu->esp;\n    sc->ip = cpu->eip;\n    collapse_flags(cpu);\n    sc->flags = cpu->eflags;\n    sc->trapno = cpu->trapno;\n    if (cpu->trapno == INT_GPF)\n        sc->cr2 = cpu->segfault_addr;\n    // TODO more shit\n    sc->oldmask = current->blocked & 0xffffffff;\n}\n\nstatic void setup_sigframe(struct siginfo_ *info, struct sigframe_ *frame) {\n    frame->restorer = sigreturn_trampoline(\"__kernel_sigreturn\");\n    frame->sig = info->sig;\n    setup_sigcontext(&frame->sc, &current->cpu);\n    frame->extramask = current->blocked >> 32;\n\n    static const struct {\n        uint16_t popmov;\n        uint32_t nr_sigreturn;\n        uint16_t int80;\n    } __attribute__((packed)) retcode = {\n        .popmov = 0xb858,\n        .nr_sigreturn = 113,\n        .int80 = 0x80cd,\n    };\n    memcpy(frame->retcode, &retcode, sizeof(retcode));\n}\n\nstatic void setup_rt_sigframe(struct siginfo_ *info, struct rt_sigframe_ *frame) {\n    frame->restorer = sigreturn_trampoline(\"__kernel_rt_sigreturn\");\n    frame->sig = info->sig;\n    frame->info = *info;\n    frame->uc.flags = 0;\n    frame->uc.link = 0;\n    altstack_to_user(current->sighand, &frame->uc.stack);\n    setup_sigcontext(&frame->uc.mcontext, &current->cpu);\n    frame->uc.sigmask = current->blocked;\n\n    static const struct {\n        uint8_t mov;\n        uint32_t nr_rt_sigreturn;\n        uint16_t int80;\n        uint8_t pad;\n    } __attribute__((packed)) rt_retcode = {\n        .mov = 0xb8,\n        .nr_rt_sigreturn = 173,\n        .int80 = 0x80cd,\n    };\n    memcpy(frame->retcode, &rt_retcode, sizeof(rt_retcode));\n}\n\nstatic void receive_signal(struct sighand *sighand, struct siginfo_ *info) {\n    int sig = info->sig;\n    STRACE(\"%d receiving signal %d\\n\", current->pid, sig);\n\n    switch (signal_action(sighand, sig)) {\n        case SIGNAL_IGNORE:\n            return;\n\n        case SIGNAL_STOP:\n            lock(&current->group->lock);\n            current->group->stopped = true;\n            current->group->group_exit_code = sig << 8 | 0x7f;\n            unlock(&current->group->lock);\n            return;\n\n        case SIGNAL_KILL:\n            unlock(&sighand->lock); // do_exit must be called without this lock\n            do_exit_group(sig);\n    }\n\n    struct sigaction_ *action = &sighand->action[info->sig];\n    bool need_siginfo = action->flags & SA_SIGINFO_;\n\n    // setup the frame\n    union {\n        struct sigframe_ sigframe;\n        struct rt_sigframe_ rt_sigframe;\n    } frame = {};\n    size_t frame_size;\n    if (need_siginfo) {\n        setup_rt_sigframe(info, &frame.rt_sigframe);\n        frame_size = sizeof(frame.rt_sigframe);\n    } else {\n        setup_sigframe(info, &frame.sigframe);\n        frame_size = sizeof(frame.sigframe);\n    }\n\n    // set up registers for signal handler\n    current->cpu.eax = info->sig;\n    current->cpu.eip = sighand->action[info->sig].handler;\n\n    dword_t sp = current->cpu.esp;\n    if (sighand->altstack && !is_on_altstack(sp, sighand)) {\n        sp = sighand->altstack + sighand->altstack_size;\n    }\n    if (xsave_extra) {\n        // do as the kernel does\n        // this is superhypermega condensed version of fpu__alloc_mathframe in\n        // arch/x86/kernel/fpu/signal.c\n        sp -= xsave_extra;\n        sp &=~ 0x3f;\n        sp -= fxsave_extra;\n    }\n    sp -= frame_size;\n    // align sp + 4 on a 16-byte boundary because that's what the abi says\n    sp = ((sp + 4) & ~0xf) - 4;\n    current->cpu.esp = sp;\n\n    // Update the mask. By default the signal will be blocked while in the\n    // handler, but sigaction is allowed to customize this.\n    if (!(action->flags & SA_NODEFER_))\n        sigset_add(&current->blocked, info->sig);\n    current->blocked |= action->mask;\n\n    // these have to be filled in after the location of the frame is known\n    if (need_siginfo) {\n        frame.rt_sigframe.pinfo = sp + offsetof(struct rt_sigframe_, info);\n        frame.rt_sigframe.puc = sp + offsetof(struct rt_sigframe_, uc);\n        current->cpu.edx = frame.rt_sigframe.pinfo;\n        current->cpu.ecx = frame.rt_sigframe.puc;\n    }\n\n    // install frame\n    if (user_write(sp, &frame, frame_size)) {\n        printk(\"failed to install frame for %d at %#x\\n\", info->sig, sp);\n        deliver_signal(current, SIGSEGV_, SIGINFO_NIL);\n    }\n\n    if (action->flags & SA_RESETHAND_)\n        *action = (struct sigaction_) {.handler = SIG_DFL_};\n}\n\nvoid signal_delivery_stop(int sig, struct siginfo_ *info) {\n    lock(&current->ptrace.lock);\n    current->ptrace.stopped = true;\n    current->ptrace.signal = sig;\n    current->ptrace.info = *info;\n    unlock(&current->ptrace.lock);\n    notify(&current->parent->group->child_exit);\n    // TODO add siginfo\n    send_signal(current->parent, current->group->leader->exit_signal, SIGINFO_NIL);\n\n    unlock(&current->sighand->lock);\n    lock(&current->ptrace.lock);\n    while (current->ptrace.stopped) {\n        wait_for_ignore_signals(&current->ptrace.cond, &current->ptrace.lock, NULL);\n        lock(&current->sighand->lock);\n        bool got_sigkill = sigset_has(current->pending, SIGKILL_);\n        unlock(&current->sighand->lock);\n        if (got_sigkill) {\n            STRACE(\"%d received a SIGKILL in signal delivery stop\\n\", current->pid);\n            unlock(&current->ptrace.lock);\n            do_exit_group(SIGKILL_);\n        }\n    }\n    unlock(&current->ptrace.lock);\n    lock(&current->sighand->lock);\n}\n\nvoid receive_signals() {\n    lock(&current->group->lock);\n    bool was_stopped = current->group->stopped;\n    unlock(&current->group->lock);\n\n    struct sighand *sighand = current->sighand;\n    lock(&sighand->lock);\n\n    // A saved mask means that the last system call was a call like sigsuspend\n    // that changes the mask during the call. Only ignore a signal right now if\n    // it was both blocked during the call and should still be blocked after\n    // the call.\n    sigset_t_ blocked = current->blocked;\n    if (current->has_saved_mask) {\n        blocked &= current->saved_mask;\n        current->has_saved_mask = false;\n        current->blocked = current->saved_mask;\n    }\n\n    struct sigqueue *sigqueue, *tmp;\n    list_for_each_entry_safe(&current->queue, sigqueue, tmp, queue) {\n        int sig = sigqueue->info.sig;\n        if (sigset_has(blocked, sig))\n            continue;\n        list_remove(&sigqueue->queue);\n        sigset_del(&current->pending, sig);\n\n        if (current->ptrace.traced && sig != SIGKILL_) {\n            // This notifies the parent, goes to sleep, and waits for the\n            // parent to tell it to continue.\n            // Any signals received while waiting are left on the queue, except\n            // for SIGKILL_, which causes an immediate exit.\n            signal_delivery_stop(sig, &sigqueue->info);\n        } else {\n            receive_signal(sighand, &sigqueue->info);\n        }\n        free(sigqueue);\n    }\n\n    unlock(&sighand->lock);\n\n    // this got moved out of the switch case in receive_signal to fix locking problems\n    if (!was_stopped) {\n        lock(&current->group->lock);\n        bool now_stopped = current->group->stopped;\n        unlock(&current->group->lock);\n        if (now_stopped) {\n            lock(&pids_lock);\n            notify(&current->parent->group->child_exit);\n            // TODO add siginfo\n            send_signal(current->parent, current->group->leader->exit_signal, SIGINFO_NIL);\n            unlock(&pids_lock);\n        }\n    }\n}\n\nstatic void restore_sigcontext(struct sigcontext_ *context, struct cpu_state *cpu) {\n    cpu->eax = context->ax;\n    cpu->ebx = context->bx;\n    cpu->ecx = context->cx;\n    cpu->edx = context->dx;\n    cpu->edi = context->di;\n    cpu->esi = context->si;\n    cpu->ebp = context->bp;\n    cpu->esp = context->sp;\n    cpu->eip = context->ip;\n    collapse_flags(cpu);\n\n    // Use AC, RF, OF, DF, TF, SF, ZF, AF, PF, CF\n#define USE_FLAGS 0b1010000110111010101\n    cpu->eflags = (context->flags & USE_FLAGS) | (cpu->eflags & ~USE_FLAGS);\n}\n\ndword_t sys_rt_sigreturn() {\n    struct cpu_state *cpu = &current->cpu;\n    struct rt_sigframe_ frame;\n    // esp points past the first field of the frame\n    if (user_get(cpu->esp - offsetof(struct rt_sigframe_, sig), frame)) {\n        deliver_signal(current, SIGSEGV_, SIGINFO_NIL);\n        return _EFAULT;\n    }\n    restore_sigcontext(&frame.uc.mcontext, cpu);\n\n    lock(&current->sighand->lock);\n    // FIXME this duplicates logic from sys_sigaltstack\n    if (!is_on_altstack(cpu->esp, current->sighand) &&\n            frame.uc.stack.size >= MINSIGSTKSZ_) {\n        current->sighand->altstack = frame.uc.stack.stack;\n        current->sighand->altstack_size = frame.uc.stack.size;\n    }\n    sigmask_set(frame.uc.sigmask);\n    unlock(&current->sighand->lock);\n    return cpu->eax;\n}\n\ndword_t sys_sigreturn() {\n    struct cpu_state *cpu = &current->cpu;\n    struct sigframe_ frame;\n    // esp points past the first two fields of the frame\n    if (user_get(cpu->esp - offsetof(struct sigframe_, sc), frame)) {\n        deliver_signal(current, SIGSEGV_, SIGINFO_NIL);\n        return _EFAULT;\n    }\n    restore_sigcontext(&frame.sc, cpu);\n\n    lock(&current->sighand->lock);\n    sigset_t_ oldmask = ((sigset_t_) frame.extramask << 32) | frame.sc.oldmask;\n    sigmask_set(oldmask);\n    unlock(&current->sighand->lock);\n    return cpu->eax;\n}\n\nstruct sighand *sighand_new() {\n    struct sighand *sighand = malloc(sizeof(struct sighand));\n    if (sighand == NULL)\n        return NULL;\n    memset(sighand, 0, sizeof(struct sighand));\n    sighand->refcount = 1;\n    lock_init(&sighand->lock);\n    return sighand;\n}\n\nstruct sighand *sighand_copy(struct sighand *sighand) {\n    struct sighand *new_sighand = sighand_new();\n    if (new_sighand == NULL)\n        return NULL;\n    memcpy(new_sighand->action, sighand->action, sizeof(new_sighand->action));\n    return new_sighand;\n}\n\nvoid sighand_release(struct sighand *sighand) {\n    if (--sighand->refcount == 0) {\n        free(sighand);\n    }\n}\n\nstatic int do_sigaction(int sig, const struct sigaction_ *action, struct sigaction_ *oldaction) {\n    if (sig >= NUM_SIGS)\n        return _EINVAL;\n    if (!signal_is_blockable(sig))\n        return _EINVAL;\n\n    struct sighand *sighand = current->sighand;\n    lock(&sighand->lock);\n    if (oldaction)\n        *oldaction = sighand->action[sig];\n    if (action)\n        sighand->action[sig] = *action;\n    unlock(&sighand->lock);\n    return 0;\n}\n\ndword_t sys_rt_sigaction(dword_t signum, addr_t action_addr, addr_t oldaction_addr, dword_t sigset_size) {\n    if (sigset_size != sizeof(sigset_t_))\n        return _EINVAL;\n    struct sigaction_ action, oldaction;\n    if (action_addr != 0)\n        if (user_get(action_addr, action))\n            return _EFAULT;\n    STRACE(\"rt_sigaction(%d, %#x {handler=%#x, flags=%#x, restorer=%#x, mask=%#llx}, 0x%x, %d)\", signum,\n            action_addr, action.handler, action.flags, action.restorer,\n            (unsigned long long) action.mask, oldaction_addr, sigset_size);\n\n    int err = do_sigaction(signum,\n            action_addr ? &action : NULL,\n            oldaction_addr ? &oldaction : NULL);\n    if (err < 0)\n        return err;\n\n    if (oldaction_addr != 0)\n        if (user_put(oldaction_addr, oldaction))\n            return _EFAULT;\n    return err;\n}\n\ndword_t sys_sigaction(dword_t signum, addr_t action_addr, addr_t oldaction_addr) {\n    return sys_rt_sigaction(signum, action_addr, oldaction_addr, 1);\n}\n\nstatic void sigmask_set(sigset_t_ set) {\n    current->blocked = (set & ~UNBLOCKABLE_MASK);\n}\n\nstatic void sigmask_set_temp_unlocked(sigset_t_ mask) {\n    current->saved_mask = current->blocked;\n    current->has_saved_mask = true;\n    sigmask_set(mask);\n}\n\nvoid sigmask_set_temp(sigset_t_ mask) {\n    lock(&current->sighand->lock);\n    sigmask_set_temp_unlocked(mask);\n    unlock(&current->sighand->lock);\n}\n\nstatic int do_sigprocmask(dword_t how, sigset_t_ set) {\n    if (how == SIG_BLOCK_)\n        sigmask_set(current->blocked | set);\n    else if (how == SIG_UNBLOCK_)\n        sigmask_set(current->blocked & ~set);\n    else if (how == SIG_SETMASK_)\n        sigmask_set(set);\n    else\n        return _EINVAL;\n    return 0;\n}\n\ndword_t sys_rt_sigprocmask(dword_t how, addr_t set_addr, addr_t oldset_addr, dword_t size) {\n    if (size != sizeof(sigset_t_))\n        return _EINVAL;\n\n    sigset_t_ set;\n    if (set_addr != 0)\n        if (user_get(set_addr, set))\n            return _EFAULT;\n    STRACE(\"rt_sigprocmask(%s, %#llx, %#x, %d)\",\n            how == SIG_BLOCK_ ? \"SIG_BLOCK\" :\n            how == SIG_UNBLOCK_ ? \"SIG_UNBLOCK\" :\n            how == SIG_SETMASK_ ? \"SIG_SETMASK\" : \"??\",\n            set_addr != 0 ? (long long) set : -1, oldset_addr, size);\n\n    if (oldset_addr != 0)\n        if (user_put(oldset_addr, current->blocked))\n            return _EFAULT;\n    if (set_addr != 0) {\n        struct sighand *sighand = current->sighand;\n        lock(&sighand->lock);\n        int err = do_sigprocmask(how, set);\n        unlock(&sighand->lock);\n        if (err < 0)\n            return err;\n    }\n    return 0;\n}\n\nint_t sys_rt_sigpending(addr_t set_addr) {\n    STRACE(\"rt_sigpending(%#x)\");\n    // as defined by the standard\n    sigset_t_ pending = current->pending & current->blocked;\n    if (user_put(set_addr, pending))\n        return _EFAULT;\n    return 0;\n}\n\nstatic bool is_on_altstack(dword_t sp, struct sighand *sighand) {\n    return sp > sighand->altstack && sp <= sighand->altstack + sighand->altstack_size;\n}\n\nstatic void altstack_to_user(struct sighand *sighand, struct stack_t_ *user_stack) {\n    user_stack->stack = sighand->altstack;\n    user_stack->size = sighand->altstack_size;\n    user_stack->flags = 0;\n    if (sighand->altstack == 0)\n        user_stack->flags |= SS_DISABLE_;\n    if (is_on_altstack(current->cpu.esp, sighand))\n        user_stack->flags |= SS_ONSTACK_;\n}\n\ndword_t sys_sigaltstack(addr_t ss_addr, addr_t old_ss_addr) {\n    STRACE(\"sigaltstack(0x%x, 0x%x)\", ss_addr, old_ss_addr);\n    struct sighand *sighand = current->sighand;\n    lock(&sighand->lock);\n    if (old_ss_addr != 0) {\n        struct stack_t_ old_ss;\n        altstack_to_user(sighand, &old_ss);\n        if (user_put(old_ss_addr, old_ss)) {\n            unlock(&sighand->lock);\n            return _EFAULT;\n        }\n    }\n    if (ss_addr != 0) {\n        if (is_on_altstack(current->cpu.esp, sighand)) {\n            unlock(&sighand->lock);\n            return _EPERM;\n        }\n        struct stack_t_ ss;\n        if (user_get(ss_addr, ss)) {\n            unlock(&sighand->lock);\n            return _EFAULT;\n        }\n        if (ss.flags & SS_DISABLE_) {\n            sighand->altstack = 0;\n        } else {\n            if (ss.size < MINSIGSTKSZ_)\n                return _ENOMEM;\n            sighand->altstack = ss.stack;\n            sighand->altstack_size = ss.size;\n        }\n    }\n    unlock(&sighand->lock);\n    return 0;\n}\n\nint_t sys_rt_sigsuspend(addr_t mask_addr, uint_t size) {\n    if (size != sizeof(sigset_t_))\n        return _EINVAL;\n    sigset_t_ mask;\n    if (user_get(mask_addr, mask))\n        return _EFAULT;\n    STRACE(\"sigsuspend(0x%llx) = ...\\n\", (long long) mask);\n\n    lock(&current->sighand->lock);\n    sigmask_set_temp_unlocked(mask);\n    while (wait_for(&current->pause, &current->sighand->lock, NULL) != _EINTR)\n        continue;\n    unlock(&current->sighand->lock);\n    STRACE(\"%d done sigsuspend\", current->pid);\n    return _EINTR;\n}\n\nint_t sys_pause() {\n    lock(&current->sighand->lock);\n    while (wait_for(&current->pause, &current->sighand->lock, NULL) != _EINTR)\n        continue;\n    unlock(&current->sighand->lock);\n    return _EINTR;\n}\n\nint_t sys_rt_sigtimedwait(addr_t set_addr, addr_t info_addr, addr_t timeout_addr, uint_t set_size) {\n    if (set_size != sizeof(sigset_t_))\n        return _EINVAL;\n    sigset_t_ set;\n    if (user_get(set_addr, set))\n        return _EFAULT;\n    struct timespec timeout;\n    if (timeout_addr != 0) {\n        struct timespec_ fake_timeout;\n        if (user_get(timeout_addr, fake_timeout))\n            return _EFAULT;\n        timeout.tv_sec = fake_timeout.sec;\n        timeout.tv_nsec = fake_timeout.nsec;\n    }\n    STRACE(\"sigtimedwait(%#llx, %#x, %#x) = ...\\n\", (long long) set, info_addr, timeout_addr);\n\n    lock(&current->sighand->lock);\n    assert(current->waiting == 0);\n    current->waiting = set;\n    int err;\n    do {\n        err = wait_for(&current->pause, &current->sighand->lock, timeout_addr == 0 ? NULL : &timeout);\n    } while (err == 0);\n    current->waiting = 0;\n    if (err == _ETIMEDOUT) {\n        unlock(&current->sighand->lock);\n        STRACE(\"sigtimedwait timed out\\n\");\n        return _EAGAIN;\n    }\n\n    struct sigqueue *sigqueue;\n    bool found = false;\n    list_for_each_entry(&current->queue, sigqueue, queue) {\n        if (sigset_has(set, sigqueue->info.sig)) {\n            found = true;\n            list_remove(&sigqueue->queue);\n            break;\n        }\n    }\n    unlock(&current->sighand->lock);\n    if (!found)\n        return _EINTR;\n    struct siginfo_ info = sigqueue->info;\n    free(sigqueue);\n    if (info_addr != 0)\n        if (user_put(info_addr, info))\n            return _EFAULT;\n    STRACE(\"done sigtimedwait = %d\\n\", info.sig);\n    return info.sig;\n}\n\nstatic int kill_task(struct task *task, dword_t sig) {\n    if (!superuser() &&\n            current->uid != task->uid &&\n            current->uid != task->suid &&\n            current->euid != task->uid &&\n            current->euid != task->suid)\n        return _EPERM;\n    struct siginfo_ info = {\n        .code = SI_USER_,\n        .kill.pid = current->pid,\n        .kill.uid = current->uid,\n    };\n    send_signal(task, sig, info);\n    return 0;\n}\n\nstatic int kill_group(pid_t_ pgid, dword_t sig) {\n    struct pid *pid = pid_get(pgid);\n    if (pid == NULL) {\n        unlock(&pids_lock);\n        return _ESRCH;\n    }\n    struct tgroup *tgroup;\n    int err = _EPERM;\n    list_for_each_entry(&pid->pgroup, tgroup, pgroup) {\n        int kill_err = kill_task(tgroup->leader, sig);\n        // killing a group should return an error only if no process can be signaled\n        if (err == _EPERM)\n            err = kill_err;\n    }\n    return err;\n}\n\nstatic int kill_everything(dword_t sig) {\n    int err = _EPERM;\n    for (int i = 2; i < MAX_PID; i++) {\n        struct task *task = pid_get_task(i);\n        if (task == NULL || task == current || !task_is_leader(task))\n            continue;\n        int kill_err = kill_task(task, sig);\n        if (err == _EPERM)\n            err = kill_err;\n    }\n    return err;\n}\n\nstatic int do_kill(pid_t_ pid, dword_t sig, pid_t_ tgid) {\n    STRACE(\"kill(%d, %d)\", pid, sig);\n    if (sig >= NUM_SIGS)\n        return _EINVAL;\n    if (pid == 0)\n        pid = -current->group->pgid;\n\n    int err;\n    lock(&pids_lock);\n\n    if (pid == -1) {\n        err = kill_everything(sig);\n    } else if (pid < 0) {\n        err = kill_group(-pid, sig);\n    } else {\n        struct task *task = pid_get_task(pid);\n        if (task == NULL) {\n            unlock(&pids_lock);\n            return _ESRCH;\n        }\n\n        // If tgid is nonzero, it must be correct\n        if (tgid != 0 && task->tgid != tgid) {\n            unlock(&pids_lock);\n            return _ESRCH;\n        }\n\n        err = kill_task(task, sig);\n    }\n\n    unlock(&pids_lock);\n    return err;\n}\n\ndword_t sys_kill(pid_t_ pid, dword_t sig) {\n    return do_kill(pid, sig, 0);\n}\ndword_t sys_tgkill(pid_t_ tgid, pid_t_ tid, dword_t sig) {\n    if (tid <= 0 || tgid <= 0)\n        return _EINVAL;\n    return do_kill(tid, sig, tgid);\n}\ndword_t sys_tkill(pid_t_ tid, dword_t sig) {\n    if (tid <= 0)\n        return _EINVAL;\n    return do_kill(tid, sig, 0);\n}\n"
  },
  {
    "path": "kernel/signal.h",
    "content": "#ifndef SIGNAL_H\n#define SIGNAL_H\n\n#include \"misc.h\"\n#include \"util/list.h\"\n#include \"util/sync.h\"\nstruct task;\n\ntypedef qword_t sigset_t_;\n\n#define SIG_ERR_ -1\n#define SIG_DFL_ 0\n#define SIG_IGN_ 1\n\n#define SA_SIGINFO_ 4\n#define SA_NODEFER_ 0x40000000\n#define SA_RESETHAND_ 0x80000000\n\nstruct sigaction_ {\n    addr_t handler;\n    dword_t flags;\n    addr_t restorer;\n    sigset_t_ mask;\n} __attribute__((packed));\n\n#define NUM_SIGS 64\n\n#define\tSIGHUP_    1\n#define\tSIGINT_    2\n#define\tSIGQUIT_   3\n#define\tSIGILL_    4\n#define\tSIGTRAP_   5\n#define\tSIGABRT_   6\n#define\tSIGIOT_    6\n#define\tSIGBUS_    7\n#define\tSIGFPE_    8\n#define\tSIGKILL_   9\n#define\tSIGUSR1_   10\n#define\tSIGSEGV_   11\n#define\tSIGUSR2_   12\n#define\tSIGPIPE_   13\n#define\tSIGALRM_   14\n#define\tSIGTERM_   15\n#define\tSIGSTKFLT_ 16\n#define\tSIGCHLD_   17\n#define\tSIGCONT_   18\n#define\tSIGSTOP_   19\n#define\tSIGTSTP_   20\n#define\tSIGTTIN_   21\n#define\tSIGTTOU_   22\n#define\tSIGURG_    23\n#define\tSIGXCPU_   24\n#define\tSIGXFSZ_   25\n#define\tSIGVTALRM_ 26\n#define\tSIGPROF_   27\n#define\tSIGWINCH_  28\n#define\tSIGIO_     29\n#define\tSIGPWR_    30\n#define SIGSYS_    31\n\n#define SI_USER_ 0\n#define SI_TIMER_ -2\n#define SI_TKILL_ -6\n#define SI_KERNEL_ 128\n#define TRAP_TRACE_ 2\n#define SEGV_MAPERR_ 1\n#define SEGV_ACCERR_ 2\n\nunion sigval_ {\n    int_t sv_int;\n    addr_t sv_ptr;\n};\n\nstruct siginfo_ {\n    int_t sig;\n    int_t sig_errno;\n    int_t code;\n    union {\n        struct {\n            pid_t_ pid;\n            uid_t_ uid;\n        } kill;\n        struct {\n            pid_t_ pid;\n            uid_t_ uid;\n            int_t status;\n            clock_t_ utime;\n            clock_t_ stime;\n        } child;\n        struct {\n            addr_t addr;\n        } fault;\n        struct {\n            addr_t addr;\n            int_t syscall;\n        } sigsys;\n        struct {\n            int_t timer;\n            int_t overrun;\n            union sigval_ value;\n            int_t _private;\n        } timer;\n    };\n};\n\n// a reasonable default siginfo\nstatic const struct siginfo_ SIGINFO_NIL = {\n    .code = SI_KERNEL_,\n};\n\nstruct sigqueue {\n    struct list queue;\n    struct siginfo_ info;\n};\n\nstruct sigevent_ {\n    union sigval_ value;\n    int_t signo;\n    int_t method;\n    pid_t_ tid;\n};\n\n// send a signal\n// you better make sure the task isn't gonna get freed under me (pids_lock or current)\nvoid send_signal(struct task *task, int sig, struct siginfo_ info);\n// send a signal without regard for whether the signal is blocked or ignored\nvoid deliver_signal(struct task *task, int sig, struct siginfo_ info);\n// send a signal to current if it's not blocked or ignored, return whether that worked\n// exists specifically for sending SIGTTIN/SIGTTOU\nbool try_self_signal(int sig);\n// send a signal to all processes in a group, could return ESRCH\nint send_group_signal(dword_t pgid, int sig, struct siginfo_ info);\n// check for and deliver pending signals on current\n// must be called without pids_lock, current->group->lock, or current->sighand->lock\nvoid receive_signals(void);\n// set the signal mask, restore it to what it was before on the next receive_signals call\nvoid sigmask_set_temp(sigset_t_ mask);\n\nstruct sighand {\n    atomic_uint refcount;\n    struct sigaction_ action[NUM_SIGS];\n    addr_t altstack;\n    dword_t altstack_size;\n    lock_t lock;\n};\nstruct sighand *sighand_new(void);\nstruct sighand *sighand_copy(struct sighand *sighand);\nvoid sighand_release(struct sighand *sighand);\n\ndword_t sys_rt_sigaction(dword_t signum, addr_t action_addr, addr_t oldaction_addr, dword_t sigset_size);\ndword_t sys_sigaction(dword_t signum, addr_t action_addr, addr_t oldaction_addr);\ndword_t sys_rt_sigreturn(void);\ndword_t sys_sigreturn(void);\n\n#define SIG_BLOCK_ 0\n#define SIG_UNBLOCK_ 1\n#define SIG_SETMASK_ 2\ntypedef uint64_t sigset_t_;\ndword_t sys_rt_sigprocmask(dword_t how, addr_t set, addr_t oldset, dword_t size);\nint_t sys_rt_sigpending(addr_t set_addr);\n\nstatic inline sigset_t_ sig_mask(int sig) {\n    assert(sig >= 1 && sig < NUM_SIGS);\n    return 1l << (sig - 1);\n}\n\nstatic inline bool sigset_has(sigset_t_ set, int sig) {\n    return !!(set & sig_mask(sig));\n}\nstatic inline void sigset_add(sigset_t_ *set, int sig) {\n    *set |= sig_mask(sig);\n}\nstatic inline void sigset_del(sigset_t_ *set, int sig) {\n    *set &= ~sig_mask(sig);\n}\n\nstruct stack_t_ {\n    addr_t stack;\n    dword_t flags;\n    dword_t size;\n};\n#define SS_ONSTACK_ 1\n#define SS_DISABLE_ 2\n#define MINSIGSTKSZ_ 2048\ndword_t sys_sigaltstack(addr_t ss, addr_t old_ss);\n\nint_t sys_rt_sigsuspend(addr_t mask_addr, uint_t size);\nint_t sys_pause(void);\nint_t sys_rt_sigtimedwait(addr_t set_addr, addr_t info_addr, addr_t timeout_addr, uint_t set_size);\n\ndword_t sys_kill(pid_t_ pid, dword_t sig);\ndword_t sys_tkill(pid_t_ tid, dword_t sig);\ndword_t sys_tgkill(pid_t_ tgid, pid_t_ tid, dword_t sig);\n\n// signal frame structs. There's a good chance this should go in its own header file\n\n// thanks kernel for giving me something to copy/paste\nstruct sigcontext_ {\n    word_t gs, __gsh;\n    word_t fs, __fsh;\n    word_t es, __esh;\n    word_t ds, __dsh;\n    dword_t di;\n    dword_t si;\n    dword_t bp;\n    dword_t sp;\n    dword_t bx;\n    dword_t dx;\n    dword_t cx;\n    dword_t ax;\n    dword_t trapno;\n    dword_t err;\n    dword_t ip;\n    word_t cs, __csh;\n    dword_t flags;\n    dword_t sp_at_signal;\n    word_t ss, __ssh;\n\n    dword_t fpstate;\n    dword_t oldmask;\n    dword_t cr2;\n};\n\nstruct ucontext_ {\n    uint_t flags;\n    uint_t link;\n    struct stack_t_ stack;\n    struct sigcontext_ mcontext;\n    sigset_t_ sigmask;\n} __attribute__((packed));\n\nstruct fpreg_ {\n    word_t significand[4];\n    word_t exponent;\n};\n\nstruct fpxreg_ {\n    word_t significand[4];\n    word_t exponent;\n    word_t padding[3];\n};\n\nstruct xmmreg_ {\n    uint32_t element[4];\n};\n\nstruct fpstate_ {\n    /* Regular FPU environment.  */\n    dword_t cw;\n    dword_t sw;\n    dword_t tag;\n    dword_t ipoff;\n    dword_t cssel;\n    dword_t dataoff;\n    dword_t datasel;\n    struct fpreg_ st[8];\n    word_t status;\n    word_t magic;\n\n    /* FXSR FPU environment.  */\n    dword_t _fxsr_env[6];\n    dword_t mxcsr;\n    dword_t reserved;\n    struct fpxreg_ fxsr_st[8];\n    struct xmmreg_ xmm[8];\n    dword_t padding[56];\n};\n\nstruct sigframe_ {\n    addr_t restorer;\n    dword_t sig;\n    struct sigcontext_ sc;\n    struct fpstate_ fpstate;\n    dword_t extramask;\n    char retcode[8];\n};\n\nstruct rt_sigframe_ {\n    addr_t restorer;\n    int_t sig;\n    addr_t pinfo;\n    addr_t puc;\n    union {\n        struct siginfo_ info;\n        char __pad[128];\n    };\n    struct ucontext_ uc;\n    char retcode[8];\n};\n\n// On a 64-bit system with 32-bit emulation, the fpu state is stored in extra\n// space at the end of the frame, not in the frame itself. We store the fpu\n// state in the frame where it should be, and ptraceomatic will set this. If\n// they are set we'll add some padding to the bottom to the frame to make\n// everything align.\nextern int xsave_extra;\nextern int fxsave_extra;\n\n#endif\n"
  },
  {
    "path": "kernel/task.c",
    "content": "#define _GNU_SOURCE\n#include <pthread.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"kernel/task.h\"\n#include \"kernel/memory.h\"\n#include \"emu/tlb.h\"\n\n__thread struct task *current;\n\nstatic struct pid pids[MAX_PID + 1] = {};\nlock_t pids_lock = LOCK_INITIALIZER;\n\nstatic bool pid_empty(struct pid *pid) {\n    return pid->task == NULL && list_empty(&pid->session) && list_empty(&pid->pgroup);\n}\n\nstruct pid *pid_get(dword_t id) {\n    if (id > sizeof(pids)/sizeof(pids[0]))\n        return NULL;\n    struct pid *pid = &pids[id];\n    if (pid_empty(pid))\n        return NULL;\n    return pid;\n}\n\nstruct task *pid_get_task_zombie(dword_t id) {\n    struct pid *pid = pid_get(id);\n    if (pid == NULL)\n        return NULL;\n    struct task *task = pid->task;\n    return task;\n}\n\nstruct task *pid_get_task(dword_t id) {\n    struct task *task = pid_get_task_zombie(id);\n    if (task != NULL && task->zombie)\n        return NULL;\n    return task;\n}\n\nstruct task *task_create_(struct task *parent) {\n    lock(&pids_lock);\n    static int cur_pid = 0;\n    do {\n        cur_pid++;\n        if (cur_pid > MAX_PID) cur_pid = 1;\n    } while (!pid_empty(&pids[cur_pid]));\n    struct pid *pid = &pids[cur_pid];\n    pid->id = cur_pid;\n    list_init(&pid->session);\n    list_init(&pid->pgroup);\n\n    struct task *task = malloc(sizeof(struct task));\n    if (task == NULL)\n        return NULL;\n    *task = (struct task) {};\n    if (parent != NULL)\n        *task = *parent;\n    task->pid = pid->id;\n    pid->task = task;\n\n    list_init(&task->children);\n    list_init(&task->siblings);\n    if (parent != NULL) {\n        task->parent = parent;\n        list_add(&parent->children, &task->siblings);\n    }\n    unlock(&pids_lock);\n\n    task->pending = 0;\n    list_init(&task->queue);\n    task->clear_tid = 0;\n    task->robust_list = 0;\n    task->did_exec = false;\n    lock_init(&task->general_lock);\n\n    task->sockrestart = (struct task_sockrestart) {};\n    list_init(&task->sockrestart.listen);\n\n    task->waiting_cond = NULL;\n    task->waiting_lock = NULL;\n    lock_init(&task->waiting_cond_lock);\n    cond_init(&task->pause);\n\n    lock_init(&task->ptrace.lock);\n    cond_init(&task->ptrace.cond);\n    return task;\n}\n\nvoid task_destroy(struct task *task) {\n    list_remove(&task->siblings);\n    pid_get(task->pid)->task = NULL;\n    free(task);\n}\n\nvoid task_run_current() {\n    struct cpu_state *cpu = &current->cpu;\n    struct tlb tlb = {};\n    tlb_refresh(&tlb, &current->mem->mmu);\n    while (true) {\n        read_wrlock(&current->mem->lock);\n        int interrupt = cpu_run_to_interrupt(cpu, &tlb);\n        read_wrunlock(&current->mem->lock);\n        handle_interrupt(interrupt);\n    }\n}\n\nstatic void *task_thread(void *task) {\n    current = task;\n    update_thread_name();\n    task_run_current();\n    die(\"task_thread returned\"); // above function call should never return\n}\n\nstatic pthread_attr_t task_thread_attr;\n__attribute__((constructor)) static void create_attr() {\n    pthread_attr_init(&task_thread_attr);\n    pthread_attr_setdetachstate(&task_thread_attr, PTHREAD_CREATE_DETACHED);\n}\n\nvoid task_start(struct task *task) {\n    if (pthread_create(&task->thread, &task_thread_attr, task_thread, task) < 0)\n        die(\"could not create thread\");\n}\n\nint_t sys_sched_yield() {\n    STRACE(\"sched_yield()\");\n    sched_yield();\n    return 0;\n}\n\nvoid update_thread_name() {\n    char name[16]; // As long as Linux will let us make this\n    snprintf(name, sizeof(name), \"-%d\", current->pid);\n    size_t pid_width = strlen(name);\n    size_t name_width = snprintf(name, sizeof(name), \"%s\", current->comm);\n    sprintf(name + (name_width < sizeof(name) - 1 - pid_width ? name_width : sizeof(name) - 1 - pid_width), \"-%d\", current->pid);\n#if __APPLE__\n    pthread_setname_np(name);\n#else\n    pthread_setname_np(pthread_self(), name);\n#endif\n}\n"
  },
  {
    "path": "kernel/task.h",
    "content": "#ifndef TASK_H\n#define TASK_H\n\n#include <pthread.h>\n#include \"emu/cpu.h\"\n#include \"kernel/mm.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/signal.h\"\n#include \"kernel/resource.h\"\n#include \"fs/sockrestart.h\"\n#include \"util/list.h\"\n#include \"util/timer.h\"\n#include \"util/sync.h\"\n\n// everything here is private to the thread executing this task and needs no\n// locking, unless otherwise specified\nstruct task {\n    struct cpu_state cpu;\n    struct mm *mm; // locked by general_lock\n    struct mem *mem; // pointer to mm.mem, for convenience\n    pthread_t thread;\n    uint64_t threadid;\n\n    struct tgroup *group; // immutable\n    struct list group_links;\n    pid_t_ pid, tgid; // immutable\n    uid_t_ uid, gid;\n    uid_t_ euid, egid;\n    uid_t_ suid, sgid;\n#define MAX_GROUPS 32\n    unsigned ngroups;\n    uid_t_ groups[MAX_GROUPS];\n    char comm[16] __strncpy_safe; // locked by general_lock\n    bool did_exec; // for that one annoying setsid edge case\n\n    struct fdtable *files;\n    struct fs_info *fs;\n\n    // locked by sighand->lock\n    struct sighand *sighand;\n    sigset_t_ blocked;\n    sigset_t_ pending;\n    sigset_t_ waiting; // if nonzero, an ongoing call to sigtimedwait is waiting on these\n    struct list queue;\n    cond_t pause; // please don't signal this\n    // private\n    sigset_t_ saved_mask;\n    bool has_saved_mask;\n\n    struct {\n        // Locks all ptrace-related things\n        lock_t lock;\n        cond_t cond;\n\n        bool traced;\n        bool stopped;\n        int signal;\n        struct siginfo_ info;\n        int trap_event;\n    } ptrace;\n\n    // locked by pids_lock\n    struct task *parent;\n    struct list children;\n    struct list siblings;\n\n    addr_t clear_tid;\n    addr_t robust_list;\n\n    // locked by pids_lock\n    dword_t exit_code;\n    bool zombie;\n    bool exiting;\n\n    // this structure is allocated on the stack of the parent's clone() call\n    struct vfork_info {\n        bool done;\n        cond_t cond;\n        lock_t lock;\n    } *vfork;\n    int exit_signal;\n\n    // lock for anything that needs locking but is not covered by some other lock\n    // specifically: comm, mm\n    lock_t general_lock;\n\n    struct task_sockrestart sockrestart;\n\n    // current condition/lock, so it can be notified in case of a signal\n    cond_t *waiting_cond;\n    lock_t *waiting_lock;\n    lock_t waiting_cond_lock;\n};\n\n// current will always give the process that is currently executing\n// if I have to stop using __thread, current will become a macro\nextern __thread struct task *current;\n\nstatic inline void task_set_mm(struct task *task, struct mm *mm) {\n    task->mm = mm;\n    task->mem = &task->mm->mem;\n    task->cpu.mmu = &task->mem->mmu;\n}\n\n// Creates a new process, initializes most fields from the parent. Specify\n// parent as NULL to create the init process. Returns NULL if out of memory.\n// Ends with an underscore because there's a mach function by the same name\nstruct task *task_create_(struct task *parent);\n// Removes the process from the process table and frees it. Must be called with pids_lock.\nvoid task_destroy(struct task *task);\n\n// misc\nvoid vfork_notify(struct task *task);\npid_t_ task_setsid(struct task *task);\nvoid task_leave_session(struct task *task);\n\nstruct posix_timer {\n    struct timer *timer;\n    int_t timer_id;\n    struct tgroup *tgroup;\n    pid_t_ thread_pid;\n    int_t signal;\n    union sigval_ sig_value;\n};\n\n// struct thread_group is way too long to type comfortably\nstruct tgroup {\n    struct list threads; // locked by pids_lock, by majority vote\n    struct task *leader; // immutable\n    struct rusage_ rusage;\n\n    // locked by pids_lock\n    pid_t_ sid, pgid;\n    struct list session;\n    struct list pgroup;\n\n    bool stopped;\n    cond_t stopped_cond;\n\n    struct tty *tty;\n    struct timer *itimer;\n#define TIMERS_MAX 16\n    struct posix_timer posix_timers[TIMERS_MAX];\n\n    struct rlimit_ limits[RLIMIT_NLIMITS_];\n\n    // From https://twitter.com/tblodt/status/957706819236904960\n    // > there are two distinct ways for a p̶r̶o̶c̶e̶s̶s̶ thread group to exit:\n    // > \n    // > - each thread calls exit\n    // > wait will return the exit code for the group leader\n    // > \n    // > - any thread calls exit_group\n    // > the SIGNAL_GROUP_EXIT flag will be set and wait will return the status passed to exit_group\n    //\n    // TODO locking\n    bool doing_group_exit;\n    dword_t group_exit_code;\n\n    struct rusage_ children_rusage;\n    cond_t child_exit;\n\n    dword_t personality;\n\n    // for everything in this struct not locked by something else\n    lock_t lock;\n};\n\nstatic inline bool task_is_leader(struct task *task) {\n    return task->group->leader == task;\n}\n\nstruct pid {\n    dword_t id;\n    struct task *task;\n    struct list session;\n    struct list pgroup;\n};\n\n// synchronizes obtaining a pointer to a task and freeing that task\nextern lock_t pids_lock;\n// these functions must be called with pids_lock\nstruct pid *pid_get(dword_t pid);\nstruct task *pid_get_task(dword_t pid);\nstruct task *pid_get_task_zombie(dword_t id); // don't return null if the task exists as a zombie\n\n#define MAX_PID (1 << 15) // oughta be enough\n\n// TODO document\nvoid task_start(struct task *task);\nvoid task_run_current(void);\n\nextern void (*exit_hook)(struct task *task, int code);\n\n#define superuser() (current != NULL && current->euid == 0)\n\n// Update the thread name to match the current task, in the format \"comm-pid\".\n// Will ensure that the -pid part always fits, then will fit as much of comm as possible.\nvoid update_thread_name(void);\n\n#endif\n"
  },
  {
    "path": "kernel/time.c",
    "content": "#ifdef __linux__\n#define _GNU_SOURCE\n#include <sys/resource.h>\n#endif\n#include \"debug.h\"\n#include <time.h>\n#include <signal.h>\n#include <sys/time.h>\n#include \"kernel/calls.h\"\n#include \"kernel/errno.h\"\n#include \"kernel/resource.h\"\n#include \"kernel/time.h\"\n#include \"fs/poll.h\"\n\nstatic int clockid_to_real(uint_t clock, clockid_t *real) {\n    switch (clock) {\n        case CLOCK_REALTIME_:\n        case CLOCK_REALTIME_COARSE_:\n            *real = CLOCK_REALTIME; break;\n        case CLOCK_MONOTONIC_: *real = CLOCK_MONOTONIC; break;\n        default: return _EINVAL;\n    }\n    return 0;\n}\n\nstatic struct timer_spec timer_spec_to_real(struct itimerspec_ itspec) {\n    struct timer_spec spec = {\n        .value.tv_sec = itspec.value.sec,\n        .value.tv_nsec = itspec.value.nsec,\n        .interval.tv_sec = itspec.interval.sec,\n        .interval.tv_nsec = itspec.interval.nsec,\n    };\n    return spec;\n};\n\nstatic struct itimerspec_ timer_spec_from_real(struct timer_spec spec) {\n    struct itimerspec_ itspec = {\n        .value.sec = spec.value.tv_sec,\n        .value.nsec = spec.value.tv_nsec,\n        .interval.sec = spec.interval.tv_sec,\n        .interval.nsec = spec.interval.tv_nsec,\n    };\n    return itspec;\n};\n\ndword_t sys_time(addr_t time_out) {\n    dword_t now = time(NULL);\n    if (time_out != 0)\n        if (user_put(time_out, now))\n            return _EFAULT;\n    return now;\n}\n\ndword_t sys_stime(addr_t UNUSED(time)) {\n    return _EPERM;\n}\n\ndword_t sys_clock_gettime(dword_t clock, addr_t tp) {\n    STRACE(\"clock_gettime(%d, 0x%x)\", clock, tp);\n\n    struct timespec ts;\n    if (clock == CLOCK_PROCESS_CPUTIME_ID_) {\n        // FIXME this is thread usage, not process usage\n        struct rusage_ rusage = rusage_get_current();\n        ts.tv_sec = rusage.utime.sec;\n        ts.tv_nsec = rusage.utime.usec * 1000;\n    } else {\n        clockid_t clock_id;\n        if (clockid_to_real(clock, &clock_id)) return _EINVAL;\n        int err = clock_gettime(clock_id, &ts);\n        if (err < 0)\n            return errno_map();\n    }\n    struct timespec_ t;\n    t.sec = ts.tv_sec;\n    t.nsec = ts.tv_nsec;\n    if (user_put(tp, t))\n        return _EFAULT;\n    STRACE(\" {%lds %ldns}\", t.sec, t.nsec);\n    return 0;\n}\n\ndword_t sys_clock_getres(dword_t clock, addr_t res_addr) {\n    STRACE(\"clock_getres(%d, %#x)\", clock, res_addr);\n    clockid_t clock_id;\n    if (clockid_to_real(clock, &clock_id)) return _EINVAL;\n\n    struct timespec res;\n    int err = clock_getres(clock_id, &res);\n    if (err < 0)\n        return errno_map();\n    struct timespec_ t;\n    t.sec = res.tv_sec;\n    t.nsec = res.tv_nsec;\n    if (user_put(res_addr, t))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_clock_settime(dword_t UNUSED(clock), addr_t UNUSED(tp)) {\n    return _EPERM;\n}\n\nstatic void itimer_notify(struct task *task) {\n    struct siginfo_ info = {\n        .code = SI_TIMER_,\n    };\n    send_signal(task, SIGALRM_, info);\n}\n\nstatic int itimer_set(struct tgroup *group, int which, struct timer_spec spec, struct timer_spec *old_spec) {\n    if (which != ITIMER_REAL_) {\n        FIXME(\"unimplemented setitimer %d\", which);\n        return _EINVAL;\n    }\n\n    if (!group->itimer) {\n        struct timer *timer = timer_new(CLOCK_REALTIME, (timer_callback_t) itimer_notify, current);\n        if (IS_ERR(timer))\n            return PTR_ERR(timer);\n        group->itimer = timer;\n    }\n\n    return timer_set(group->itimer, spec, old_spec);\n}\n\nint_t sys_setitimer(int_t which, addr_t new_val_addr, addr_t old_val_addr) {\n    struct itimerval_ val;\n    if (user_get(new_val_addr, val))\n        return _EFAULT;\n    STRACE(\"setitimer(%d, {%ds %dus, %ds %dus}, 0x%x)\", which, val.value.sec, val.value.usec, val.interval.sec, val.interval.usec, old_val_addr);\n\n    struct timer_spec spec = {\n        .interval.tv_sec = val.interval.sec,\n        .interval.tv_nsec = val.interval.usec * 1000,\n        .value.tv_sec = val.value.sec,\n        .value.tv_nsec = val.value.usec * 1000,\n    };\n    struct timer_spec old_spec;\n\n    struct tgroup *group = current->group;\n    lock(&group->lock);\n    int err = itimer_set(group, which, spec, &old_spec);\n    unlock(&group->lock);\n    if (err < 0)\n        return err;\n\n    if (old_val_addr != 0) {\n        struct itimerval_ old_val;\n        old_val.interval.sec = old_spec.interval.tv_sec;\n        old_val.interval.usec = old_spec.interval.tv_nsec / 1000;\n        old_val.value.sec = old_spec.value.tv_sec;\n        old_val.value.usec = old_spec.value.tv_nsec / 1000;\n        if (user_put(old_val_addr, old_val))\n            return _EFAULT;\n    }\n\n    return 0;\n}\n\nuint_t sys_alarm(uint_t seconds) {\n    STRACE(\"alarm(%d)\", seconds);\n    struct timer_spec spec = {\n        .value.tv_sec = seconds,\n    };\n    struct timer_spec old_spec;\n\n    struct tgroup *group = current->group;\n    lock(&group->lock);\n    int err = itimer_set(group, ITIMER_REAL_, spec, &old_spec);\n    unlock(&group->lock);\n    if (err < 0)\n        return err;\n\n    // Round up, and make sure to not return 0 if old_spec is > 0\n    seconds = old_spec.value.tv_sec;\n    if (old_spec.value.tv_nsec >= 500000000)\n        seconds++;\n    if (seconds == 0 && !timespec_is_zero(old_spec.value))\n        seconds = 1;\n    return seconds;\n}\n\ndword_t sys_nanosleep(addr_t req_addr, addr_t rem_addr) {\n    struct timespec_ req_ts;\n    if (user_get(req_addr, req_ts))\n        return _EFAULT;\n    STRACE(\"nanosleep({%d, %d}, 0x%x\", req_ts.sec, req_ts.nsec, rem_addr);\n    struct timespec req;\n    req.tv_sec = req_ts.sec;\n    req.tv_nsec = req_ts.nsec;\n    struct timespec rem;\n    if (nanosleep(&req, &rem) < 0)\n        return errno_map();\n    if (rem_addr != 0) {\n        struct timespec_ rem_ts;\n        rem_ts.sec = rem.tv_sec;\n        rem_ts.nsec = rem.tv_nsec;\n        if (user_put(rem_addr, rem_ts))\n            return _EFAULT;\n    }\n    return 0;\n}\n\ndword_t sys_times(addr_t tbuf) {\n    STRACE(\"times(0x%x)\", tbuf);\n    if (tbuf) {\n        struct tms_ tmp;\n        struct rusage_ rusage = rusage_get_current();\n        tmp.tms_utime = clock_from_timeval(rusage.utime);\n        tmp.tms_stime = clock_from_timeval(rusage.stime);\n        tmp.tms_cutime = tmp.tms_utime;\n        tmp.tms_cstime = tmp.tms_stime;\n        if (user_put(tbuf, tmp))\n            return _EFAULT;\n    }\n    return 0;\n}\n\ndword_t sys_gettimeofday(addr_t tv, addr_t tz) {\n    STRACE(\"gettimeofday(0x%x, 0x%x)\", tv, tz);\n    struct timeval timeval;\n    struct timezone timezone;\n    if (gettimeofday(&timeval, &timezone) < 0) {\n        return errno_map();\n    }\n    struct timeval_ tv_;\n    struct timezone_ tz_;\n    tv_.sec = timeval.tv_sec;\n    tv_.usec = timeval.tv_usec;\n    tz_.minuteswest = timezone.tz_minuteswest;\n    tz_.dsttime = timezone.tz_dsttime;\n    if ((tv && user_put(tv, tv_)) || (tz && user_put(tz, tz_))) {\n        return _EFAULT;\n    }\n    return 0;\n}\n\ndword_t sys_settimeofday(addr_t UNUSED(tv), addr_t UNUSED(tz)) {\n    return _EPERM;\n}\n\nstatic void posix_timer_callback(struct posix_timer *timer) {\n    if (timer->tgroup == NULL)\n        return;\n    struct siginfo_ info = {\n        .code = SI_TIMER_,\n        .timer.timer = timer->timer_id,\n        .timer.overrun = 0,\n        .timer.value = timer->sig_value,\n    };\n    lock(&pids_lock);\n    struct task *thread = pid_get_task(timer->thread_pid);\n    // TODO: solve pid reuse. currently we have two ways of referring to a task: pid_t_ and struct task *. pids get reused. task struct pointers get freed on exit or reap. need a third option for cases like this, like a refcount layer.\n    if (thread != NULL)\n        send_signal(thread, timer->signal, info);\n    unlock(&pids_lock);\n}\n\n#define SIGEV_SIGNAL_ 0\n#define SIGEV_NONE_ 1\n#define SIGEV_THREAD_ID_ 4\n\nint_t sys_timer_create(dword_t clock, addr_t sigevent_addr, addr_t timer_addr) {\n    STRACE(\"timer_create(%d, %#x, %#x)\", clock, sigevent_addr, timer_addr);\n    clockid_t real_clockid;\n    if (clockid_to_real(clock, &real_clockid))\n        return _EINVAL;\n    struct sigevent_ sigev;\n    if (user_get(sigevent_addr, sigev))\n        return _EFAULT;\n    if (sigev.method != SIGEV_SIGNAL_ && sigev.method != SIGEV_NONE_ && sigev.method != SIGEV_THREAD_ID_)\n        return _EINVAL;\n\n    if (sigev.method == SIGEV_THREAD_ID_) {\n        lock(&pids_lock);\n        if (pid_get_task(sigev.tid) == NULL)\n            return _EINVAL;\n        unlock(&pids_lock);\n    }\n\n    struct tgroup *group = current->group;\n    lock(&group->lock);\n    unsigned timer_id;\n    for (timer_id = 0; timer_id < TIMERS_MAX; timer_id++) {\n        if (group->posix_timers[timer_id].timer == NULL)\n            break;\n    }\n    if (timer_id >= TIMERS_MAX) {\n        unlock(&group->lock);\n        return _ENOMEM;\n    }\n    if (user_put(timer_addr, timer_id)) {\n        unlock(&group->lock);\n        return _EFAULT;\n    }\n\n    struct posix_timer *timer = &group->posix_timers[timer_id];\n    timer->timer_id = timer_id;\n    timer->timer = timer_new(real_clockid, (timer_callback_t) posix_timer_callback, timer);\n    timer->signal = sigev.signo;\n    timer->sig_value = sigev.value;\n    timer->tgroup = NULL;\n    if (sigev.method == SIGEV_SIGNAL_) {\n        timer->tgroup = group;\n        timer->thread_pid = 0;\n    } else if (sigev.method == SIGEV_THREAD_ID_) {\n        timer->tgroup = group;\n        timer->thread_pid = group->leader->pid;\n    }\n    unlock(&group->lock);\n    return 0;\n}\n\n#define TIMER_ABSTIME_ (1 << 0)\n\nint_t sys_timer_settime(dword_t timer_id, int_t flags, addr_t new_value_addr, addr_t old_value_addr) {\n    STRACE(\"timer_settime(%d, %d, %#x, %#x)\", timer_id, flags, new_value_addr, old_value_addr);\n    struct itimerspec_ value;\n    if (user_get(new_value_addr, value))\n        return _EFAULT;\n    if (timer_id > TIMERS_MAX)\n        return _EINVAL;\n\n    lock(&current->group->lock);\n    struct posix_timer *timer = &current->group->posix_timers[timer_id];\n    struct timer_spec spec = timer_spec_to_real(value);\n    struct timer_spec old_spec;\n    if (flags & TIMER_ABSTIME_) {\n        struct timespec now = timespec_now(timer->timer->clockid);\n        spec.value = timespec_subtract(spec.value, now);\n    }\n    int err = timer_set(timer->timer, spec, &old_spec);\n    unlock(&current->group->lock);\n    if (err < 0)\n        return err;\n\n    if (old_value_addr) {\n        struct itimerspec_ old_value = timer_spec_from_real(old_spec);\n        if (user_put(old_value_addr, old_value))\n            return _EFAULT;\n    }\n    return 0;\n}\n\nint_t sys_timer_delete(dword_t timer_id) {\n    STRACE(\"timer_delete(%d)\\n\", timer_id);\n    lock(&current->group->lock);\n    struct posix_timer *timer = &current->group->posix_timers[timer_id];\n    if (timer->timer == NULL) {\n        unlock(&current->group->lock);\n        return _EINVAL;\n    }\n    timer_free(timer->timer);\n    timer->timer = NULL;\n    unlock(&current->group->lock);\n    return 0;\n}\n\nstatic struct fd_ops timerfd_ops;\n\nstatic void timerfd_callback(struct fd *fd) {\n    lock(&fd->lock);\n    fd->timerfd.expirations++;\n    notify(&fd->cond);\n    unlock(&fd->lock);\n    poll_wakeup(fd, POLL_READ);\n}\n\nfd_t sys_timerfd_create(int_t clockid, int_t flags) {\n    STRACE(\"timerfd_create(%d, %#x)\", clockid, flags);\n    clockid_t real_clockid;\n    if (clockid_to_real(clockid, &real_clockid)) return _EINVAL;\n\n    struct fd *fd = adhoc_fd_create(&timerfd_ops);\n    if (fd == NULL)\n        return _ENOMEM;\n\n    fd->timerfd.timer = timer_new(real_clockid, (timer_callback_t) timerfd_callback, fd);\n    return f_install(fd, flags);\n}\n\nint_t sys_timerfd_settime(fd_t f, int_t flags, addr_t new_value_addr, addr_t old_value_addr) {\n    STRACE(\"timerfd_settime(%d, %d, %#x, %#x)\", f, flags, new_value_addr, old_value_addr);\n    if (flags & ~(TIMER_ABSTIME_))\n        return _EINVAL;\n    struct fd *fd = f_get(f);\n    if (fd == NULL)\n        return _EBADF;\n    if (fd->ops != &timerfd_ops)\n        return _EINVAL;\n    struct itimerspec_ value;\n    if (user_get(new_value_addr, value))\n        return _EFAULT;\n    struct timer_spec spec = timer_spec_to_real(value);\n    struct timer_spec old_spec;\n    if (flags & TIMER_ABSTIME_) {\n        struct timespec now = timespec_now(fd->timerfd.timer->clockid);\n        spec.value = timespec_subtract(spec.value, now);\n    }\n\n    lock(&fd->lock);\n    int err = timer_set(fd->timerfd.timer, spec, &old_spec);\n    unlock(&fd->lock);\n    if (err < 0)\n        return err;\n\n    if (old_value_addr) {\n        struct itimerspec_ old_value = timer_spec_from_real(old_spec);\n        if (user_put(old_value_addr, old_value))\n            return _EFAULT;\n    }\n\n    return 0;\n}\n\nstatic ssize_t timerfd_read(struct fd *fd, void *buf, size_t bufsize) {\n    if (bufsize < sizeof(uint64_t))\n        return _EINVAL;\n    lock(&fd->lock);\n    while (fd->timerfd.expirations == 0) {\n        if (fd->flags & O_NONBLOCK_) {\n            unlock(&fd->lock);\n            return _EAGAIN;\n        }\n        int err = wait_for(&fd->cond, &fd->lock, NULL);\n        if (err < 0) {\n            unlock(&fd->lock);\n            return err;\n        }\n    }\n\n    *(uint64_t *) buf = fd->timerfd.expirations;\n    fd->timerfd.expirations = 0;\n    unlock(&fd->lock);\n    return sizeof(uint64_t);\n}\nstatic int timerfd_poll(struct fd *fd) {\n    int res = 0;\n    lock(&fd->lock);\n    if (fd->timerfd.expirations != 0)\n        res |= POLL_READ;\n    unlock(&fd->lock);\n    return res;\n}\nstatic int timerfd_close(struct fd *fd) {\n    timer_free(fd->timerfd.timer);\n    return 0;\n}\n\nstatic struct fd_ops timerfd_ops = {\n    .read = timerfd_read,\n    .poll = timerfd_poll,\n    .close = timerfd_close,\n};\n"
  },
  {
    "path": "kernel/time.h",
    "content": "#ifndef TIME_H\n#define TIME_H\n#include \"misc.h\"\n\ndword_t sys_time(addr_t time_out);\ndword_t sys_stime(addr_t time);\n#define CLOCK_REALTIME_ 0\n#define CLOCK_MONOTONIC_ 1\n#define CLOCK_PROCESS_CPUTIME_ID_ 2\n#define CLOCK_REALTIME_COARSE_ 5\ndword_t sys_clock_gettime(dword_t clock, addr_t tp);\ndword_t sys_clock_settime(dword_t clock, addr_t tp);\ndword_t sys_clock_getres(dword_t clock, addr_t res_addr);\n\nstruct timeval_ {\n    dword_t sec;\n    dword_t usec;\n};\nstruct timespec_ {\n    dword_t sec;\n    dword_t nsec;\n};\nstruct timezone_ {\n    dword_t minuteswest;\n    dword_t dsttime;\n};\n\nstatic inline clock_t_ clock_from_timeval(struct timeval_ timeval) {\n    return timeval.sec * 100 + timeval.usec / 10000;\n}\n\nstatic inline struct timespec convert_timespec(struct timespec_ t) {\n    struct timespec ts;\n    ts.tv_sec = t.sec;\n    ts.tv_nsec = t.nsec;\n    return ts;\n}\n\nstatic inline struct timespec convert_timeval(struct timeval_ t) {\n    struct timespec ts;\n    ts.tv_sec = t.sec;\n    ts.tv_nsec = t.usec * 1000;\n    return ts;\n}\n\n#define ITIMER_REAL_ 0\n#define ITIMER_VIRTUAL_ 1\n#define ITIMER_PROF_ 2\nstruct itimerval_ {\n    struct timeval_ interval;\n    struct timeval_ value;\n};\n\nstruct itimerspec_ {\n    struct timespec_ interval;\n    struct timespec_ value;\n};\n\nstruct tms_ {\n    clock_t_ tms_utime;  /* user time */\n    clock_t_ tms_stime;  /* system time */\n    clock_t_ tms_cutime; /* user time of children */\n    clock_t_ tms_cstime; /* system time of children */\n};\n\nint_t sys_setitimer(int_t which, addr_t new_val, addr_t old_val);\nuint_t sys_alarm(uint_t seconds);\nint_t sys_timer_create(dword_t clock, addr_t sigevent_addr, addr_t timer_addr);\nint_t sys_timer_settime(dword_t timer, int_t flags, addr_t new_value_addr, addr_t old_value_addr);\nint_t sys_timer_delete(dword_t timer_id);\nfd_t sys_timerfd_create(int_t clockid, int_t flags);\nint_t sys_timerfd_settime(fd_t f, int_t flags, addr_t new_value_addr, addr_t old_value_addr);\n\ndword_t sys_times(addr_t tbuf);\ndword_t sys_nanosleep(addr_t req, addr_t rem);\ndword_t sys_gettimeofday(addr_t tv, addr_t tz);\ndword_t sys_settimeofday(addr_t tv, addr_t tz);\n\n\n#endif\n"
  },
  {
    "path": "kernel/tls.c",
    "content": "#include \"kernel/calls.h\"\n\nstruct user_desc {\n    dword_t entry_number;\n    dword_t base_addr;\n    dword_t limit;\n    unsigned int seg_32bit:1;\n    unsigned int contents:2;\n    unsigned int read_exec_only:1;\n    unsigned int limit_in_pages:1;\n    unsigned int seg_not_present:1;\n    unsigned int useable:1;\n};\n\nint task_set_thread_area(struct task *task, addr_t u_info) {\n    struct user_desc info;\n    if (user_get_task(task, u_info, info))\n        return _EFAULT;\n\n    // On a real system, TLS works by creating a special segment pointing to\n    // the TLS buffer. Our shitty emulation of that is to ignore attempts to\n    // modify GS and add this address to any memory reference that uses GS.\n    task->cpu.tls_ptr = info.base_addr;\n\n    if (info.entry_number == (unsigned) -1) {\n        info.entry_number = 0xc;\n    }\n\n    if (user_put(u_info, info))\n            return _EFAULT;\n    return 0;\n}\n\nint sys_set_thread_area(addr_t u_info) {\n    STRACE(\"set_thread_area(0x%x)\", u_info);\n    return task_set_thread_area(current, u_info);\n}\n\nint sys_set_tid_address(addr_t tid) {\n    current->clear_tid = tid;\n    return sys_getpid();\n}\n"
  },
  {
    "path": "kernel/uname.c",
    "content": "#include <sys/utsname.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"platform/platform.h\"\n\n#if __APPLE__\n#include <sys/sysctl.h>\n#elif __linux__\n#include <sys/sysinfo.h>\n#endif\n\nconst char *uname_version = \"SUPER AWESOME\";\nconst char *uname_hostname_override = NULL;\n\nvoid do_uname(struct uname *uts) {\n    struct utsname real_uname;\n    uname(&real_uname);\n    const char *hostname = real_uname.nodename;\n    if (uname_hostname_override)\n        hostname = uname_hostname_override;\n\n    memset(uts, 0, sizeof(struct uname));\n    strcpy(uts->system, \"Linux\");\n    strcpy(uts->hostname, hostname);\n    strcpy(uts->release, \"4.20.69-ish\");\n    snprintf(uts->version, sizeof(uts->version), \"%s %s %s\", uname_version, __DATE__, __TIME__);\n    strcpy(uts->arch, \"i686\");\n    strcpy(uts->domain, \"(none)\");\n}\n\ndword_t sys_uname(addr_t uts_addr) {\n    struct uname uts;\n    do_uname(&uts);\n    if (user_put(uts_addr, uts))\n        return _EFAULT;\n    return 0;\n}\n\ndword_t sys_sethostname(addr_t UNUSED(hostname_addr), dword_t UNUSED(hostname_len)) {\n    return _EPERM;\n}\n\n#if __APPLE__\nstatic uint64_t get_total_ram() {\n    uint64_t total_ram;\n    sysctl((int []) {CTL_DEBUG, HW_PHYSMEM}, 2, &total_ram, NULL, NULL, 0);\n    return total_ram;\n}\nstatic void sysinfo_specific(struct sys_info *info) {\n    info->totalram = get_total_ram();\n    // TODO: everything else\n}\n#elif __linux__\nstatic void sysinfo_specific(struct sys_info *info) {\n    struct sysinfo host_info;\n    sysinfo(&host_info);\n    info->totalram = host_info.totalram;\n    info->freeram = host_info.freeram;\n    info->sharedram = host_info.sharedram;\n    info->totalswap = host_info.totalswap;\n    info->freeswap = host_info.freeswap;\n    info->procs = host_info.procs;\n    info->totalhigh = host_info.totalhigh;\n    info->freehigh = host_info.freehigh;\n    info->mem_unit = host_info.mem_unit;\n}\n#endif\n\ndword_t sys_sysinfo(addr_t info_addr) {\n    struct sys_info info = {0};\n    struct uptime_info uptime = get_uptime();\n    info.uptime = uptime.uptime_ticks;\n    info.loads[0] = uptime.load_1m;\n    info.loads[1] = uptime.load_5m;\n    info.loads[2] = uptime.load_15m;\n    sysinfo_specific(&info);\n\n    if (user_put(info_addr, info))\n        return _EFAULT;\n    return 0;\n}\n"
  },
  {
    "path": "kernel/user.c",
    "content": "#include <string.h>\n#include \"kernel/calls.h\"\n\nstatic int __user_read_task(struct task *task, addr_t addr, void *buf, size_t count) {\n    char *cbuf = (char *) buf;\n    addr_t p = addr;\n    while (p < addr + count) {\n        addr_t chunk_end = (PAGE(p) + 1) << PAGE_BITS;\n        if (chunk_end > addr + count)\n            chunk_end = addr + count;\n        const char *ptr = mem_ptr(task->mem, p, MEM_READ);\n        if (ptr == NULL)\n            return 1;\n        memcpy(&cbuf[p - addr], ptr, chunk_end - p);\n        p = chunk_end;\n    }\n    return 0;\n}\n\nstatic int __user_write_task(struct task *task, addr_t addr, const void *buf, size_t count, bool ptrace) {\n    const char *cbuf = (const char *) buf;\n    addr_t p = addr;\n    while (p < addr + count) {\n        addr_t chunk_end = (PAGE(p) + 1) << PAGE_BITS;\n        if (chunk_end > addr + count)\n            chunk_end = addr + count;\n        char *ptr = mem_ptr(task->mem, p, ptrace ? MEM_WRITE_PTRACE : MEM_WRITE);\n        if (ptr == NULL)\n            return 1;\n        memcpy(ptr, &cbuf[p - addr], chunk_end - p);\n        p = chunk_end;\n    }\n    return 0;\n}\n\nint user_read_task(struct task *task, addr_t addr, void *buf, size_t count) {\n    read_wrlock(&task->mem->lock);\n    int res = __user_read_task(task, addr, buf, count);\n    read_wrunlock(&task->mem->lock);\n    return res;\n}\n\nint user_read(addr_t addr, void *buf, size_t count) {\n    return user_read_task(current, addr, buf, count);\n}\n\nint user_write_task(struct task *task, addr_t addr, const void *buf, size_t count) {\n    read_wrlock(&task->mem->lock);\n    int res = __user_write_task(task, addr, buf, count, false);\n    read_wrunlock(&task->mem->lock);\n    return res;\n}\n\nint user_write_task_ptrace(struct task *task, addr_t addr, const void *buf, size_t count) {\n    read_wrlock(&task->mem->lock);\n    int res = __user_write_task(task, addr, buf, count, true);\n    read_wrunlock(&task->mem->lock);\n    return res;\n}\n\nint user_write(addr_t addr, const void *buf, size_t count) {\n    return user_write_task(current, addr, buf, count);\n}\n\nint user_read_string(addr_t addr, char *buf, size_t max) {\n    if (addr == 0)\n        return 1;\n    read_wrlock(&current->mem->lock);\n    size_t i = 0;\n    while (i < max) {\n        if (__user_read_task(current, addr + i, &buf[i], sizeof(buf[i]))) {\n            read_wrunlock(&current->mem->lock);\n            return 1;\n        }\n        if (buf[i] == '\\0')\n            break;\n        i++;\n    }\n    read_wrunlock(&current->mem->lock);\n    return 0;\n}\n\nint user_write_string(addr_t addr, const char *buf) {\n    if (addr == 0)\n        return 1;\n    read_wrlock(&current->mem->lock);\n    size_t i = 0;\n    do {\n        if (__user_write_task(current, addr + i, &buf[i], sizeof(buf[i]), false)) {\n            read_wrunlock(&current->mem->lock);\n            return 1;\n        }\n        i++;\n    } while (buf[i - 1] != '\\0');\n    read_wrunlock(&current->mem->lock);\n    return 0;\n}\n"
  },
  {
    "path": "kernel/vdso.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"kernel/elf.h\"\n#include \"kernel/vdso.h\"\n\n__asm__(\".data\\n\"\n        \".global vdso_data\\n\"\n        \"vdso_data:\\n\"\n        \".incbin \\\"vdso/libvdso.so.elf\\\"\\n\"\n        \".skip \"str(VDSO_PAGES)\" * (1 << 12) - (. - vdso_data)\\n\");\n\nint vdso_symbol(const char *name) {\n    struct elf_header *header = (void *) vdso_data;\n    struct prg_header *ph = (void *) ((char *) header + header->prghead_off);\n\n    // find the PT_DYNAMIC section\n    struct dyn_ent *dyn = NULL;\n    for (int i = 0; i < header->phent_count; i++) {\n        if (ph[i].type == PT_DYNAMIC) {\n            dyn = (void *) ((char *) header + ph[i].offset);\n            break;\n        }\n    }\n    if (dyn == NULL)\n        goto fail;\n\n    // grab pointers to the symbols and the strings\n    char *strings = NULL;\n    struct elf_sym *syms = NULL;\n    uint32_t *hash = NULL;\n    for (; dyn->tag != DT_NULL; dyn++) {\n        void *p = (char *) header + dyn->val;\n        if (dyn->tag == DT_STRTAB)\n            strings = p;\n        else if (dyn->tag == DT_SYMTAB)\n            syms = p;\n        else if (dyn->tag == DT_HASH)\n            hash = p;\n    }\n    if (strings == NULL || syms == NULL || hash == NULL)\n        goto fail;\n\n    // conveniently enough, the hashtable includes the number of symbols, which doesn't seeem to be anywhere else\n    // https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/\n    int num_syms = hash[1];\n    for (int i = 0; i < num_syms; i++) {\n        char *sym_name = strings + syms[i].name;\n        if (strcmp(name, sym_name) == 0)\n            return syms[i].value;\n    }\n    return 0; // symbol not found\n\nfail:\n    // It shouldn't be possible to actually end up with an invalid vsdo compiled in\n    fflush(stdout);\n    fprintf(stderr, \"invalid vdso. this should never happen.\\n\");\n    fflush(stderr);\n    abort();\n}\n"
  },
  {
    "path": "kernel/vdso.h",
    "content": "#ifndef KERNEL_VDSO_H\n#define KERNEL_VDSO_H\n#include \"tools/ptraceomatic-config.h\"\n\nextern const char vdso_data[VDSO_PAGES * (1 << 12)] __asm__(\"vdso_data\");\nint vdso_symbol(const char *name);\n\n#endif\n"
  },
  {
    "path": "linux/emu_asbestos.c",
    "content": "#include <stdbool.h>\n#include <stdlib.h>\n\n#include <linux/threads.h>\n#include <asm/ptrace.h>\n#include <emu/exec.h>\n#include <emu/kernel.h>\n#include \"../kernel/irq_user.h\"\n\n#include \"emu/cpu.h\"\n#include \"emu/tlb.h\"\n#include \"emu/interrupt.h\"\n#define ENGINE_ASBESTOS 1\n#include \"asbestos/asbestos.h\"\n\nextern int current_pid(void);\n\nstruct emu_mm_ctx {\n\tstruct mmu mmu;\n\tstruct emu_mm *emu_mm;\n};\n\nstatic __thread struct tlb the_tlb;\n\nstatic void *ishemu_translate(struct mmu *mem, addr_t addr, int type)\n{\n\tstruct emu_mm *emu_mm = container_of(mem, struct emu_mm_ctx, mmu)->emu_mm;\n\tbool writable;\n\tvoid *ptr = user_to_kernel_emu(emu_mm, addr, &writable);\n\tif (ptr && type == MEM_WRITE && !writable) {\n\t\tptr = NULL;\n\t}\n\treturn ptr;\n}\n\nstatic struct mmu_ops ishemu_ops = {\n\t.translate = ishemu_translate,\n};\n\nstatic bool poke[NR_CPUS];\n\nstatic void emu_run_to_interrupt(struct emu *emu, struct cpu_state *cpu)\n{\n\tstruct pt_regs *regs = emu_pt_regs(emu);\n\tstruct emu_mm_ctx *mm_ctx = emu->mm->ctx;\n\n\tcpu->mmu = &mm_ctx->mmu;\n\tcpu->eax = regs->ax;\n\tcpu->ebx = regs->bx;\n\tcpu->ecx = regs->cx;\n\tcpu->edx = regs->dx;\n\tcpu->esi = regs->si;\n\tcpu->edi = regs->di;\n\tcpu->ebp = regs->bp;\n\tcpu->esp = regs->sp;\n\tcpu->eip = regs->ip;\n\tcpu->eflags = regs->flags;\n\tcpu->tls_ptr = regs->tls;\n\texpand_flags(cpu);\n\tcpu->poked_ptr = &poke[get_smp_processor_id()];\n\n\tint interrupt = cpu_run_to_interrupt(cpu, &the_tlb);\n\n\tcollapse_flags(cpu);\n\tregs->ax = cpu->eax;\n\tregs->bx = cpu->ebx;\n\tregs->cx = cpu->ecx;\n\tregs->dx = cpu->edx;\n\tregs->si = cpu->esi;\n\tregs->di = cpu->edi;\n\tregs->bp = cpu->ebp;\n\tregs->sp = cpu->esp;\n\tregs->ip = cpu->eip;\n\tregs->flags = cpu->eflags;\n\tregs->tls = cpu->tls_ptr;\n\n\tif (interrupt == INT_GPF) {\n\t\tregs->cr2 = cpu->segfault_addr;\n\t\tregs->error_code = cpu->segfault_was_write << 1;\n\t} else {\n\t\tregs->cr2 = regs->error_code = 0;\n\t}\n\tregs->trap_nr = interrupt;\n}\n\nvoid emu_run(struct emu *emu)\n{\n\tstruct cpu_state cpu = {};\n\tif (emu->snapshot) {\n\t\tstruct cpu_state *snapshot = emu->snapshot;\n\t\tcpu = *snapshot;\n\t\tfree(snapshot);\n\t\temu->snapshot = NULL;\n\t}\n\temu->ctx = &cpu;\n\tfor (;;) {\n\t\temu_run_to_interrupt(emu, &cpu);\n\t\thandle_cpu_trap(emu);\n\t}\n}\n\nvoid emu_finish_fork(struct emu *emu)\n{\n\tstruct cpu_state *cpu = emu->ctx;\n\tstruct cpu_state *snapshot = emu->snapshot = calloc(1, sizeof(*snapshot));\n\t*snapshot = *cpu;\n\temu->ctx = NULL;\n}\n\nvoid emu_destroy(struct emu *emu)\n{\n}\n\nvoid emu_poke_cpu(int cpu)\n{\n\t__atomic_store_n(&poke[cpu], true, __ATOMIC_SEQ_CST);\n}\n\nvoid emu_flush_tlb_local(struct emu_mm *mm, unsigned long start, unsigned long end)\n{\n\tif (the_tlb.mmu == NULL)\n\t\treturn;\n\ttlb_flush(&the_tlb);\n\tstruct emu_mm_ctx *mm_ctx = mm->ctx;\n\tif (mm_ctx->mmu.asbestos != NULL)\n\t\tasbestos_invalidate_range(mm_ctx->mmu.asbestos, start / PAGE_SIZE, (end + PAGE_SIZE - 1) / PAGE_SIZE /* TODO DIV_ROUND_UP? */);\n}\n\nvoid emu_mmu_init(struct emu_mm *mm)\n{\n\tstruct emu_mm_ctx *mm_ctx = mm->ctx = calloc(1, sizeof(*mm_ctx));\n\tmm_ctx->emu_mm = mm;\n\tmm_ctx->mmu.asbestos = asbestos_new(&mm_ctx->mmu);\n\tmm_ctx->mmu.ops = &ishemu_ops;\n}\n\nvoid emu_mmu_destroy(struct emu_mm *mm)\n{\n\tstruct emu_mm_ctx *mm_ctx = mm->ctx;\n\tasbestos_free(mm_ctx->mmu.asbestos);\n\tmm_ctx->mmu.asbestos = NULL;\n}\n\nvoid emu_switch_mm(struct emu *emu, struct emu_mm *mm)\n{\n\tstruct emu_mm_ctx *mm_ctx = mm->ctx;\n\ttlb_refresh(&the_tlb, &mm_ctx->mmu);\n}\n"
  },
  {
    "path": "linux/emu_unicorn.c",
    "content": "#include <unicorn/unicorn.h>\n#include <asm/ptrace.h>\n#include <asm/page.h>\n\n#include \"emu_unicorn.h\"\n\n#include <emu/exec.h>\n#include <emu/kernel.h>\n\n#define check(err) __check(err, __FUNCTION__, __LINE__)\nstatic void __check(uc_err err, const char *function, int line)\n{\n\tif (err != UC_ERR_OK) {\n\t\tpanic(\"%s:%d: %s\", function, line, uc_strerror(err));\n\t}\n}\n\n#define uc_reg(op, uc, reg_id, field) \\\n\tcheck(uc_reg_##op##2((uc), (reg_id), (field), (size_t[]){sizeof(*(field))}))\n\n/****** GDT ******/\n\nstruct gdt_desc {\n\tunsigned limit1:16;\n\tunsigned base1:16;\n\tunsigned base2:8;\n\tunsigned type:4;\n\tunsigned system:1;\n\tunsigned dpl:2;\n\tunsigned present:1;\n\tunsigned limit2:4;\n\tunsigned avl:1;\n\tunsigned _:1;\n\tunsigned db:1;\n\tunsigned granularity:1;\n\tunsigned base3:8;\n} __attribute__((packed));\n\nstatic const uint32_t gdt_base = 0xfffff000;\nstatic const uint32_t gdt_size = 16 * sizeof(struct gdt_desc);\n\nstatic void install_gdt_segment(uc_engine *uc, int segment, uint32_t base, int dpl)\n{\n\tstruct gdt_desc desc = {\n\t\t.limit1 = 0xffff,\n\t\t.limit2 = 0xf,\n\t\t.base1 = (base & 0x0000ffff) >> 0,\n\t\t.base2 = (base & 0x00ff0000) >> 16,\n\t\t.base3 = (base & 0xff000000) >> 24,\n\t\t.type = 3, // read & write\n\t\t.system = 1, // user\n\t\t.dpl = dpl,\n\t\t.present = 1,\n\t\t.db = 1, // 32 bit code\n\t\t.granularity = 1,\n\t};\n\tcheck(uc_mem_write(uc, gdt_base + (segment * sizeof(desc)), &desc, sizeof(desc)));\n}\n\n/****** Entry/exit ******/\n\nstatic void load_regs(struct emu *emu)\n{\n\tstruct emu_uc *ctx = emu->ctx;\n\tuc_engine *uc = ctx->uc;\n\tstruct pt_regs *regs = emu_pt_regs(emu);\n\tuc_reg(write, uc, UC_X86_REG_EAX, &regs->ax);\n\tuc_reg(write, uc, UC_X86_REG_EBX, &regs->bx);\n\tuc_reg(write, uc, UC_X86_REG_ECX, &regs->cx);\n\tuc_reg(write, uc, UC_X86_REG_EDX, &regs->dx);\n\tuc_reg(write, uc, UC_X86_REG_ESI, &regs->si);\n\tuc_reg(write, uc, UC_X86_REG_EDI, &regs->di);\n\tuc_reg(write, uc, UC_X86_REG_EBP, &regs->bp);\n\tuc_reg(write, uc, UC_X86_REG_ESP, &regs->sp);\n\tuc_reg(write, uc, UC_X86_REG_EIP, &regs->ip);\n\tuc_reg(write, uc, UC_X86_REG_FLAGS, &regs->flags);\n\n\tif (ctx->tls_ptr != regs->tls) {\n\t\tctx->tls_ptr = regs->tls;\n\t\tinstall_gdt_segment(uc, 0xc, ctx->tls_ptr, 3);\n\t}\n\tunsigned long mm_flush_count = __atomic_load_n(&emu->mm->flush_count, __ATOMIC_SEQ_CST);\n\tif (ctx->mm_flush_count != mm_flush_count) {\n\t\tcheck(uc_ctl_flush_tlb(uc));\n\t\tctx->mm_flush_count = mm_flush_count;\n\t}\n}\n\nstatic void save_regs(struct emu *emu)\n{\n\tstruct emu_uc *ctx = emu->ctx;\n\tuc_engine *uc = ctx->uc;\n\tstruct pt_regs *regs = emu_pt_regs(emu);\n\tregs->ax = 0;\n\tuc_reg(read, uc, UC_X86_REG_EAX, &regs->ax);\n\tuc_reg(read, uc, UC_X86_REG_EBX, &regs->bx);\n\tuc_reg(read, uc, UC_X86_REG_ECX, &regs->cx);\n\tuc_reg(read, uc, UC_X86_REG_EDX, &regs->dx);\n\tuc_reg(read, uc, UC_X86_REG_ESI, &regs->si);\n\tuc_reg(read, uc, UC_X86_REG_EDI, &regs->di);\n\tuc_reg(read, uc, UC_X86_REG_EBP, &regs->bp);\n\tuc_reg(read, uc, UC_X86_REG_ESP, &regs->sp);\n\tuc_reg(read, uc, UC_X86_REG_EIP, &regs->ip);\n\tuc_reg(read, uc, UC_X86_REG_FLAGS, &regs->flags);\n}\n\nstatic void do_trap(struct emu *emu, int trap_nr)\n{\n\tsave_regs(emu);\n\temu_pt_regs(emu)->trap_nr = trap_nr;\n\thandle_cpu_trap(emu);\n\tload_regs(emu);\n}\n\n/****** Hooks ******/\n\nstatic bool mem_type_is_write(uc_mem_type type)\n{\n\tswitch (type) {\n\tcase UC_MEM_READ:\n\tcase UC_MEM_READ_UNMAPPED:\n\tcase UC_MEM_READ_PROT:\n\tcase UC_MEM_READ_AFTER:\n\tcase UC_MEM_FETCH:\n\tcase UC_MEM_FETCH_UNMAPPED:\n\tcase UC_MEM_FETCH_PROT:\n\t\treturn false;\n\tcase UC_MEM_WRITE:\n\tcase UC_MEM_WRITE_UNMAPPED:\n\tcase UC_MEM_WRITE_PROT:\n\t\treturn true;\n\t}\n}\n\nstatic bool hook_tlb_fill(uc_engine *uc, uint64_t vaddr, uc_mem_type type, uc_tlb_entry *result, void *user_data)\n{\n\tstruct emu *emu = user_data;\n\n\tif (vaddr == gdt_base) {\n\t\tresult->paddr = vaddr;\n\t\tresult->perms = UC_PROT_READ | UC_PROT_WRITE;\n\t\treturn true;\n\t}\n\n\tbool is_write = mem_type_is_write(type);\n\tbool writable;\n\tvoid *kernel_addr = user_to_kernel_emu(emu->mm, vaddr, &writable);\n\n\tif (kernel_addr == NULL || (is_write && !writable)) {\n\t\tstruct pt_regs *regs = emu_pt_regs(emu);\n\t\tregs->cr2 = vaddr;\n\t\tregs->error_code = mem_type_is_write(type) ? 2 : 0;\n\t\tdo_trap(emu, 13);\n\t\tkernel_addr = user_to_kernel_emu(emu->mm, vaddr, &writable);\n\t}\n\tif (kernel_addr == NULL) {\n\t\tuc_emu_stop(uc);\n\t\treturn false;\n\t}\n\n\tunsigned long paddr = __pa(kernel_addr);\n\tresult->paddr = __pa(kernel_addr);\n\tresult->perms = UC_PROT_READ | UC_PROT_EXEC | (writable ? UC_PROT_WRITE : 0);\n\treturn true;\n}\n\nstatic void hook_intr(uc_engine *uc, uint32_t intno, void *user_data)\n{\n\tstruct emu *emu = user_data;\n\tdo_trap(emu, intno);\n}\n\nstatic bool hook_trace_code(uc_engine *uc, uint64_t address, size_t size, void *user_data)\n{\n\tstruct emu *emu = user_data;\n\tvoid *ptr = user_to_kernel_emu(emu->mm, address, NULL);\n\tuint32_t sp;\n\tuc_reg(read, uc, UC_X86_REG_ESP, &sp);\n\textern int current_pid();\n\tprintk(\"%d code %#llx+%ld %*ph\\n\", current_pid(), address, size, (int) size, ptr);\n\treturn true;\n}\n\n/****** Main implementation ******/\n\nstatic void create_unicorn(struct emu *emu)\n{\n\tstruct emu_uc *ctx = emu->ctx = calloc(1, sizeof(*ctx));\n\n\tuc_hook hh;\n\tcheck(uc_open(UC_ARCH_X86, UC_MODE_32, &ctx->uc));\n\n\tcheck(uc_mem_map_ptr(ctx->uc, 0x0, ish_phys_size, UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, (void *) ish_phys_base));\n\tcheck(uc_ctl_tlb_mode(ctx->uc, UC_TLB_VIRTUAL));\n\tcheck(uc_hook_add(ctx->uc, &hh, UC_HOOK_TLB_FILL, hook_tlb_fill, emu, 1, 0));\n\n\tcheck(uc_mem_map(ctx->uc, gdt_base, 0x1000, UC_PROT_READ | UC_PROT_WRITE));\n\tstruct uc_x86_mmr gdtr = {.base = gdt_base, .limit = gdt_size};\n\tuc_reg(write, ctx->uc, UC_X86_REG_GDTR, &gdtr);\n\t// unicorn bug (maybe): if you load any segment register other than ss, sp suddenly becomes 16 bit. can be fixed by loading ss correctly\n\tinstall_gdt_segment(ctx->uc, 1, 0, 0);\n\tint seg = (1 << 3) | 0; // ring 0? why?\n\tuc_reg(write, ctx->uc, UC_X86_REG_SS, &seg);\n\n\tcheck(uc_hook_add(ctx->uc, &hh, UC_HOOK_INTR, hook_intr, emu, 1, 0));\n\n\tif (unicorn_trace) {\n\t\tcheck(uc_hook_add(ctx->uc, &hh, UC_HOOK_BLOCK, hook_trace_code, emu, 1, 0));\n\t}\n}\n\nvoid emu_run(struct emu *emu)\n{\n\tif (!emu->ctx) {\n\t\tcreate_unicorn(emu);\n\t}\n\tstruct emu_uc *ctx = emu->ctx;\n\n\tload_regs(emu);\n\n\tfor (;;) {\n\t\tcheck(uc_emu_start(ctx->uc, emu_pt_regs(emu)->ip, 0, 0, 0));\n\t}\n}\n\nvoid emu_finish_fork(struct emu *emu)\n{\n\tstruct emu_uc *old = emu->ctx;\n\tcreate_unicorn(emu);\n\tstruct emu_uc *new = emu->ctx;\n\n\tuc_context *ctx;\n\tcheck(uc_context_alloc(old->uc, &ctx));\n\tcheck(uc_context_save(old->uc, ctx));\n\tcheck(uc_context_restore(new->uc, ctx));\n\tcheck(uc_context_free(ctx));\n}\n\nvoid emu_destroy(struct emu *emu)\n{\n\tstruct emu_uc *ctx = emu->ctx;\n\tcheck(uc_close(ctx->uc));\n\tfree(ctx);\n\temu->ctx = NULL;\n}\n\nvoid emu_poke_cpu(int cpu) {}\n\nvoid emu_mmu_init(struct emu_mm *mm)\n{\n}\nvoid emu_mmu_destroy(struct emu_mm *mm)\n{\n}\nvoid emu_switch_mm(struct emu *emu, struct emu_mm *mm)\n{\n\temu->mm = mm;\n}\nvoid emu_flush_tlb_local(struct emu_mm *mm, unsigned long start, unsigned long end)\n{\n\t__atomic_fetch_add(&mm->flush_count, 1, __ATOMIC_SEQ_CST);\n}\n"
  },
  {
    "path": "linux/emu_unicorn.h",
    "content": "#ifndef __UNICORN_UNICORN_H\n#define __UNICORN_UNICORN_H\n#if __KERNEL__\n#include <linux/types.h>\n#else\n#include <stdbool.h>\n#endif\n\nstruct emu_uc {\n\tstruct uc_struct *uc;\n\tunsigned long tls_ptr;\n\tunsigned long mm_flush_count;\n};\n\nextern bool unicorn_trace;\n\n#endif\n"
  },
  {
    "path": "linux/emu_unicorn_kernel.c",
    "content": "#include <linux/moduleparam.h>\n#include \"emu_unicorn.h\"\n\n#define KBUILD_MODNAME \"unicorn\"\n\nbool unicorn_trace;\nmodule_param_named(trace, unicorn_trace, bool, 0);\n"
  },
  {
    "path": "linux/fakefs.c",
    "content": "/* Based on hostfs to a significant extent */\n#include <asm/fcntl.h>\n#include <linux/dcache.h>\n#include <linux/fs.h>\n#include <linux/fs_context.h>\n#include <linux/init.h>\n#include <linux/limits.h>\n#include <linux/mount.h>\n#include <linux/pagemap.h>\n#include <linux/statfs.h>\n#include <user/fs.h>\n\n#include <sqlite3.h>\n#include \"fs/fake-db.h\"\n#include \"../fs/hostfs/hostfs.h\" // just a quick way to get stat without typing too much\n\nstruct fakefs_super {\n    struct fakefs_db db;\n    int root_fd;\n};\n\n// free with __putname\nstatic char *dentry_name(struct dentry *dentry) {\n    char *name = __getname();\n    if (name == NULL)\n        return ERR_PTR(-ENOMEM);\n    char *path = dentry_path_raw(dentry, name, PATH_MAX);\n    if (IS_ERR(path))\n        return path;\n    BUG_ON(path[0] != '/');\n    if (strcmp(path, \"/\") == 0)\n        path[0] = '\\0';\n    memmove(name, path, strlen(path) + 1);\n    return name;\n}\n\n/***** inode *****/\n\nstatic int restat_inode(struct inode *ino);\nstatic int read_inode(struct inode *ino);\n\n#define INODE_FD(ino) (*((uintptr_t *) &(ino)->i_private))\n\nstatic int open_fd_for_dentry(struct inode *dir, struct dentry *dentry) {\n    int fd = host_openat(INODE_FD(dir), dentry->d_name.name, O_RDWR, 0);\n    if (fd == -EISDIR)\n        fd = host_openat(INODE_FD(dir), dentry->d_name.name, O_RDONLY, 0);\n    return fd;\n}\n\nstatic struct dentry *fakefs_lookup(struct inode *ino, struct dentry *dentry, unsigned int flags) {\n    struct fakefs_super *info = ino->i_sb->s_fs_info;\n    struct inode *child = NULL;\n\n    char *path = dentry_name(dentry);\n    if (IS_ERR(path))\n        return ERR_PTR(PTR_ERR(path));\n    db_begin_read(&info->db);\n    inode_t child_ino = path_get_inode(&info->db, path);\n    __putname(path);\n    if (child_ino == 0)\n        goto out;\n\n    child = ilookup(ino->i_sb, child_ino);\n    if (child != NULL)\n        goto out;\n\n    int fd = open_fd_for_dentry(ino, dentry);\n    if (fd < 0) {\n        child = ERR_PTR(fd);\n        goto out;\n    }\n\n    child = new_inode(ino->i_sb);\n    if (child == NULL) {\n        child = ERR_PTR(-ENOMEM);\n        host_close(fd);\n        goto out;\n    }\n    child->i_ino = child_ino;\n    INODE_FD(child) = fd;\n    int err = read_inode(child);\n    if (err < 0) {\n        iput(child);\n        /* TODO: check whether iput manages to close the FD by calling evict_inode */\n        child = ERR_PTR(err);\n        goto out;\n    }\n\nout:\n    if (IS_ERR(child)) {\n        db_rollback(&info->db);\n        printk(\"fakefs_lookup failed: %pe\\n\", child);\n    } else {\n        db_commit(&info->db);\n    }\n    return d_splice_alias(child, dentry);\n}\n\nstatic int __finish_make_node(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev, int fd) {\n    struct fakefs_super *info = dir->i_sb->s_fs_info;\n    struct inode *child = NULL;\n    int err;\n\n    if (fd == -1)\n        fd = open_fd_for_dentry(dir, dentry);\n    if (fd < 0) {\n        err = fd;\n        goto fail;\n    }\n\n    child = new_inode(dir->i_sb);\n    err = -ENOMEM;\n    if (child == NULL) {\n        host_close(fd);\n        goto fail;\n    }\n    inode_init_owner(mnt_userns, child, dir, mode);\n    INODE_FD(child) = fd;\n\n    char *path = dentry_name(dentry);\n    if (IS_ERR(path)) {\n        err = PTR_ERR(path);\n        goto fail;\n    }\n\n    db_begin_write(&info->db);\n    struct ish_stat ishstat = {\n        .mode = mode,\n        .uid = i_uid_read(child),\n        .gid = i_gid_read(child),\n        .rdev = rdev,\n    };\n    child->i_ino = path_create(&info->db, path, &ishstat);\n    __putname(path);\n\n    err = read_inode(child);\n    if (err < 0) {\n        db_rollback(&info->db);\n        goto fail;\n    }\n    db_commit(&info->db);\n    d_instantiate(dentry, child);\n    return 0;\n\nfail:\n    if (child != NULL)\n        iput(child);\n    host_unlinkat(INODE_FD(dir), dentry->d_name.name);\n    return err;\n}\n\nstatic int finish_make_node(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, int fd) {\n    return __finish_make_node(mnt_userns, dir, dentry, mode, 0, fd);\n}\n\nstatic int fakefs_create(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) {\n    int fd = host_openat(INODE_FD(dir), dentry->d_name.name, O_CREAT | O_RDWR | (excl ? O_EXCL : 0), 0666);\n    if (fd < 0)\n        return fd;\n    return finish_make_node(mnt_userns, dir, dentry, mode, fd);\n}\n\nstatic int fakefs_rename(struct user_namespace *mnt_userns, struct inode *from_dir, struct dentry *from_dentry, struct inode *to_dir, struct dentry *to_dentry, unsigned flags) {\n    if (flags != 0)\n        return -EINVAL;\n    struct fakefs_super *info = from_dir->i_sb->s_fs_info;\n\n    char *from_path = dentry_name(from_dentry);\n    if (IS_ERR(from_path))\n        return PTR_ERR(from_path);\n    char *to_path = dentry_name(to_dentry);\n    if (IS_ERR(to_path)) {\n        __putname(from_path);\n        return PTR_ERR(to_path);\n    }\n\n    db_begin_write(&info->db);\n    path_rename(&info->db, from_path, to_path);\n    __putname(from_path);\n    __putname(to_path);\n\n    int err = host_renameat(INODE_FD(from_dir), from_dentry->d_name.name,\n                            INODE_FD(to_dir), to_dentry->d_name.name);\n    if (err < 0) {\n        db_rollback(&info->db);\n        return err;\n    }\n    db_commit(&info->db);\n\n    return 0;\n}\n\nstatic int fakefs_link(struct dentry *from, struct inode *ino, struct dentry *to) {\n    struct fakefs_super *info = ino->i_sb->s_fs_info;\n    struct inode *inode;\n\n    char *from_path = dentry_name(from);\n    if (IS_ERR(from_path))\n        return PTR_ERR(from_path);\n    char *to_path = dentry_name(to);\n    if (IS_ERR(to_path)) {\n        __putname(from_path);\n        return PTR_ERR(to_path);\n    }\n\n    db_begin_write(&info->db);\n    path_link(&info->db, from_path, to_path);\n    __putname(from_path);\n    __putname(to_path);\n\n    int err = host_linkat(INODE_FD(d_inode(from->d_parent)), from->d_name.name,\n                          INODE_FD(d_inode(to->d_parent)), to->d_name.name);\n    if (err < 0) {\n        db_rollback(&info->db);\n        return err;\n    }\n    db_commit(&info->db);\n\n    inode = d_inode(from);\n    ihold(inode);\n    d_instantiate(to, inode);\n    return 0;\n}\n\nstatic int unlink_common(struct inode *dir, struct dentry *dentry, int is_dir) {\n    struct fakefs_super *info = dir->i_sb->s_fs_info;\n    char *path = dentry_name(dentry);\n    if (IS_ERR(path))\n        return PTR_ERR(path);\n\n    db_begin_write(&info->db);\n    path_unlink(&info->db, path);\n    __putname(path);\n\n    int err = (is_dir ? host_rmdirat : host_unlinkat)(INODE_FD(dir), dentry->d_name.name);\n    if (err < 0) {\n        db_rollback(&info->db);\n        return err;\n    }\n    db_commit(&info->db);\n    return 0;\n}\n\nstatic int fakefs_unlink(struct inode *dir, struct dentry *dentry) {\n    return unlink_common(dir, dentry, 0);\n}\n\nstatic int fakefs_rmdir(struct inode *dir, struct dentry *dentry) {\n    return unlink_common(dir, dentry, 1);\n}\n\nstatic int fakefs_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, const char *target) {\n    int fd = host_openat(INODE_FD(dir), dentry->d_name.name, O_RDWR | O_CREAT | O_TRUNC, 0666);\n    if (fd < 0)\n        return fd;\n    ssize_t res = host_write(fd, target, strlen(target));\n    if (res < 0) {\n        host_close(fd);\n        host_unlinkat(INODE_FD(dir), dentry->d_name.name);\n        return res;\n    }\n    return finish_make_node(mnt_userns, dir, dentry, S_IFLNK | 0777, fd);\n}\n\nstatic int fakefs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode) {\n    int err = host_mkdirat(INODE_FD(dir), dentry->d_name.name, 0777);\n    if (err < 0)\n        return err;\n    err = finish_make_node(mnt_userns, dir, dentry, S_IFDIR | mode, -1);\n    if (err < 0)\n        host_rmdirat(INODE_FD(dir), dentry->d_name.name);\n    return err;\n}\n\nstatic int fakefs_mknod(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) {\n    int fd = host_openat(INODE_FD(dir), dentry->d_name.name, O_RDWR | O_CREAT | O_TRUNC, 0666);\n    if (fd < 0)\n        return fd;\n    return __finish_make_node(mnt_userns, dir, dentry, mode, dev, fd);\n}\n\nstatic int fakefs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *attr) {\n    int err = setattr_prepare(mnt_userns, dentry, attr);\n    if (err < 0)\n        return err;\n    struct inode *inode = d_inode(dentry);\n\n    // attributes of ishstat\n    struct fakefs_super *info = inode->i_sb->s_fs_info;\n    if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) {\n        db_begin_write(&info->db);\n        struct ish_stat stat;\n        inode_read_stat_or_die(&info->db, inode->i_ino, &stat);\n        if (attr->ia_valid & ATTR_MODE)\n            stat.mode = attr->ia_mode;\n        if (attr->ia_valid & ATTR_UID)\n            stat.uid = from_kuid(mnt_userns, attr->ia_uid);\n        if (attr->ia_valid & ATTR_GID)\n            stat.gid = from_kgid(mnt_userns, attr->ia_gid);\n        inode_write_stat(&info->db, inode->i_ino, &stat);\n        db_commit(&info->db);\n    }\n\n    // size\n    if (attr->ia_valid & ATTR_SIZE && attr->ia_size != i_size_read(inode)) {\n        err = host_ftruncate(INODE_FD(inode), attr->ia_size);\n        if (err < 0)\n            return err;\n        truncate_setsize(inode, attr->ia_size);\n    }\n\n    // time\n    if (attr->ia_valid & (ATTR_ATIME | ATTR_ATIME_SET | ATTR_MTIME | ATTR_MTIME_SET)) {\n        struct timespec64 atime = {.tv_nsec = UTIME_OMIT};\n        if (attr->ia_valid & ATTR_ATIME_SET)\n            atime = attr->ia_atime;\n        else if (attr->ia_valid & ATTR_ATIME)\n            atime.tv_nsec = UTIME_NOW;\n        struct timespec64 mtime = {.tv_nsec = UTIME_OMIT};\n        if (attr->ia_valid & ATTR_MTIME_SET)\n            mtime = attr->ia_mtime;\n        else if (attr->ia_valid & ATTR_MTIME)\n            mtime.tv_nsec = UTIME_NOW;\n\n        struct host_timespec times[2] = {\n            {.tv_sec = atime.tv_sec, .tv_nsec = atime.tv_nsec},\n            {.tv_sec = mtime.tv_sec, .tv_nsec = mtime.tv_nsec},\n        };\n        err = host_futimens(INODE_FD(inode), times);\n        if (err < 0)\n            return err;\n        err = restat_inode(inode);\n        if (err < 0)\n            return err;\n    }\n\n    setattr_copy(mnt_userns, inode, attr);\n    mark_inode_dirty(inode); // TODO: is this actually necessary?\n    return 0;\n}\n\nstatic const struct inode_operations fakefs_iops = {\n    .setattr = fakefs_setattr,\n};\n\nstatic const struct inode_operations fakefs_dir_iops = {\n    .lookup = fakefs_lookup,\n    .create = fakefs_create,\n    .link = fakefs_link,\n    .unlink = fakefs_unlink,\n    .symlink = fakefs_symlink,\n    .mkdir = fakefs_mkdir,\n    .rmdir = fakefs_rmdir,\n    .mknod = fakefs_mknod,\n    .rename = fakefs_rename,\n    .setattr = fakefs_setattr,\n};\n\nstatic const struct inode_operations fakefs_link_iops = {\n    .get_link = page_get_link,\n};\n\n/***** file *****/\n\n#define FILE_DIR(file) ((file)->private_data)\n\nstatic int fakefs_file_release(struct inode *inode, struct file *file) {\n    filemap_write_and_wait(inode->i_mapping);\n    return 0;\n}\n\nstatic int fakefs_fsync(struct file *file, loff_t start, loff_t end,\n                        int datasync) {\n    int err = file_write_and_wait_range(file, start, end);\n    if (err)\n        return err;\n    return host_fsync(INODE_FD(file->f_inode), datasync);\n}\n\nstatic int fakefs_iterate(struct file *file, struct dir_context *ctx) {\n    struct fakefs_super *info = file->f_inode->i_sb->s_fs_info;\n\n    if (FILE_DIR(file) == NULL) {\n        int err = host_dup_opendir(INODE_FD(file->f_inode), &FILE_DIR(file));\n        if (err < 0)\n            return err;\n    }\n    void *dir = FILE_DIR(file);\n    int res;\n    if (ctx->pos == 0)\n        res = host_rewinddir(dir);\n    else\n        res = host_seekdir(dir, ctx->pos - 1);\n    if (res < 0)\n        return res;\n\n    char *dir_path = dentry_name(file->f_path.dentry);\n    if (IS_ERR(dir_path))\n        return PTR_ERR(dir_path);\n    size_t dir_path_len = strlen(dir_path);\n\n\n    struct host_dirent ent;\n    for (;;) {\n        res = host_readdir(dir, &ent);\n        if (res <= 0)\n            break;\n        // Get the inode number by constructing the file path and looking it up in the database\n        if (strcmp(ent.name, \".\") == 0) {\n            ent.ino = file->f_inode->i_ino;\n        } else if (strcmp(ent.name, \"..\") == 0) {\n            ent.ino = d_inode(file->f_path.dentry->d_parent)->i_ino;\n        } else {\n            db_begin_read(&info->db);\n            if (dir_path_len + 1 + strlen(ent.name) + 1 > PATH_MAX)\n                continue; // a\n            dir_path[dir_path_len] = '/';\n            strcpy(&dir_path[dir_path_len + 1], ent.name);\n            ent.ino = path_get_inode(&info->db, dir_path);\n            db_commit(&info->db);\n        }\n        if (!dir_emit(ctx, ent.name, strlen(ent.name), ent.ino, ent.type))\n            break;\n        ctx->pos = host_telldir(dir) + 1;\n    }\n    return res;\n}\n\nstatic int fakefs_dir_release(struct inode *ino, struct file *file) {\n    if (FILE_DIR(file) != NULL)\n        return host_closedir(FILE_DIR(file));\n    return 0;\n}\n\nstatic const struct file_operations fakefs_file_fops = {\n    .llseek = generic_file_llseek,\n    .splice_read = generic_file_splice_read,\n    .read_iter = generic_file_read_iter,\n    .write_iter = generic_file_write_iter,\n    .mmap = generic_file_mmap,\n    .release = fakefs_file_release,\n    .fsync = fakefs_fsync,\n};\n\nstatic const struct file_operations fakefs_dir_fops = {\n    .iterate = fakefs_iterate,\n    .release = fakefs_dir_release,\n};\n\n/***** address space *****/\n\nstatic int fakefs_readpage(struct file *file, struct page *page) {\n    struct inode *inode = file ? file->f_inode : page->mapping->host;\n    char *buffer = kmap(page);\n    ssize_t res = host_pread(INODE_FD(inode), buffer, PAGE_SIZE, page_offset(page));\n    if (res < 0) {\n        ClearPageUptodate(page);\n        SetPageError(page);\n        goto out;\n    }\n    memset(buffer + res, 0, PAGE_SIZE - res);\n\n    res = 0;\n    ClearPageError(page);\n    SetPageUptodate(page);\n\nout:\n    flush_dcache_page(page);\n    kunmap(page);\n    unlock_page(page);\n    return res;\n}\n\nstatic int fakefs_writepage(struct page *page, struct writeback_control *wbc) {\n    struct inode *inode = page->mapping->host;\n\n    loff_t start = page_offset(page);\n    int len = PAGE_SIZE;\n    if (page->index > (inode->i_size >> PAGE_SHIFT))\n        len = inode->i_size & (PAGE_SIZE - 1);\n\n    void *buffer = kmap(page);\n    ssize_t res = host_pwrite(INODE_FD(inode), buffer, len, start);\n    kunmap(page);\n\n    if (res != len) {\n        ClearPageUptodate(page);\n        goto out;\n    }\n\n    if (start + len > inode->i_size)\n        inode->i_size = start + len;\n    ClearPageError(page);\n    res = 0;\n\nout:\n    unlock_page(page);\n    return res;\n}\n\nstatic int fakefs_write_begin(struct file *file, struct address_space *mapping,\n                              loff_t pos, unsigned len, unsigned flags,\n                              struct page **pagep, void **fsdata) {\n    pgoff_t index = pos >> PAGE_SHIFT;\n    *pagep = grab_cache_page_write_begin(mapping, index, flags);\n    if (!*pagep)\n        return -ENOMEM;\n    return 0;\n}\n\n/* copied from hostfs, I don't really know what it does or how it works */\nstatic int fakefs_write_end(struct file *file, struct address_space *mapping,\n                            loff_t pos, unsigned len, unsigned copied,\n                            struct page *page, void *fsdata) {\n    struct inode *inode = mapping->host;\n    unsigned from = pos & (PAGE_SIZE - 1);\n\n    void *buffer = kmap(page);\n    ssize_t res = host_pwrite(INODE_FD(file->f_inode), buffer + from, copied, pos);\n    kunmap(page);\n    if (res < 0)\n        goto out;\n\n    if (!PageUptodate(page) && res == PAGE_SIZE)\n        SetPageUptodate(page);\n\n    pos += res;\n    if (pos > inode->i_size)\n        inode->i_size = pos;\n\nout:\n    unlock_page(page);\n    put_page(page);\n    return res;\n}\n\nstatic const struct address_space_operations fakefs_aops = {\n    .readpage = fakefs_readpage,\n    .writepage = fakefs_writepage,\n    .set_page_dirty = __set_page_dirty_nobuffers,\n    .write_begin = fakefs_write_begin,\n    .write_end = fakefs_write_end,\n};\n\nstatic int restat_inode(struct inode *ino) {\n    struct hostfs_stat host_stat;\n    int err = stat_file(NULL, &host_stat, INODE_FD(ino));\n    if (err < 0)\n        return err;\n    set_nlink(ino, host_stat.nlink);\n    ino->i_size = host_stat.size;\n    ino->i_blocks = host_stat.blocks;\n    ino->i_atime.tv_sec = host_stat.atime.tv_sec;\n    ino->i_atime.tv_nsec = host_stat.atime.tv_nsec;\n    ino->i_ctime.tv_sec = host_stat.ctime.tv_sec;\n    ino->i_ctime.tv_nsec = host_stat.ctime.tv_nsec;\n    ino->i_mtime.tv_sec = host_stat.mtime.tv_sec;\n    ino->i_mtime.tv_nsec = host_stat.mtime.tv_nsec;\n    return 0;\n}\n\nstatic int read_inode(struct inode *ino) {\n    struct fakefs_super *info = ino->i_sb->s_fs_info;\n    struct ish_stat ishstat;\n    inode_read_stat_or_die(&info->db, ino->i_ino, &ishstat);\n    ino->i_mode = ishstat.mode;\n    i_uid_write(ino, ishstat.uid);\n    i_gid_write(ino, ishstat.gid);\n\n    int err = restat_inode(ino);\n    if (err < 0)\n        return err;\n\n    switch (ino->i_mode & S_IFMT) {\n    case S_IFREG:\n        ino->i_op = &fakefs_iops;\n        ino->i_fop = &fakefs_file_fops;\n        ino->i_mapping->a_ops = &fakefs_aops;\n        break;\n    case S_IFDIR:\n        ino->i_op = &fakefs_dir_iops;\n        ino->i_fop = &fakefs_dir_fops;\n        break;\n    case S_IFLNK:\n        ino->i_op = &fakefs_link_iops;\n        ino->i_mapping->a_ops = &fakefs_aops;\n        inode_nohighmem(ino);\n        break;\n    case S_IFCHR:\n    case S_IFBLK:\n    case S_IFIFO:\n    case S_IFSOCK:\n        init_special_inode(ino, ino->i_mode & S_IFMT, ishstat.rdev);\n        ino->i_op = &fakefs_iops;\n        break;\n    default:\n        printk(\"read_inode: unexpected S_IFMT: %o\\n\", ino->i_mode & S_IFMT);\n        return -EIO;\n    }\n    return 0;\n}\n\nstatic const struct dentry_operations fakefs_dops = {\n    .d_delete = always_delete_dentry,\n};\n\n/***** superblock *****/\n\nstatic void fakefs_evict_inode(struct inode *ino) {\n    struct fakefs_super *info = ino->i_sb->s_fs_info;\n    if (INODE_FD(ino) != info->root_fd && INODE_FD(ino) != -1)\n        host_close(INODE_FD(ino));\n    INODE_FD(ino) = -1;\n    truncate_inode_pages_final(&ino->i_data);\n    clear_inode(ino);\n}\n\nstatic int fakefs_statfs(struct dentry *dentry, struct kstatfs *kstat) {\n    struct host_statfs stat;\n    int err = host_fstatfs(INODE_FD(d_inode(dentry)), &stat);\n    if (err < 0)\n        return err;\n    kstat->f_type = 0x66616b65;\n    kstat->f_bsize = stat.bsize;\n    kstat->f_frsize = stat.frsize;\n    kstat->f_blocks = stat.blocks;\n    kstat->f_bfree = stat.bfree;\n    kstat->f_bavail = stat.bavail;\n    kstat->f_files = stat.files;\n    kstat->f_ffree = stat.ffree;\n    kstat->f_fsid = u64_to_fsid(stat.fsid);\n    kstat->f_namelen = stat.namemax;\n    return 0;\n}\n\nstatic const struct super_operations fakefs_super_ops = {\n    .drop_inode = generic_delete_inode,\n    .evict_inode = fakefs_evict_inode,\n    .statfs = fakefs_statfs,\n};\n\nstatic int fakefs_fill_super(struct super_block *sb, struct fs_context *fc) {\n    struct fakefs_super *info = sb->s_fs_info;\n\n    // https://lore.kernel.org/all/d9bcb237-39e1-29b1-9718-b720a7e7540b@collabora.com/T/\n    int err = super_setup_bdi(sb);\n    if (err < 0)\n        return err;\n\n    struct inode *root = new_inode(sb);\n    if (root == NULL)\n        return -ENOMEM;\n    db_begin_read(&info->db);\n    root->i_ino = path_get_inode(&info->db, \"\");\n    if (root->i_ino == 0) {\n        printk(\"fakefs: could not find root inode\\n\");\n        db_rollback(&info->db);\n        iput(root);\n        return -EINVAL;\n    }\n    INODE_FD(root) = info->root_fd;\n    err = read_inode(root);\n    if (err < 0) {\n        db_rollback(&info->db);\n        iput(root);\n        return err;\n    }\n    db_commit(&info->db);\n\n    sb->s_op = &fakefs_super_ops;\n    sb->s_d_op = &fakefs_dops;\n    sb->s_root = d_make_root(root);\n    if (sb->s_root == NULL) {\n        iput(root);\n        return -ENOMEM;\n    }\n\n    return 0;\n}\n\n/***** context/init *****/\n\nstruct fakefs_context {\n    const char *path;\n};\n\nstatic int fakefs_fc_parse_param(struct fs_context *fc, struct fs_parameter *param) {\n    struct fakefs_context *ctx = fc->fs_private;\n    if (strcmp(param->key, \"source\") == 0) {\n        ctx->path = kstrdup(param->string, GFP_KERNEL);\n        if (ctx->path == NULL)\n            return -ENOMEM;\n    }\n    return 0;\n}\n\nstatic void fakefs_fc_free(struct fs_context *fc) {\n    struct fakefs_context *ctx = fc->fs_private;\n    kfree(ctx->path);\n    kfree(ctx);\n}\n\nstatic int fakefs_get_tree(struct fs_context *fc) {\n    fc->s_fs_info = kzalloc(sizeof(struct fakefs_super), GFP_KERNEL);\n    if (fc->s_fs_info == NULL)\n        return -ENOMEM;\n    struct fakefs_super *info = fc->s_fs_info;\n    struct fakefs_context *ctx = fc->fs_private;\n\n    char *path = kmalloc(strlen(ctx->path) + 10, GFP_KERNEL);\n    strcpy(path, ctx->path);\n    strcat(path, \"/data\");\n    info->root_fd = host_open(path, O_RDONLY);\n    if (info->root_fd < 0) {\n        kfree(path);\n        return info->root_fd;\n    }\n\n    strcpy(path, ctx->path);\n    strcat(path, \"/meta.db\");\n    int err = fake_db_init(&info->db, path, info->root_fd);\n    if (err < 0) {\n        kfree(path);\n        return err;\n    }\n    kfree(path);\n\n    err = vfs_get_super(fc, vfs_get_keyed_super, fakefs_fill_super);\n    if (err < 0)\n        return err;\n    return 0;\n}\n\nstatic struct fs_context_operations fakefs_context_ops = {\n    .parse_param = fakefs_fc_parse_param,\n    .free = fakefs_fc_free,\n    .get_tree = fakefs_get_tree,\n};\n\nstatic int fakefs_init_fs_context(struct fs_context *fc) {\n    fc->ops = &fakefs_context_ops;\n    fc->fs_private = kzalloc(sizeof(struct fakefs_context), GFP_KERNEL);\n    if (fc->fs_private == NULL)\n        return -ENOMEM;\n    return 0;\n}\n\nstatic void fakefs_kill_sb(struct super_block *sb) {\n    struct fakefs_super *info = sb->s_fs_info;\n    fake_db_deinit(&info->db);\n    host_close(info->root_fd);\n    kill_anon_super(sb);\n    kfree(info);\n}\n\nstatic struct file_system_type fakefs_type = {\n    .name = \"fakefs\",\n    .init_fs_context = fakefs_init_fs_context,\n    .kill_sb = fakefs_kill_sb,\n};\n\nstatic int fakefs_init(void) {\n    return register_filesystem(&fakefs_type);\n}\n\nfs_initcall(fakefs_init);\n"
  },
  {
    "path": "linux/main.c",
    "content": "#include <linux/compiler_attributes.h>\n#include <linux/start_kernel.h>\n#include <linux/string.h>\n#include <user/user.h>\n\nextern void run_kernel(void);\n\nint main(int argc, const char *argv[])\n{\n\tint i;\n\tfor (i = 1; i < argc; i++) {\n\t\tif (i > 1)\n\t\t\tstrcat(boot_command_line, \" \");\n\t\tstrcat(boot_command_line, argv[i]);\n\t}\n\trun_kernel();\n\tfor (;;) host_pause();\n\treturn 0;\n}\n"
  },
  {
    "path": "main.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"kernel/calls.h\"\n#include \"kernel/task.h\"\n#include \"xX_main_Xx.h\"\n\nint main(int argc, char *const argv[]) {\n    char envp[100] = {0};\n    if (getenv(\"TERM\"))\n        strcpy(envp, getenv(\"TERM\") - strlen(\"TERM\") - 1);\n    int err = xX_main_Xx(argc, argv, envp);\n    if (err < 0) {\n        fprintf(stderr, \"xX_main_Xx: %s\\n\", strerror(-err));\n        return err;\n    }\n    do_mount(&procfs, \"proc\", \"/proc\", \"\", 0);\n    do_mount(&devptsfs, \"devpts\", \"/dev/pts\", \"\", 0);\n    task_run_current();\n}\n"
  },
  {
    "path": "meson.build",
    "content": "project('ish', 'c',\n    default_options: ['default_library=static', 'c_std=gnu11', 'warning_level=2'])\ncc = meson.get_compiler('c')\n\nif cc.get_id() == 'clang'\n    add_project_arguments('-Wimplicit-fallthrough', '-Wtautological-constant-in-range-compare', language: 'c')\nendif\n\nif get_option('b_sanitize').split(',').contains('undefined')\n    add_project_arguments('-fno-sanitize=alignment', language: 'c')\nendif\n\nlog_on = get_option('log').split()\nlog_off = get_option('nolog').split()\nforeach channel : log_on + log_off\n    if log_on.contains(channel)\n        add_project_arguments('-DDEBUG_' + channel + '=1', language: 'c')\n    else\n        add_project_arguments('-DDEBUG_' + channel + '=0', language: 'c')\n    endif\nendforeach\nadd_project_arguments('-DLOG_HANDLER_' + get_option('log_handler').to_upper() + '=1', language: 'c')\nadd_project_arguments('-DENGINE_' + get_option('engine').to_upper() + '=1', language: 'c')\n\nif get_option('no_crlf')\n    add_project_arguments('-DNO_CRLF', language: 'c')\nendif\n\nadd_project_arguments('-Wno-switch', language: 'c')\n\nincludes = [include_directories('.')]\n\nthreads = dependency('threads')\nlibrt = cc.find_library('rt', required: false)\nlibm = cc.find_library('m', required: false)\nlibdl = cc.find_library('dl', required: false)\nsqlite3 = cc.find_library('sqlite3')\ndependencies = [librt, libm, libdl, threads, sqlite3]\n\nsubdir('vdso') # ish depends on the vdso\n\noffsets = custom_target('offsets',\n    output: 'cpu-offsets.h', input: 'asbestos/offsets.c', depfile: 'cpu-offsets.h.d',\n    command: [find_program('tools/staticdefine.sh'), '@OUTDIR@/compile_commands.json', '@INPUT@', '@OUTPUT@', '@DEPFILE@'])\n\nemu_src = [\n    'emu/tlb.c',\n    'emu/fpu.c',\n    'emu/vec.c',\n    'emu/mmx.c',\n    'emu/float80.c',\n\n]\ngadgets = 'asbestos/gadgets-' + host_machine.cpu_family()\nemu_src += [\n    'asbestos/asbestos.c',\n    'asbestos/gen.c',\n    'asbestos/helpers.c',\n    gadgets+'/entry.S',\n    gadgets+'/memory.S',\n    gadgets+'/control.S',\n    gadgets+'/math.S',\n    gadgets+'/bits.S',\n    gadgets+'/string.S',\n    gadgets+'/misc.S',\n    offsets,\n]\n\nlibish_emu = library('ish_emu', emu_src, include_directories: includes)\n\nlibfakefs = library('fakefs',\n    ['fs/fake-db.c', 'fs/fake-migrate.c', 'fs/fake-rebuild.c'],\n    include_directories: includes,\n    dependencies: sqlite3)\n\nsubdir('deps')\n\nif get_option('kernel') == 'ish'\n    if get_option('engine') != 'asbestos'\n        error('Only asbestos is supported with ish kernel')\n    endif\n\n    src = [\n        'kernel/init.c',\n        'kernel/errno.c',\n\n        'kernel/calls.c',\n        'kernel/memory.c',\n        'kernel/user.c',\n        'kernel/vdso.c', vdso,\n        'kernel/task.c',\n        'kernel/group.c',\n        'kernel/log.c',\n\n        'kernel/fork.c',\n        'kernel/exec.c',\n        'kernel/exit.c',\n        'kernel/time.c',\n        'kernel/mmap.c',\n        'kernel/uname.c',\n        'kernel/tls.c',\n        'kernel/futex.c',\n        'kernel/getset.c',\n        'kernel/signal.c',\n        'kernel/resource.c',\n        'kernel/random.c',\n        'kernel/misc.c',\n        'kernel/eventfd.c',\n        'kernel/ipc.c',\n        'kernel/ptrace.c',\n\n        'kernel/fs.c',\n        'kernel/fs_info.c',\n        'fs/mount.c',\n        'fs/fd.c',\n        'fs/inode.c',\n        'fs/stat.c',\n        'fs/dir.c',\n        'fs/generic.c',\n        'fs/path.c',\n        'fs/real.c',\n        'fs/fake.c',\n\n        'fs/proc.c',\n        'fs/proc/entry.c',\n        'fs/proc/ish.c',\n        'fs/proc/root.c',\n        'fs/proc/pid.c',\n\n        'fs/dyndev.c',\n\n        'fs/adhoc.c',\n        'fs/sock.c',\n        'fs/pipe.c',\n        'fs/sockrestart.c',\n        'fs/lock.c',\n\n        'fs/dev.c',\n        'fs/mem.c',\n        'fs/tty.c',\n        'fs/tty-real.c',\n        'fs/pty.c',\n        'fs/tmp.c',\n\n        'fs/poll.c',\n        'kernel/poll.c',\n        'kernel/epoll.c',\n\n        'util/timer.c',\n        'util/sync.c',\n        'util/fifo.c',\n        'util/fchdir.c',\n\n        'platform/' + host_machine.system() + '.c',\n    ]\n\n    libish = library('ish', src,\n        include_directories: includes)\n    ish = declare_dependency(\n        link_with: [libish, libish_emu, libfakefs],\n        dependencies: dependencies,\n        include_directories: includes)\n\n    if not meson.is_cross_build()\n        executable('ish', ['main.c'], dependencies: ish)\n    endif\n\nelif get_option('kernel') == 'linux'\n    kernel_src = [\n        'linux/main.c',\n        'linux/fakefs.c',\n    ]\n    user_src = []\n    emu_deps = []\n\n    if get_option('engine') == 'asbestos'\n        user_src += 'linux/emu_asbestos.c'\n        emu_deps += declare_dependency(link_with: libish_emu)\n    elif get_option('engine') == 'unicorn'\n        user_src += 'linux/emu_unicorn.c'\n        kernel_src += 'linux/emu_unicorn_kernel.c'\n        emu_deps += declare_dependency(\n            dependencies: [cc.find_library('unicorn', dirs: [meson.current_source_dir()+'/deps/unicorn/build'])],\n            include_directories: include_directories('deps/unicorn/include'),\n        )\n    endif\n\n    modules = static_library('linux_modules', kernel_src, dependencies: [linux_headers, sqlite3])\n    user_modules = static_library('linux_user', user_src, dependencies: [user_linux_headers] + emu_deps)\n    executable('ish',\n        build_linux,\n        link_with: [libfakefs],\n        dependencies: [\n            liblinux,\n            libm,\n            libdl,\n            threads,\n            declare_dependency(link_whole: [modules, user_modules]),\n        ] + emu_deps)\nendif\n\nsubdir('tools')\n\ngdb_scripts = ['ish-gdb.gdb']\nforeach script : gdb_scripts\n    custom_target(script,\n        output: script, input: script,\n        command: ['ln', '-sf', '@INPUT@', '@OUTPUT@'],\n        build_by_default: true)\nendforeach\n\nif not meson.is_cross_build()\n    # test for floating point library\n    float80_test = executable('float80_test', ['emu/float80.c', 'emu/float80-test.c'], dependencies: [libm])\n    test('float80', float80_test)\nendif\n\ne2e_test = find_program('tests/e2e/e2e.bash')\ntest('e2e', e2e_test, args: ['-y'], timeout: 180)\n"
  },
  {
    "path": "meson_options.txt",
    "content": "option('log', type: 'string', value: '')\noption('nolog', type: 'string', value: '')\noption('log_handler', type: 'string', value: 'dprintf')\n\noption('engine', type: 'combo', choices: ['asbestos', 'unicorn'], value: 'asbestos')\noption('kernel', type: 'combo', choices: ['ish', 'linux'], value: 'ish')\noption('kconfig', type: 'array', value: [])\n\noption('vdso_c_args', type: 'string', value: '')\n\noption('no_crlf', type: 'boolean', value: false)\n"
  },
  {
    "path": "misc.h",
    "content": "#ifndef MISC_H\n#define MISC_H\n\n#ifdef __KERNEL__\n#include <linux/types.h>\n#else\n#include <assert.h>\n#include <sys/types.h>\n#include <stdnoreturn.h>\n#include <stdbool.h>\n#include <stdint.h>\n#endif\n\n// utility macros\n#define glue(a, b) _glue(a, b)\n#define _glue(a, b) a##b\n#define glue3(a,b,c) glue(a, glue(b, c))\n#define glue4(a,b,c,d) glue(a, glue3(b, c, d))\n\n#define str(x) _str(x)\n#define _str(x) #x\n\n// compiler check\n#define is_gcc(version) (__GNUC__ >= version)\n\n#if !defined(__has_attribute)\n#define has_attribute(x) 0\n#else\n#define has_attribute __has_attribute\n#endif\n\n#if !defined(__has_feature)\n#define has_feature(x) 0\n#else\n#define has_feature __has_feature\n#endif\n\n// keywords\n#define bitfield unsigned int\n#define forceinline inline __attribute__((always_inline))\n#if defined(NDEBUG) || defined(__KERNEL__)\n#define posit __builtin_assume\n#else\n#define posit assert\n#endif\n#define must_check __attribute__((warn_unused_result))\n\n#ifndef __KERNEL__\n#define unlikely(x) __builtin_expect((x), 0)\n#define typecheck(type, x) ({type _x = x; x;})\n#define container_of(ptr, type, member) \\\n    ((type *) ((char *) (ptr) - offsetof(type, member)))\n#if has_attribute(fallthrough)\n#define fallthrough __attribute__((fallthrough))\n#else\n#define fallthrough\n#endif\n#endif\n\n#if has_attribute(no_sanitize)\n#define __no_instrument_msan\n#if defined(__has_feature)\n#if has_feature(memory_sanitizer)\n#undef __no_instrument_msan\n#define __no_instrument_msan __attribute__((no_sanitize(\"memory\"))\n#endif\n#endif\n#define __no_instrument __attribute__((no_sanitize(\"address\", \"thread\", \"undefined\", \"leak\"))) __no_instrument_msan\n#else\n#define __no_instrument\n#endif\n\n#if has_attribute(nonstring)\n#define __strncpy_safe __attribute__((nonstring))\n#else\n#define __strncpy_safe\n#endif\n\n#define zero_init(type) ((type[1]){}[0])\n#define pun(type, x) (((union {typeof(x) _; type a;}) (x)).a)\n\n#define UNUSED(x) UNUSED_##x __attribute__((unused))\nstatic inline void __use(int dummy __attribute__((unused)), ...) {}\n#define use(...) __use(0, ##__VA_ARGS__)\n\n#if defined(__x86_64__)\n#define rdtsc() ({ \\\n        uint32_t low, high; \\\n        __asm__ volatile(\"rdtsc\" : \"=a\" (high), \"=d\" (low)); \\\n        ((uint64_t) high) << 32 | low; \\\n    })\n#elif defined(__arm64__) || defined(__aarch64__)\n#define rdtsc() ({ \\\n        uint64_t tsc; \\\n        __asm__ volatile(\"mrs %0, PMCCNTR_EL0\" : \"=r\" (tsc)); \\\n        tsc; \\\n    })\n#endif\n\n#ifndef __KERNEL__\n#define array_size(arr) (sizeof(arr)/sizeof((arr)[0]))\n#endif\n\n// types\ntypedef int64_t sqword_t;\ntypedef uint64_t qword_t;\ntypedef uint32_t dword_t;\ntypedef int32_t sdword_t;\ntypedef uint16_t word_t;\ntypedef uint8_t byte_t;\n\ntypedef dword_t addr_t;\ntypedef dword_t uint_t;\ntypedef sdword_t int_t;\n\ntypedef sdword_t pid_t_;\ntypedef dword_t uid_t_;\ntypedef word_t mode_t_;\ntypedef sqword_t off_t_;\ntypedef dword_t time_t_;\ntypedef dword_t clock_t_;\n\n#define uint(size) glue3(uint,size,_t)\n#define sint(size) glue3(int,size,_t)\n\n#ifndef __KERNEL__\n#define ERR_PTR(err) (void *) (intptr_t) (err)\n#define PTR_ERR(ptr) (intptr_t) (ptr)\n#define IS_ERR(ptr) ((uintptr_t) (ptr) > (uintptr_t) -0xfff)\n#endif\n\n#endif\n"
  },
  {
    "path": "platform/darwin.c",
    "content": "#include <mach/mach.h>\n#include <sys/sysctl.h>\n#include <sys/time.h>\n#include \"platform/platform.h\"\n\nstruct cpu_usage get_cpu_usage() {\n    host_cpu_load_info_data_t load;\n    mach_msg_type_number_t fuck = HOST_CPU_LOAD_INFO_COUNT;\n    host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t) &load, &fuck);\n    struct cpu_usage usage;\n    usage.user_ticks = load.cpu_ticks[CPU_STATE_USER];\n    usage.system_ticks = load.cpu_ticks[CPU_STATE_SYSTEM];\n    usage.idle_ticks = load.cpu_ticks[CPU_STATE_IDLE];\n    usage.nice_ticks = load.cpu_ticks[CPU_STATE_NICE];\n    return usage;\n}\n\nstruct mem_usage get_mem_usage() {\n    host_basic_info_data_t basic = {};\n    mach_msg_type_number_t fuck = HOST_BASIC_INFO_COUNT;\n    kern_return_t status = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) &basic, &fuck);\n    assert(status == KERN_SUCCESS);\n    vm_statistics64_data_t vm = {};\n    fuck = HOST_VM_INFO64_COUNT;\n    status = host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t) &vm, &fuck);\n    assert(status == KERN_SUCCESS);\n\n    struct mem_usage usage;\n    usage.total = basic.max_mem;\n    usage.free = vm.free_count * vm_page_size;\n    usage.active = vm.active_count * vm_page_size;\n    usage.inactive = vm.inactive_count * vm_page_size;\n    return usage;\n}\n\nstruct uptime_info get_uptime() {\n    uint64_t kern_boottime[2];\n    size_t size = sizeof(kern_boottime);\n    sysctlbyname(\"kern.boottime\", &kern_boottime, &size, NULL, 0);\n    struct timeval now;\n    gettimeofday(&now, NULL);\n\n    struct {\n        uint32_t ldavg[3];\n        long scale;\n    } vm_loadavg;\n    size = sizeof(vm_loadavg);\n    sysctlbyname(\"vm.loadavg\", &vm_loadavg, &size, NULL, 0);\n\n    // linux wants the scale to be 16 bits\n    for (int i = 0; i < 3; i++) {\n        if (FSHIFT < 16)\n            vm_loadavg.ldavg[i] <<= 16 - FSHIFT;\n        else\n            vm_loadavg.ldavg[i] >>= FSHIFT - 16;\n    }\n\n    struct uptime_info uptime = {\n        .uptime_ticks = now.tv_sec - kern_boottime[0],\n        .load_1m = vm_loadavg.ldavg[0],\n        .load_5m = vm_loadavg.ldavg[1],\n        .load_15m = vm_loadavg.ldavg[2],\n    };\n    return uptime;\n}\n"
  },
  {
    "path": "platform/linux.c",
    "content": "#include <sys/sysinfo.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <string.h>\n#include \"platform/platform.h\"\n#include \"debug.h\"\n\nstatic void read_proc_line(const char *file, const char *name, char *buf) {\n    FILE *f = fopen(file, \"r\");\n    if (f == NULL) ERRNO_DIE(file);\n    do {\n        fgets(buf, 1234, f);\n        if (feof(f))\n            die(\"could not find proc line %s\", name);\n    } while (!(strncmp(name, buf, strlen(name)) == 0 && buf[strlen(name)] == ' '));\n    fclose(f);\n}\n\nstruct cpu_usage get_cpu_usage() {\n    struct cpu_usage usage = {};\n    char buf[1234];\n    read_proc_line(\"/proc/stat\", \"cpu\", buf);\n    sscanf(buf, \"cpu %\"SCNu64\" %\"SCNu64\" %\"SCNu64\" %\"SCNu64\"\\n\", &usage.user_ticks, &usage.system_ticks, &usage.idle_ticks, &usage.nice_ticks);\n    return usage;\n}\n\nstruct mem_usage get_mem_usage() {\n    struct mem_usage usage;\n    char buf[1234];\n\n    read_proc_line(\"/proc/meminfo\", \"MemTotal:\", buf);\n    sscanf(buf, \"MemTotal: %\"PRIu64\" kB\\n\", &usage.total);\n    read_proc_line(\"/proc/meminfo\", \"MemFree:\", buf);\n    sscanf(buf, \"MemFree: %\"PRIu64\" kB\\n\", &usage.free);\n    read_proc_line(\"/proc/meminfo\", \"Active:\", buf);\n    sscanf(buf, \"Active: %\"PRIu64\" kB\\n\", &usage.active);\n    read_proc_line(\"/proc/meminfo\", \"Inactive:\", buf);\n    sscanf(buf, \"Inactive: %\"PRIu64\" kB\\n\", &usage.inactive);\n\n    return usage;\n}\n\nstruct uptime_info get_uptime() {\n    struct sysinfo info;\n    sysinfo(&info);\n    struct uptime_info uptime = {\n        .uptime_ticks = info.uptime,\n        .load_1m = info.loads[0],\n        .load_5m = info.loads[1],\n        .load_15m = info.loads[2],\n    };\n    return uptime;\n}\n"
  },
  {
    "path": "platform/platform.h",
    "content": "#ifndef PLATFORM_H\n#define PLATFORM_H\n#include \"misc.h\"\n\n// for some reason a tick is always 10ms\nstruct cpu_usage {\n    uint64_t user_ticks;\n    uint64_t system_ticks;\n    uint64_t idle_ticks;\n    uint64_t nice_ticks;\n};\nstruct cpu_usage get_cpu_usage(void);\n\nstruct mem_usage {\n    uint64_t total;\n    uint64_t free;\n    uint64_t active;\n    uint64_t inactive;\n};\nstruct mem_usage get_mem_usage(void);\n\nstruct uptime_info {\n    uint64_t uptime_ticks;\n    uint64_t load_1m, load_5m, load_15m;\n};\nstruct uptime_info get_uptime(void);\n\n#endif\n"
  },
  {
    "path": "shell.nix",
    "content": "{ pkgs ? import <nixpkgs> {} }:\npkgs.mkShell {\n\tnativeBuildInputs = [\n\t\tpkgs.clang\n\t\tpkgs.libarchive\n\t\tpkgs.lld\n\t\tpkgs.meson\n\t\tpkgs.ninja\n\t\tpkgs.sqlite\n\t];\n}\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "busybox-*/\n"
  },
  {
    "path": "tests/e2e/e2e.bash",
    "content": "#!/bin/bash\ncd \"$(dirname \"$0\")\"\ncd ../../\nmkdir -p e2e_out\n\nFS=./e2e_out/testfs\nVERBOSE=false\nYES_MODE=false\nTEST_PATTERN=\"\"\nSUMMARY_LOG=./e2e_out/summary.txt\nALPINE_IMAGE=\"http://dl-cdn.alpinelinux.org/alpine/v3.11/releases/x86/alpine-minirootfs-3.11.2-x86.tar.gz\"\nrm -f \"$SUMMARY_LOG\"\n\nwhile getopts \"hvyf:e:\" OPTION; do\n    case $OPTION in\n        v)\n            VERBOSE=true\n            ;;\n        f)\n            if [ -d \"$OPTARG\" ]; then\n                FS=\"$OPTARG\"\n            else\n                echo \"No such directory $OPTARG\"\n            fi\n            ;;\n        e)\n            TEST_PATTERN=\"$OPTARG\"\n            ;;\n        y)\n            YES_MODE=true\n            ;;\n        h)\n            echo \"iSH E2E Testing\"\n            echo \"===============\"\n            echo \"Automates e2e tests defined in tests/e2e.\"\n            echo \"\"\n            echo \"  -h        Display help text.\"\n            echo \"  -v        Verbosely show test output as it is received.\"\n            echo \"  -f fs     Use \\\"fs\\\" as the -f option for iSH. Default \\\"alpine\\\".\"\n            echo \"  -e pat    Use pattern \\\"pat\\\" to pattern match tests to run.\"\n            echo \"            Syntax is same as grep -E for local system.\"\n            echo \"  -y        Accept creation of test file system if it does not exist\"\n            echo \"\"\n            echo \"Example: Run the \\\"hello\\\" test in the alpine2 fake file system.\"\n            echo \"$ bash e2e.bash -f alpine2 -e ^hello$\"\n            exit 0\n            ;;\n    esac\ndone\n\nISH=\"./build/ish -f $FS\"\n\nif [ ! -d \"$FS\" ]; then\n    if [ \"$YES_MODE\" = \"true\" ]; then\n        INSTALL=\"Yes\";\n    else\n        echo \"File System \\\"$FS\\\" does not exist. Automatically set up now?\"\n        select yn in \"Yes\" \"No\"; do\n            INSTALL=\"$yn\";\n            break;\n        done\n    fi\n    echo install? $INSTALL\n    case \"$INSTALL\" in\n        Yes)\n            echo\n            echo \"### Setting up test file system...\"\n            echo \"###### Downloading Alpine\"\n            wget \"$ALPINE_IMAGE\" -O e2e_out/alpine.tar.gz\n            echo \"###### Unpacking Alpine\"\n            ./build/tools/fakefsify e2e_out/alpine.tar.gz \"$FS\"\n            echo \"###### Configuring iSH and installing base libraries\"\n            grep -E \"^nameserver\" /etc/resolv.conf | head -1 | $ISH /bin/sed -n \"w /etc/resolv.conf\"\n            $ISH /bin/sh -c \"apk update && apk add build-base python2 python3\"\n            ;;\n        No) exit 1;;\n    esac\nfi\n\nNUM_TOTAL=0\nNUM_FAILS=0\n\ntest_setup() {\n    $ISH /bin/rm -rf /tmp/e2e/$1\n    $ISH /bin/mkdir -p /tmp/e2e/$1\n    rm -rf ./e2e_out/$1\n    mkdir -p ./e2e_out/$1\n    tar -cf - -C tests/e2e $1 | $ISH /bin/tar xf - -C /tmp/e2e\n}\n\ntest_teardown() {\n    # Yes, this can overwrite actual.txt. This is a feature I guess. /shrug\n    $ISH /bin/tar -cf - -C /tmp/e2e $1 | tar -xf - -C ./e2e_out\n}\n\nrun_test() {\n    ACTUAL_LOG=\"e2e_out/$1/actual.txt\"\n    if [ \"$VERBOSE\" = \"true\" ]; then\n        $ISH /usr/bin/env sh -c \"source /etc/profile && cd /tmp/e2e/$1 && sh test.sh\" 2>&1 | tee -a \"$SUMMARY_LOG\" | tee \"$ACTUAL_LOG\"\n    else\n        $ISH /usr/bin/env sh -c \"source /etc/profile && cd /tmp/e2e/$1 && sh test.sh\" 2>&1 | tee -a \"$SUMMARY_LOG\" > \"$ACTUAL_LOG\"\n    fi\n}\n\nvalidate_test() {\n    EXPECTED_LOG=\"tests/e2e/$1/expected.txt\"\n    ACTUAL_LOG=\"e2e_out/$1/actual.txt\"\n    DIFF_LOG=\"e2e_out/$1/diff.txt\"\n    diff -uw \"$EXPECTED_LOG\" \"$ACTUAL_LOG\" > $DIFF_LOG\n    DIFF_EXIT_CODE=$?\n    case \"$DIFF_EXIT_CODE\" in\n        0)\n            # :)\n            ;;\n        1)\n            NUM_FAILS=$((NUM_FAILS+1))\n            echo \"!!! Failed: $1\" | tee -a \"$SUMMARY_LOG\"\n            cat \"$DIFF_LOG\" | tee -a \"$SUMMARY_LOG\"\n            ;;\n        2)\n            NUM_FAILS=$((NUM_FAILS+1))\n            echo \"Something went wrong when trying to validate $1.\" | tee -a \"$SUMMARY_LOG\"\n            ;;\n    esac\n}\n\nfor e2e_test in $(ls tests/e2e | grep -E \"^[a-zA-Z0-9_]+$\" | grep -E \"$TEST_PATTERN\"); do\n    if [ -d \"tests/e2e/$e2e_test\" ]; then\n        let \"NUM_TOTAL+=1\"\n        printf \"### Running Test: \"%-16s\" ###\\n\" \"$e2e_test\" | tee -a \"$SUMMARY_LOG\"\n        test_setup $e2e_test\n        run_test $e2e_test\n        test_teardown $e2e_test\n        validate_test $e2e_test\n    else\n        echo $e2e_test is not a directory! Skipping... | tee -a \"$SUMMARY_LOG\"\n    fi\ndone\n\nif [ \"$NUM_TOTAL\" -eq 0 ]; then\n    printf \"### No tests were run\" | tee -a \"$SUMMARY_LOG\" \n    exit 0\nfi\n\nNUM_PASSES=$((NUM_TOTAL-NUM_FAILS))\nPERCENT_PASSES=$(echo \"scale=2; 100 * $NUM_PASSES / $NUM_TOTAL\" | bc)\nPASSED_STR=\"$NUM_PASSES/$NUM_TOTAL ($PERCENT_PASSES%)\"\nprintf \"### Passed: %-22s ###\\n\" \"$PASSED_STR\" | tee -a \"$SUMMARY_LOG\"\n\nif [ \"$NUM_FAILS\" -eq 0 ]; then\n    exit 0\nelse\n    exit 1\nfi\n"
  },
  {
    "path": "tests/e2e/fpu/expected.txt",
    "content": "16.12\n-15.88\n1.92\n0.01\n133.33\n16.13\n0.01\n"
  },
  {
    "path": "tests/e2e/fpu/test.sh",
    "content": "#!/bin/sh\ngcc test_fpu.c -o ./test_fpu\n./test_fpu\n"
  },
  {
    "path": "tests/e2e/fpu/test_fpu.c",
    "content": "#include <stdio.h>\n\nint main(void) {\n    double a = 0.12;\n    double b = 16.0;\n    printf(\"%.2f\\n\", a + b);\n    printf(\"%.2f\\n\", a - b);\n    printf(\"%.2f\\n\", a * b);\n    printf(\"%.2f\\n\", a / b);\n    printf(\"%.2f\\n\", b / a);\n    printf(\"%.2f\\n\", a + a / b + b);\n    printf(\"%.2f\\n\", (a + a) / (b + b));\n    return 0;\n}\n\n"
  },
  {
    "path": "tests/e2e/hello/expected.txt",
    "content": "Hello, sh!\nHello, Python 2!\nHello, Python 3!\nHello, C!\n"
  },
  {
    "path": "tests/e2e/hello/test.sh",
    "content": "#!/bin/sh\necho Hello, sh!\n\npython test_python2.py\npython3 test_python3.py\n\ngcc test_c.c -o ./hello_c\n./hello_c\n"
  },
  {
    "path": "tests/e2e/hello/test_c.c",
    "content": "#include <stdio.h>\n\nint main(void) {\n    printf(\"Hello, C!\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "tests/e2e/hello/test_python2.py",
    "content": "print 'Hello, Python 2!'\n"
  },
  {
    "path": "tests/e2e/hello/test_python3.py",
    "content": "print('Hello, Python 3!')\n"
  },
  {
    "path": "tests/e2e/qemu/expected.txt",
    "content": "addl       A=12345678 B=0812fada R=1a475152 CCIN=0000 CC=0010\naddw       A=12345678 B=0812fada R=12345152 CCIN=0000 CC=0011\naddb       A=12345678 B=0812fada R=12345652 CCIN=0000 CC=0011\naddl       A=00012341 B=00012341 R=00024682 CCIN=0000 CC=0004\naddw       A=00012341 B=00012341 R=00014682 CCIN=0000 CC=0004\naddb       A=00012341 B=00012341 R=00012382 CCIN=0000 CC=0884\naddl       A=00012341 B=fffedcbf R=00000000 CCIN=0000 CC=0055\naddw       A=00012341 B=fffedcbf R=00010000 CCIN=0000 CC=0055\naddb       A=00012341 B=fffedcbf R=00012300 CCIN=0000 CC=0055\naddl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\naddw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\naddb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\naddl       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\naddw       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\naddb       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\naddl       A=ffffffff B=00000001 R=00000000 CCIN=0000 CC=0055\naddw       A=ffffffff B=00000001 R=ffff0000 CCIN=0000 CC=0055\naddb       A=ffffffff B=00000001 R=ffffff00 CCIN=0000 CC=0055\naddl       A=ffffffff B=00000002 R=00000001 CCIN=0000 CC=0011\naddw       A=ffffffff B=00000002 R=ffff0001 CCIN=0000 CC=0011\naddb       A=ffffffff B=00000002 R=ffffff01 CCIN=0000 CC=0011\naddl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\naddw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\naddb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\naddl       A=7fffffff B=00000001 R=80000000 CCIN=0000 CC=0894\naddw       A=7fffffff B=00000001 R=7fff0000 CCIN=0000 CC=0055\naddb       A=7fffffff B=00000001 R=7fffff00 CCIN=0000 CC=0055\naddl       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0011\naddw       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0091\naddb       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0091\naddl       A=80000000 B=ffffffff R=7fffffff CCIN=0000 CC=0805\naddw       A=80000000 B=ffffffff R=8000ffff CCIN=0000 CC=0084\naddb       A=80000000 B=ffffffff R=800000ff CCIN=0000 CC=0084\naddl       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0080\naddw       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\naddb       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\naddl       A=80000000 B=fffffffe R=7ffffffe CCIN=0000 CC=0801\naddw       A=80000000 B=fffffffe R=8000fffe CCIN=0000 CC=0080\naddb       A=80000000 B=fffffffe R=800000fe CCIN=0000 CC=0080\naddl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\naddw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\naddb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\naddl       A=12347fff B=00000001 R=12348000 CCIN=0000 CC=0014\naddw       A=12347fff B=00000001 R=12348000 CCIN=0000 CC=0894\naddb       A=12347fff B=00000001 R=12347f00 CCIN=0000 CC=0055\naddl       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0011\naddw       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0011\naddb       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0091\naddl       A=12348000 B=ffffffff R=12347fff CCIN=0000 CC=0005\naddw       A=12348000 B=ffffffff R=12347fff CCIN=0000 CC=0805\naddb       A=12348000 B=ffffffff R=123480ff CCIN=0000 CC=0084\naddl       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\naddw       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0080\naddb       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\naddl       A=12348000 B=fffffffe R=12347ffe CCIN=0000 CC=0001\naddw       A=12348000 B=fffffffe R=12347ffe CCIN=0000 CC=0801\naddb       A=12348000 B=fffffffe R=123480fe CCIN=0000 CC=0080\naddl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\naddw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\naddb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\naddl       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0010\naddw       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0010\naddb       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0890\naddl       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\naddw       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\naddb       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\naddl       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0001\naddw       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0081\naddb       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0801\naddl       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0004\naddw       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\naddb       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\naddl       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0005\naddw       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0085\naddb       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0805\nsubl       A=12345678 B=0812fada R=0a215b9e CCIN=0000 CC=0010\nsubw       A=12345678 B=0812fada R=12345b9e CCIN=0000 CC=0011\nsubb       A=12345678 B=0812fada R=1234569e CCIN=0000 CC=0891\nsubl       A=00012341 B=00012341 R=00000000 CCIN=0000 CC=0044\nsubw       A=00012341 B=00012341 R=00010000 CCIN=0000 CC=0044\nsubb       A=00012341 B=00012341 R=00012300 CCIN=0000 CC=0044\nsubl       A=00012341 B=fffedcbf R=00024682 CCIN=0000 CC=0015\nsubw       A=00012341 B=fffedcbf R=00014682 CCIN=0000 CC=0015\nsubb       A=00012341 B=fffedcbf R=00012382 CCIN=0000 CC=0895\nsubl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsubw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsubb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsubl       A=ffffffff B=ffffffff R=00000000 CCIN=0000 CC=0044\nsubw       A=ffffffff B=ffffffff R=ffff0000 CCIN=0000 CC=0044\nsubb       A=ffffffff B=ffffffff R=ffffff00 CCIN=0000 CC=0044\nsubl       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsubw       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsubb       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsubl       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsubw       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsubb       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsubl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\nsubw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nsubb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nsubl       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0000\nsubw       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nsubb       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nsubl       A=7fffffff B=ffffffff R=80000000 CCIN=0000 CC=0885\nsubw       A=7fffffff B=ffffffff R=7fff0000 CCIN=0000 CC=0044\nsubb       A=7fffffff B=ffffffff R=7fffff00 CCIN=0000 CC=0044\nsubl       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0091\nsubw       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0011\nsubb       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0011\nsubl       A=80000000 B=00000001 R=7fffffff CCIN=0000 CC=0814\nsubw       A=80000000 B=00000001 R=8000ffff CCIN=0000 CC=0095\nsubb       A=80000000 B=00000001 R=800000ff CCIN=0000 CC=0095\nsubl       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0091\nsubw       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0011\nsubb       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0011\nsubl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nsubw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nsubb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\nsubl       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nsubw       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nsubb       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0080\nsubl       A=12347fff B=ffffffff R=12348000 CCIN=0000 CC=0005\nsubw       A=12347fff B=ffffffff R=12348000 CCIN=0000 CC=0885\nsubb       A=12347fff B=ffffffff R=12347f00 CCIN=0000 CC=0044\nsubl       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0011\nsubw       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0091\nsubb       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0011\nsubl       A=12348000 B=00000001 R=12347fff CCIN=0000 CC=0014\nsubw       A=12348000 B=00000001 R=12347fff CCIN=0000 CC=0814\nsubb       A=12348000 B=00000001 R=123480ff CCIN=0000 CC=0095\nsubl       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0011\nsubw       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0091\nsubb       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0011\nsubl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsubw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsubb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsubl       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsubw       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsubb       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsubl       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0001\nsubw       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0001\nsubb       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0881\nsubl       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0015\nsubw       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0095\nsubb       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0095\nsubl       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0010\nsubw       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0090\nsubb       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0810\nsubl       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0015\nsubw       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0095\nsubb       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0095\nxorl       A=12345678 B=0812fada R=1a26aca2 CCIN=0000 CC=0000\nxorw       A=12345678 B=0812fada R=1234aca2 CCIN=0000 CC=0080\nxorb       A=12345678 B=0812fada R=123456a2 CCIN=0000 CC=0080\nxorl       A=00012341 B=00012341 R=00000000 CCIN=0000 CC=0044\nxorw       A=00012341 B=00012341 R=00010000 CCIN=0000 CC=0044\nxorb       A=00012341 B=00012341 R=00012300 CCIN=0000 CC=0044\nxorl       A=00012341 B=fffedcbf R=fffffffe CCIN=0000 CC=0080\nxorw       A=00012341 B=fffedcbf R=0001fffe CCIN=0000 CC=0080\nxorb       A=00012341 B=fffedcbf R=000123fe CCIN=0000 CC=0080\nxorl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nxorw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nxorb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nxorl       A=ffffffff B=ffffffff R=00000000 CCIN=0000 CC=0044\nxorw       A=ffffffff B=ffffffff R=ffff0000 CCIN=0000 CC=0044\nxorb       A=ffffffff B=ffffffff R=ffffff00 CCIN=0000 CC=0044\nxorl       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nxorw       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nxorb       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nxorl       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nxorw       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nxorb       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nxorl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\nxorw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nxorb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nxorl       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0000\nxorw       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nxorb       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nxorl       A=7fffffff B=ffffffff R=80000000 CCIN=0000 CC=0084\nxorw       A=7fffffff B=ffffffff R=7fff0000 CCIN=0000 CC=0044\nxorb       A=7fffffff B=ffffffff R=7fffff00 CCIN=0000 CC=0044\nxorl       A=80000000 B=ffffffff R=7fffffff CCIN=0000 CC=0004\nxorw       A=80000000 B=ffffffff R=8000ffff CCIN=0000 CC=0084\nxorb       A=80000000 B=ffffffff R=800000ff CCIN=0000 CC=0084\nxorl       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0080\nxorw       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\nxorb       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\nxorl       A=80000000 B=fffffffe R=7ffffffe CCIN=0000 CC=0000\nxorw       A=80000000 B=fffffffe R=8000fffe CCIN=0000 CC=0080\nxorb       A=80000000 B=fffffffe R=800000fe CCIN=0000 CC=0080\nxorl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nxorw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nxorb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\nxorl       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nxorw       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nxorb       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0080\nxorl       A=12347fff B=ffffffff R=edcb8000 CCIN=0000 CC=0084\nxorw       A=12347fff B=ffffffff R=12348000 CCIN=0000 CC=0084\nxorb       A=12347fff B=ffffffff R=12347f00 CCIN=0000 CC=0044\nxorl       A=12348000 B=ffffffff R=edcb7fff CCIN=0000 CC=0084\nxorw       A=12348000 B=ffffffff R=12347fff CCIN=0000 CC=0004\nxorb       A=12348000 B=ffffffff R=123480ff CCIN=0000 CC=0084\nxorl       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\nxorw       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0080\nxorb       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\nxorl       A=12348000 B=fffffffe R=edcb7ffe CCIN=0000 CC=0080\nxorw       A=12348000 B=fffffffe R=12347ffe CCIN=0000 CC=0000\nxorb       A=12348000 B=fffffffe R=123480fe CCIN=0000 CC=0080\nxorl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nxorw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nxorb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nxorl       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nxorw       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nxorb       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nxorl       A=12347f7f B=ffffffff R=edcb8080 CCIN=0000 CC=0080\nxorw       A=12347f7f B=ffffffff R=12348080 CCIN=0000 CC=0080\nxorb       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0080\nxorl       A=12348080 B=ffffffff R=edcb7f7f CCIN=0000 CC=0080\nxorw       A=12348080 B=ffffffff R=12347f7f CCIN=0000 CC=0000\nxorb       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0000\nxorl       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0004\nxorw       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\nxorb       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\nxorl       A=12348080 B=fffffffe R=edcb7f7e CCIN=0000 CC=0084\nxorw       A=12348080 B=fffffffe R=12347f7e CCIN=0000 CC=0004\nxorb       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0004\nandl       A=12345678 B=0812fada R=00105258 CCIN=0000 CC=0000\nandw       A=12345678 B=0812fada R=12345258 CCIN=0000 CC=0000\nandb       A=12345678 B=0812fada R=12345658 CCIN=0000 CC=0000\nandl       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\nandw       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\nandb       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\nandl       A=00012341 B=fffedcbf R=00000001 CCIN=0000 CC=0000\nandw       A=00012341 B=fffedcbf R=00010001 CCIN=0000 CC=0000\nandb       A=00012341 B=fffedcbf R=00012301 CCIN=0000 CC=0000\nandl       A=ffffffff B=00000000 R=00000000 CCIN=0000 CC=0044\nandw       A=ffffffff B=00000000 R=ffff0000 CCIN=0000 CC=0044\nandb       A=ffffffff B=00000000 R=ffffff00 CCIN=0000 CC=0044\nandl       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\nandw       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\nandb       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\nandl       A=ffffffff B=00000001 R=00000001 CCIN=0000 CC=0000\nandw       A=ffffffff B=00000001 R=ffff0001 CCIN=0000 CC=0000\nandb       A=ffffffff B=00000001 R=ffffff01 CCIN=0000 CC=0000\nandl       A=ffffffff B=00000002 R=00000002 CCIN=0000 CC=0000\nandw       A=ffffffff B=00000002 R=ffff0002 CCIN=0000 CC=0000\nandb       A=ffffffff B=00000002 R=ffffff02 CCIN=0000 CC=0000\nandl       A=7fffffff B=00000000 R=00000000 CCIN=0000 CC=0044\nandw       A=7fffffff B=00000000 R=7fff0000 CCIN=0000 CC=0044\nandb       A=7fffffff B=00000000 R=7fffff00 CCIN=0000 CC=0044\nandl       A=7fffffff B=00000001 R=00000001 CCIN=0000 CC=0000\nandw       A=7fffffff B=00000001 R=7fff0001 CCIN=0000 CC=0000\nandb       A=7fffffff B=00000001 R=7fffff01 CCIN=0000 CC=0000\nandl       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0004\nandw       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0084\nandb       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0084\nandl       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0084\nandw       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0044\nandb       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0044\nandl       A=80000000 B=00000001 R=00000000 CCIN=0000 CC=0044\nandw       A=80000000 B=00000001 R=80000000 CCIN=0000 CC=0044\nandb       A=80000000 B=00000001 R=80000000 CCIN=0000 CC=0044\nandl       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0084\nandw       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0044\nandb       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0044\nandl       A=12347fff B=00000000 R=00000000 CCIN=0000 CC=0044\nandw       A=12347fff B=00000000 R=12340000 CCIN=0000 CC=0044\nandb       A=12347fff B=00000000 R=12347f00 CCIN=0000 CC=0044\nandl       A=12347fff B=00000001 R=00000001 CCIN=0000 CC=0000\nandw       A=12347fff B=00000001 R=12340001 CCIN=0000 CC=0000\nandb       A=12347fff B=00000001 R=12347f01 CCIN=0000 CC=0000\nandl       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0004\nandw       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0004\nandb       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0084\nandl       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0004\nandw       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0084\nandb       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0044\nandl       A=12348000 B=00000001 R=00000000 CCIN=0000 CC=0044\nandw       A=12348000 B=00000001 R=12340000 CCIN=0000 CC=0044\nandb       A=12348000 B=00000001 R=12348000 CCIN=0000 CC=0044\nandl       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0004\nandw       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0084\nandb       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0044\nandl       A=12347f7f B=00000000 R=00000000 CCIN=0000 CC=0044\nandw       A=12347f7f B=00000000 R=12340000 CCIN=0000 CC=0044\nandb       A=12347f7f B=00000000 R=12347f00 CCIN=0000 CC=0044\nandl       A=12347f7f B=00000001 R=00000001 CCIN=0000 CC=0000\nandw       A=12347f7f B=00000001 R=12340001 CCIN=0000 CC=0000\nandb       A=12347f7f B=00000001 R=12347f01 CCIN=0000 CC=0000\nandl       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0000\nandw       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0000\nandb       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0000\nandl       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0000\nandw       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0080\nandb       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0080\nandl       A=12348080 B=00000001 R=00000000 CCIN=0000 CC=0044\nandw       A=12348080 B=00000001 R=12340000 CCIN=0000 CC=0044\nandb       A=12348080 B=00000001 R=12348000 CCIN=0000 CC=0044\nandl       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0000\nandw       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0080\nandb       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0080\norl        A=12345678 B=0812fada R=1a36fefa CCIN=0000 CC=0004\norw        A=12345678 B=0812fada R=1234fefa CCIN=0000 CC=0084\norb        A=12345678 B=0812fada R=123456fa CCIN=0000 CC=0084\norl        A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\norw        A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\norb        A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0004\norl        A=00012341 B=fffedcbf R=ffffffff CCIN=0000 CC=0084\norw        A=00012341 B=fffedcbf R=0001ffff CCIN=0000 CC=0084\norb        A=00012341 B=fffedcbf R=000123ff CCIN=0000 CC=0084\norl        A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\norw        A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\norb        A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\norl        A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\norb        A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\norl        A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0084\norw        A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0084\norb        A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0084\norl        A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0084\norw        A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0084\norb        A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0084\norl        A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\norw        A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\norb        A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\norl        A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0004\norw        A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0084\norb        A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0084\norl        A=7fffffff B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0084\norb        A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0084\norl        A=80000000 B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=80000000 B=ffffffff R=8000ffff CCIN=0000 CC=0084\norb        A=80000000 B=ffffffff R=800000ff CCIN=0000 CC=0084\norl        A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0080\norw        A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\norb        A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\norl        A=80000000 B=fffffffe R=fffffffe CCIN=0000 CC=0080\norw        A=80000000 B=fffffffe R=8000fffe CCIN=0000 CC=0080\norb        A=80000000 B=fffffffe R=800000fe CCIN=0000 CC=0080\norl        A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\norw        A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\norb        A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\norl        A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0004\norw        A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0004\norb        A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0084\norl        A=12347fff B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=12347fff B=ffffffff R=1234ffff CCIN=0000 CC=0084\norb        A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0084\norl        A=12348000 B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=12348000 B=ffffffff R=1234ffff CCIN=0000 CC=0084\norb        A=12348000 B=ffffffff R=123480ff CCIN=0000 CC=0084\norl        A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\norw        A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0080\norb        A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\norl        A=12348000 B=fffffffe R=fffffffe CCIN=0000 CC=0080\norw        A=12348000 B=fffffffe R=1234fffe CCIN=0000 CC=0080\norb        A=12348000 B=fffffffe R=123480fe CCIN=0000 CC=0080\norl        A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\norw        A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\norb        A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\norl        A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0000\norw        A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0000\norb        A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0000\norl        A=12347f7f B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=12347f7f B=ffffffff R=1234ffff CCIN=0000 CC=0084\norb        A=12347f7f B=ffffffff R=12347fff CCIN=0000 CC=0084\norl        A=12348080 B=ffffffff R=ffffffff CCIN=0000 CC=0084\norw        A=12348080 B=ffffffff R=1234ffff CCIN=0000 CC=0084\norb        A=12348080 B=ffffffff R=123480ff CCIN=0000 CC=0084\norl        A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0004\norw        A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\norb        A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\norl        A=12348080 B=fffffffe R=fffffffe CCIN=0000 CC=0080\norw        A=12348080 B=fffffffe R=1234fffe CCIN=0000 CC=0080\norb        A=12348080 B=fffffffe R=123480fe CCIN=0000 CC=0080\ncmpl       A=12345678 B=0812fada R=12345678 CCIN=0000 CC=0010\ncmpw       A=12345678 B=0812fada R=12345678 CCIN=0000 CC=0011\ncmpb       A=12345678 B=0812fada R=12345678 CCIN=0000 CC=0891\ncmpl       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0044\ncmpw       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0044\ncmpb       A=00012341 B=00012341 R=00012341 CCIN=0000 CC=0044\ncmpl       A=00012341 B=fffedcbf R=00012341 CCIN=0000 CC=0015\ncmpw       A=00012341 B=fffedcbf R=00012341 CCIN=0000 CC=0015\ncmpb       A=00012341 B=fffedcbf R=00012341 CCIN=0000 CC=0895\ncmpl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\ncmpw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\ncmpb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\ncmpl       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0044\ncmpw       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0044\ncmpb       A=ffffffff B=ffffffff R=ffffffff CCIN=0000 CC=0044\ncmpl       A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0080\ncmpw       A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0080\ncmpb       A=ffffffff B=00000001 R=ffffffff CCIN=0000 CC=0080\ncmpl       A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0080\ncmpw       A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0080\ncmpb       A=ffffffff B=00000002 R=ffffffff CCIN=0000 CC=0080\ncmpl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\ncmpw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\ncmpb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\ncmpl       A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0000\ncmpw       A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0080\ncmpb       A=7fffffff B=00000001 R=7fffffff CCIN=0000 CC=0080\ncmpl       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0885\ncmpw       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0044\ncmpb       A=7fffffff B=ffffffff R=7fffffff CCIN=0000 CC=0044\ncmpl       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0091\ncmpw       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0011\ncmpb       A=80000000 B=ffffffff R=80000000 CCIN=0000 CC=0011\ncmpl       A=80000000 B=00000001 R=80000000 CCIN=0000 CC=0814\ncmpw       A=80000000 B=00000001 R=80000000 CCIN=0000 CC=0095\ncmpb       A=80000000 B=00000001 R=80000000 CCIN=0000 CC=0095\ncmpl       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0091\ncmpw       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0011\ncmpb       A=80000000 B=fffffffe R=80000000 CCIN=0000 CC=0011\ncmpl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\ncmpw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\ncmpb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\ncmpl       A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0000\ncmpw       A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0000\ncmpb       A=12347fff B=00000001 R=12347fff CCIN=0000 CC=0080\ncmpl       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0005\ncmpw       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0885\ncmpb       A=12347fff B=ffffffff R=12347fff CCIN=0000 CC=0044\ncmpl       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0011\ncmpw       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0091\ncmpb       A=12348000 B=ffffffff R=12348000 CCIN=0000 CC=0011\ncmpl       A=12348000 B=00000001 R=12348000 CCIN=0000 CC=0014\ncmpw       A=12348000 B=00000001 R=12348000 CCIN=0000 CC=0814\ncmpb       A=12348000 B=00000001 R=12348000 CCIN=0000 CC=0095\ncmpl       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0011\ncmpw       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0091\ncmpb       A=12348000 B=fffffffe R=12348000 CCIN=0000 CC=0011\ncmpl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\ncmpw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\ncmpb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\ncmpl       A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0004\ncmpw       A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0004\ncmpb       A=12347f7f B=00000001 R=12347f7f CCIN=0000 CC=0004\ncmpl       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0001\ncmpw       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0001\ncmpb       A=12347f7f B=ffffffff R=12347f7f CCIN=0000 CC=0881\ncmpl       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0015\ncmpw       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0095\ncmpb       A=12348080 B=ffffffff R=12348080 CCIN=0000 CC=0095\ncmpl       A=12348080 B=00000001 R=12348080 CCIN=0000 CC=0010\ncmpw       A=12348080 B=00000001 R=12348080 CCIN=0000 CC=0090\ncmpb       A=12348080 B=00000001 R=12348080 CCIN=0000 CC=0810\ncmpl       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0015\ncmpw       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0095\ncmpb       A=12348080 B=fffffffe R=12348080 CCIN=0000 CC=0095\nadcl       A=12345678 B=0812fada R=1a475152 CCIN=0000 CC=0010\nadcw       A=12345678 B=0812fada R=12345152 CCIN=0000 CC=0011\nadcb       A=12345678 B=0812fada R=12345652 CCIN=0000 CC=0011\nadcl       A=12345678 B=0812fada R=1a475153 CCIN=0001 CC=0014\nadcw       A=12345678 B=0812fada R=12345153 CCIN=0001 CC=0015\nadcb       A=12345678 B=0812fada R=12345653 CCIN=0001 CC=0015\nadcl       A=00012341 B=00012341 R=00024682 CCIN=0000 CC=0004\nadcw       A=00012341 B=00012341 R=00014682 CCIN=0000 CC=0004\nadcb       A=00012341 B=00012341 R=00012382 CCIN=0000 CC=0884\nadcl       A=00012341 B=00012341 R=00024683 CCIN=0001 CC=0000\nadcw       A=00012341 B=00012341 R=00014683 CCIN=0001 CC=0000\nadcb       A=00012341 B=00012341 R=00012383 CCIN=0001 CC=0880\nadcl       A=00012341 B=fffedcbf R=00000000 CCIN=0000 CC=0055\nadcw       A=00012341 B=fffedcbf R=00010000 CCIN=0000 CC=0055\nadcb       A=00012341 B=fffedcbf R=00012300 CCIN=0000 CC=0055\nadcl       A=00012341 B=fffedcbf R=00000001 CCIN=0001 CC=0011\nadcw       A=00012341 B=fffedcbf R=00010001 CCIN=0001 CC=0011\nadcb       A=00012341 B=fffedcbf R=00012301 CCIN=0001 CC=0011\nadcl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nadcw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nadcb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nadcl       A=ffffffff B=00000000 R=00000000 CCIN=0001 CC=0055\nadcw       A=ffffffff B=00000000 R=ffff0000 CCIN=0001 CC=0055\nadcb       A=ffffffff B=00000000 R=ffffff00 CCIN=0001 CC=0055\nadcl       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\nadcw       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\nadcb       A=ffffffff B=ffffffff R=fffffffe CCIN=0000 CC=0091\nadcl       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nadcw       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nadcb       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nadcl       A=ffffffff B=00000001 R=00000000 CCIN=0000 CC=0055\nadcw       A=ffffffff B=00000001 R=ffff0000 CCIN=0000 CC=0055\nadcb       A=ffffffff B=00000001 R=ffffff00 CCIN=0000 CC=0055\nadcl       A=ffffffff B=00000001 R=00000001 CCIN=0001 CC=0011\nadcw       A=ffffffff B=00000001 R=ffff0001 CCIN=0001 CC=0011\nadcb       A=ffffffff B=00000001 R=ffffff01 CCIN=0001 CC=0011\nadcl       A=ffffffff B=00000002 R=00000001 CCIN=0000 CC=0011\nadcw       A=ffffffff B=00000002 R=ffff0001 CCIN=0000 CC=0011\nadcb       A=ffffffff B=00000002 R=ffffff01 CCIN=0000 CC=0011\nadcl       A=ffffffff B=00000002 R=00000002 CCIN=0001 CC=0011\nadcw       A=ffffffff B=00000002 R=ffff0002 CCIN=0001 CC=0011\nadcb       A=ffffffff B=00000002 R=ffffff02 CCIN=0001 CC=0011\nadcl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\nadcw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nadcb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nadcl       A=7fffffff B=00000000 R=80000000 CCIN=0001 CC=0894\nadcw       A=7fffffff B=00000000 R=7fff0000 CCIN=0001 CC=0055\nadcb       A=7fffffff B=00000000 R=7fffff00 CCIN=0001 CC=0055\nadcl       A=7fffffff B=00000001 R=80000000 CCIN=0000 CC=0894\nadcw       A=7fffffff B=00000001 R=7fff0000 CCIN=0000 CC=0055\nadcb       A=7fffffff B=00000001 R=7fffff00 CCIN=0000 CC=0055\nadcl       A=7fffffff B=00000001 R=80000001 CCIN=0001 CC=0890\nadcw       A=7fffffff B=00000001 R=7fff0001 CCIN=0001 CC=0011\nadcb       A=7fffffff B=00000001 R=7fffff01 CCIN=0001 CC=0011\nadcl       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0011\nadcw       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0091\nadcb       A=7fffffff B=ffffffff R=7ffffffe CCIN=0000 CC=0091\nadcl       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0015\nadcw       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0095\nadcb       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0095\nadcl       A=80000000 B=ffffffff R=7fffffff CCIN=0000 CC=0805\nadcw       A=80000000 B=ffffffff R=8000ffff CCIN=0000 CC=0084\nadcb       A=80000000 B=ffffffff R=800000ff CCIN=0000 CC=0084\nadcl       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0095\nadcw       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0055\nadcb       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0055\nadcl       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0080\nadcw       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\nadcb       A=80000000 B=00000001 R=80000001 CCIN=0000 CC=0000\nadcl       A=80000000 B=00000001 R=80000002 CCIN=0001 CC=0080\nadcw       A=80000000 B=00000001 R=80000002 CCIN=0001 CC=0000\nadcb       A=80000000 B=00000001 R=80000002 CCIN=0001 CC=0000\nadcl       A=80000000 B=fffffffe R=7ffffffe CCIN=0000 CC=0801\nadcw       A=80000000 B=fffffffe R=8000fffe CCIN=0000 CC=0080\nadcb       A=80000000 B=fffffffe R=800000fe CCIN=0000 CC=0080\nadcl       A=80000000 B=fffffffe R=7fffffff CCIN=0001 CC=0805\nadcw       A=80000000 B=fffffffe R=8000ffff CCIN=0001 CC=0084\nadcb       A=80000000 B=fffffffe R=800000ff CCIN=0001 CC=0084\nadcl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nadcw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nadcb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\nadcl       A=12347fff B=00000000 R=12348000 CCIN=0001 CC=0014\nadcw       A=12347fff B=00000000 R=12348000 CCIN=0001 CC=0894\nadcb       A=12347fff B=00000000 R=12347f00 CCIN=0001 CC=0055\nadcl       A=12347fff B=00000001 R=12348000 CCIN=0000 CC=0014\nadcw       A=12347fff B=00000001 R=12348000 CCIN=0000 CC=0894\nadcb       A=12347fff B=00000001 R=12347f00 CCIN=0000 CC=0055\nadcl       A=12347fff B=00000001 R=12348001 CCIN=0001 CC=0010\nadcw       A=12347fff B=00000001 R=12348001 CCIN=0001 CC=0890\nadcb       A=12347fff B=00000001 R=12347f01 CCIN=0001 CC=0011\nadcl       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0011\nadcw       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0011\nadcb       A=12347fff B=ffffffff R=12347ffe CCIN=0000 CC=0091\nadcl       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0015\nadcw       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0015\nadcb       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0095\nadcl       A=12348000 B=ffffffff R=12347fff CCIN=0000 CC=0005\nadcw       A=12348000 B=ffffffff R=12347fff CCIN=0000 CC=0805\nadcb       A=12348000 B=ffffffff R=123480ff CCIN=0000 CC=0084\nadcl       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0015\nadcw       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0095\nadcb       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0055\nadcl       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\nadcw       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0080\nadcb       A=12348000 B=00000001 R=12348001 CCIN=0000 CC=0000\nadcl       A=12348000 B=00000001 R=12348002 CCIN=0001 CC=0000\nadcw       A=12348000 B=00000001 R=12348002 CCIN=0001 CC=0080\nadcb       A=12348000 B=00000001 R=12348002 CCIN=0001 CC=0000\nadcl       A=12348000 B=fffffffe R=12347ffe CCIN=0000 CC=0001\nadcw       A=12348000 B=fffffffe R=12347ffe CCIN=0000 CC=0801\nadcb       A=12348000 B=fffffffe R=123480fe CCIN=0000 CC=0080\nadcl       A=12348000 B=fffffffe R=12347fff CCIN=0001 CC=0005\nadcw       A=12348000 B=fffffffe R=12347fff CCIN=0001 CC=0805\nadcb       A=12348000 B=fffffffe R=123480ff CCIN=0001 CC=0084\nadcl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nadcw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nadcb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nadcl       A=12347f7f B=00000000 R=12347f80 CCIN=0001 CC=0010\nadcw       A=12347f7f B=00000000 R=12347f80 CCIN=0001 CC=0010\nadcb       A=12347f7f B=00000000 R=12347f80 CCIN=0001 CC=0890\nadcl       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0010\nadcw       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0010\nadcb       A=12347f7f B=00000001 R=12347f80 CCIN=0000 CC=0890\nadcl       A=12347f7f B=00000001 R=12347f81 CCIN=0001 CC=0014\nadcw       A=12347f7f B=00000001 R=12347f81 CCIN=0001 CC=0014\nadcb       A=12347f7f B=00000001 R=12347f81 CCIN=0001 CC=0894\nadcl       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\nadcw       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\nadcb       A=12347f7f B=ffffffff R=12347f7e CCIN=0000 CC=0015\nadcl       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nadcw       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nadcb       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nadcl       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0001\nadcw       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0081\nadcb       A=12348080 B=ffffffff R=1234807f CCIN=0000 CC=0801\nadcl       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0011\nadcw       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0091\nadcb       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0091\nadcl       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0004\nadcw       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\nadcb       A=12348080 B=00000001 R=12348081 CCIN=0000 CC=0084\nadcl       A=12348080 B=00000001 R=12348082 CCIN=0001 CC=0004\nadcw       A=12348080 B=00000001 R=12348082 CCIN=0001 CC=0084\nadcb       A=12348080 B=00000001 R=12348082 CCIN=0001 CC=0084\nadcl       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0005\nadcw       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0085\nadcb       A=12348080 B=fffffffe R=1234807e CCIN=0000 CC=0805\nadcl       A=12348080 B=fffffffe R=1234807f CCIN=0001 CC=0001\nadcw       A=12348080 B=fffffffe R=1234807f CCIN=0001 CC=0081\nadcb       A=12348080 B=fffffffe R=1234807f CCIN=0001 CC=0801\nsbbl       A=12345678 B=0812fada R=0a215b9e CCIN=0000 CC=0010\nsbbw       A=12345678 B=0812fada R=12345b9e CCIN=0000 CC=0011\nsbbb       A=12345678 B=0812fada R=1234569e CCIN=0000 CC=0891\nsbbl       A=12345678 B=0812fada R=0a215b9d CCIN=0001 CC=0010\nsbbw       A=12345678 B=0812fada R=12345b9d CCIN=0001 CC=0011\nsbbb       A=12345678 B=0812fada R=1234569d CCIN=0001 CC=0891\nsbbl       A=00012341 B=00012341 R=00000000 CCIN=0000 CC=0044\nsbbw       A=00012341 B=00012341 R=00010000 CCIN=0000 CC=0044\nsbbb       A=00012341 B=00012341 R=00012300 CCIN=0000 CC=0044\nsbbl       A=00012341 B=00012341 R=ffffffff CCIN=0001 CC=0095\nsbbw       A=00012341 B=00012341 R=0001ffff CCIN=0001 CC=0095\nsbbb       A=00012341 B=00012341 R=000123ff CCIN=0001 CC=0095\nsbbl       A=00012341 B=fffedcbf R=00024682 CCIN=0000 CC=0015\nsbbw       A=00012341 B=fffedcbf R=00014682 CCIN=0000 CC=0015\nsbbb       A=00012341 B=fffedcbf R=00012382 CCIN=0000 CC=0895\nsbbl       A=00012341 B=fffedcbf R=00024681 CCIN=0001 CC=0015\nsbbw       A=00012341 B=fffedcbf R=00014681 CCIN=0001 CC=0015\nsbbb       A=00012341 B=fffedcbf R=00012381 CCIN=0001 CC=0895\nsbbl       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsbbw       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsbbb       A=ffffffff B=00000000 R=ffffffff CCIN=0000 CC=0084\nsbbl       A=ffffffff B=00000000 R=fffffffe CCIN=0001 CC=0080\nsbbw       A=ffffffff B=00000000 R=fffffffe CCIN=0001 CC=0080\nsbbb       A=ffffffff B=00000000 R=fffffffe CCIN=0001 CC=0080\nsbbl       A=ffffffff B=ffffffff R=00000000 CCIN=0000 CC=0044\nsbbw       A=ffffffff B=ffffffff R=ffff0000 CCIN=0000 CC=0044\nsbbb       A=ffffffff B=ffffffff R=ffffff00 CCIN=0000 CC=0044\nsbbl       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nsbbw       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nsbbb       A=ffffffff B=ffffffff R=ffffffff CCIN=0001 CC=0095\nsbbl       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsbbw       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsbbb       A=ffffffff B=00000001 R=fffffffe CCIN=0000 CC=0080\nsbbl       A=ffffffff B=00000001 R=fffffffd CCIN=0001 CC=0080\nsbbw       A=ffffffff B=00000001 R=fffffffd CCIN=0001 CC=0080\nsbbb       A=ffffffff B=00000001 R=fffffffd CCIN=0001 CC=0080\nsbbl       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsbbw       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsbbb       A=ffffffff B=00000002 R=fffffffd CCIN=0000 CC=0080\nsbbl       A=ffffffff B=00000002 R=fffffffc CCIN=0001 CC=0084\nsbbw       A=ffffffff B=00000002 R=fffffffc CCIN=0001 CC=0084\nsbbb       A=ffffffff B=00000002 R=fffffffc CCIN=0001 CC=0084\nsbbl       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0004\nsbbw       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nsbbb       A=7fffffff B=00000000 R=7fffffff CCIN=0000 CC=0084\nsbbl       A=7fffffff B=00000000 R=7ffffffe CCIN=0001 CC=0000\nsbbw       A=7fffffff B=00000000 R=7ffffffe CCIN=0001 CC=0080\nsbbb       A=7fffffff B=00000000 R=7ffffffe CCIN=0001 CC=0080\nsbbl       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0000\nsbbw       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nsbbb       A=7fffffff B=00000001 R=7ffffffe CCIN=0000 CC=0080\nsbbl       A=7fffffff B=00000001 R=7ffffffd CCIN=0001 CC=0000\nsbbw       A=7fffffff B=00000001 R=7ffffffd CCIN=0001 CC=0080\nsbbb       A=7fffffff B=00000001 R=7ffffffd CCIN=0001 CC=0080\nsbbl       A=7fffffff B=ffffffff R=80000000 CCIN=0000 CC=0885\nsbbw       A=7fffffff B=ffffffff R=7fff0000 CCIN=0000 CC=0044\nsbbb       A=7fffffff B=ffffffff R=7fffff00 CCIN=0000 CC=0044\nsbbl       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0015\nsbbw       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0095\nsbbb       A=7fffffff B=ffffffff R=7fffffff CCIN=0001 CC=0095\nsbbl       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0091\nsbbw       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0011\nsbbb       A=80000000 B=ffffffff R=80000001 CCIN=0000 CC=0011\nsbbl       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0095\nsbbw       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0055\nsbbb       A=80000000 B=ffffffff R=80000000 CCIN=0001 CC=0055\nsbbl       A=80000000 B=00000001 R=7fffffff CCIN=0000 CC=0814\nsbbw       A=80000000 B=00000001 R=8000ffff CCIN=0000 CC=0095\nsbbb       A=80000000 B=00000001 R=800000ff CCIN=0000 CC=0095\nsbbl       A=80000000 B=00000001 R=7ffffffe CCIN=0001 CC=0810\nsbbw       A=80000000 B=00000001 R=8000fffe CCIN=0001 CC=0091\nsbbb       A=80000000 B=00000001 R=800000fe CCIN=0001 CC=0091\nsbbl       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0091\nsbbw       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0011\nsbbb       A=80000000 B=fffffffe R=80000002 CCIN=0000 CC=0011\nsbbl       A=80000000 B=fffffffe R=80000001 CCIN=0001 CC=0091\nsbbw       A=80000000 B=fffffffe R=80000001 CCIN=0001 CC=0011\nsbbb       A=80000000 B=fffffffe R=80000001 CCIN=0001 CC=0011\nsbbl       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nsbbw       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0004\nsbbb       A=12347fff B=00000000 R=12347fff CCIN=0000 CC=0084\nsbbl       A=12347fff B=00000000 R=12347ffe CCIN=0001 CC=0000\nsbbw       A=12347fff B=00000000 R=12347ffe CCIN=0001 CC=0000\nsbbb       A=12347fff B=00000000 R=12347ffe CCIN=0001 CC=0080\nsbbl       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nsbbw       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0000\nsbbb       A=12347fff B=00000001 R=12347ffe CCIN=0000 CC=0080\nsbbl       A=12347fff B=00000001 R=12347ffd CCIN=0001 CC=0000\nsbbw       A=12347fff B=00000001 R=12347ffd CCIN=0001 CC=0000\nsbbb       A=12347fff B=00000001 R=12347ffd CCIN=0001 CC=0080\nsbbl       A=12347fff B=ffffffff R=12348000 CCIN=0000 CC=0005\nsbbw       A=12347fff B=ffffffff R=12348000 CCIN=0000 CC=0885\nsbbb       A=12347fff B=ffffffff R=12347f00 CCIN=0000 CC=0044\nsbbl       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0015\nsbbw       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0015\nsbbb       A=12347fff B=ffffffff R=12347fff CCIN=0001 CC=0095\nsbbl       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0011\nsbbw       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0091\nsbbb       A=12348000 B=ffffffff R=12348001 CCIN=0000 CC=0011\nsbbl       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0015\nsbbw       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0095\nsbbb       A=12348000 B=ffffffff R=12348000 CCIN=0001 CC=0055\nsbbl       A=12348000 B=00000001 R=12347fff CCIN=0000 CC=0014\nsbbw       A=12348000 B=00000001 R=12347fff CCIN=0000 CC=0814\nsbbb       A=12348000 B=00000001 R=123480ff CCIN=0000 CC=0095\nsbbl       A=12348000 B=00000001 R=12347ffe CCIN=0001 CC=0010\nsbbw       A=12348000 B=00000001 R=12347ffe CCIN=0001 CC=0810\nsbbb       A=12348000 B=00000001 R=123480fe CCIN=0001 CC=0091\nsbbl       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0011\nsbbw       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0091\nsbbb       A=12348000 B=fffffffe R=12348002 CCIN=0000 CC=0011\nsbbl       A=12348000 B=fffffffe R=12348001 CCIN=0001 CC=0011\nsbbw       A=12348000 B=fffffffe R=12348001 CCIN=0001 CC=0091\nsbbb       A=12348000 B=fffffffe R=12348001 CCIN=0001 CC=0011\nsbbl       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsbbw       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsbbb       A=12347f7f B=00000000 R=12347f7f CCIN=0000 CC=0000\nsbbl       A=12347f7f B=00000000 R=12347f7e CCIN=0001 CC=0004\nsbbw       A=12347f7f B=00000000 R=12347f7e CCIN=0001 CC=0004\nsbbb       A=12347f7f B=00000000 R=12347f7e CCIN=0001 CC=0004\nsbbl       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsbbw       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsbbb       A=12347f7f B=00000001 R=12347f7e CCIN=0000 CC=0004\nsbbl       A=12347f7f B=00000001 R=12347f7d CCIN=0001 CC=0004\nsbbw       A=12347f7f B=00000001 R=12347f7d CCIN=0001 CC=0004\nsbbb       A=12347f7f B=00000001 R=12347f7d CCIN=0001 CC=0004\nsbbl       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0001\nsbbw       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0001\nsbbb       A=12347f7f B=ffffffff R=12347f80 CCIN=0000 CC=0881\nsbbl       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nsbbw       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nsbbb       A=12347f7f B=ffffffff R=12347f7f CCIN=0001 CC=0011\nsbbl       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0015\nsbbw       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0095\nsbbb       A=12348080 B=ffffffff R=12348081 CCIN=0000 CC=0095\nsbbl       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0011\nsbbw       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0091\nsbbb       A=12348080 B=ffffffff R=12348080 CCIN=0001 CC=0091\nsbbl       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0010\nsbbw       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0090\nsbbb       A=12348080 B=00000001 R=1234807f CCIN=0000 CC=0810\nsbbl       A=12348080 B=00000001 R=1234807e CCIN=0001 CC=0014\nsbbw       A=12348080 B=00000001 R=1234807e CCIN=0001 CC=0094\nsbbb       A=12348080 B=00000001 R=1234807e CCIN=0001 CC=0814\nsbbl       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0015\nsbbw       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0095\nsbbb       A=12348080 B=fffffffe R=12348082 CCIN=0000 CC=0095\nsbbl       A=12348080 B=fffffffe R=12348081 CCIN=0001 CC=0015\nsbbw       A=12348080 B=fffffffe R=12348081 CCIN=0001 CC=0095\nsbbb       A=12348080 B=fffffffe R=12348081 CCIN=0001 CC=0095\nincl       A=12345678 R=12345679 CCIN=0000 CC=0000\nincw       A=12345678 R=12345679 CCIN=0000 CC=0000\nincb       A=12345678 R=12345679 CCIN=0000 CC=0000\nincl       A=12345678 R=12345679 CCIN=0001 CC=0001\nincw       A=12345678 R=12345679 CCIN=0001 CC=0001\nincb       A=12345678 R=12345679 CCIN=0001 CC=0001\nincl       A=00012341 R=00012342 CCIN=0000 CC=0004\nincw       A=00012341 R=00012342 CCIN=0000 CC=0004\nincb       A=00012341 R=00012342 CCIN=0000 CC=0004\nincl       A=00012341 R=00012342 CCIN=0001 CC=0005\nincw       A=00012341 R=00012342 CCIN=0001 CC=0005\nincb       A=00012341 R=00012342 CCIN=0001 CC=0005\nincl       A=00012341 R=00012342 CCIN=0000 CC=0004\nincw       A=00012341 R=00012342 CCIN=0000 CC=0004\nincb       A=00012341 R=00012342 CCIN=0000 CC=0004\nincl       A=00012341 R=00012342 CCIN=0001 CC=0005\nincw       A=00012341 R=00012342 CCIN=0001 CC=0005\nincb       A=00012341 R=00012342 CCIN=0001 CC=0005\nincl       A=ffffffff R=00000000 CCIN=0000 CC=0054\nincw       A=ffffffff R=ffff0000 CCIN=0000 CC=0054\nincb       A=ffffffff R=ffffff00 CCIN=0000 CC=0054\nincl       A=ffffffff R=00000000 CCIN=0001 CC=0055\nincw       A=ffffffff R=ffff0000 CCIN=0001 CC=0055\nincb       A=ffffffff R=ffffff00 CCIN=0001 CC=0055\nincl       A=ffffffff R=00000000 CCIN=0000 CC=0054\nincw       A=ffffffff R=ffff0000 CCIN=0000 CC=0054\nincb       A=ffffffff R=ffffff00 CCIN=0000 CC=0054\nincl       A=ffffffff R=00000000 CCIN=0001 CC=0055\nincw       A=ffffffff R=ffff0000 CCIN=0001 CC=0055\nincb       A=ffffffff R=ffffff00 CCIN=0001 CC=0055\nincl       A=ffffffff R=00000000 CCIN=0000 CC=0054\nincw       A=ffffffff R=ffff0000 CCIN=0000 CC=0054\nincb       A=ffffffff R=ffffff00 CCIN=0000 CC=0054\nincl       A=ffffffff R=00000000 CCIN=0001 CC=0055\nincw       A=ffffffff R=ffff0000 CCIN=0001 CC=0055\nincb       A=ffffffff R=ffffff00 CCIN=0001 CC=0055\nincl       A=ffffffff R=00000000 CCIN=0000 CC=0054\nincw       A=ffffffff R=ffff0000 CCIN=0000 CC=0054\nincb       A=ffffffff R=ffffff00 CCIN=0000 CC=0054\nincl       A=ffffffff R=00000000 CCIN=0001 CC=0055\nincw       A=ffffffff R=ffff0000 CCIN=0001 CC=0055\nincb       A=ffffffff R=ffffff00 CCIN=0001 CC=0055\nincl       A=7fffffff R=80000000 CCIN=0000 CC=0894\nincw       A=7fffffff R=7fff0000 CCIN=0000 CC=0054\nincb       A=7fffffff R=7fffff00 CCIN=0000 CC=0054\nincl       A=7fffffff R=80000000 CCIN=0001 CC=0895\nincw       A=7fffffff R=7fff0000 CCIN=0001 CC=0055\nincb       A=7fffffff R=7fffff00 CCIN=0001 CC=0055\nincl       A=7fffffff R=80000000 CCIN=0000 CC=0894\nincw       A=7fffffff R=7fff0000 CCIN=0000 CC=0054\nincb       A=7fffffff R=7fffff00 CCIN=0000 CC=0054\nincl       A=7fffffff R=80000000 CCIN=0001 CC=0895\nincw       A=7fffffff R=7fff0000 CCIN=0001 CC=0055\nincb       A=7fffffff R=7fffff00 CCIN=0001 CC=0055\nincl       A=7fffffff R=80000000 CCIN=0000 CC=0894\nincw       A=7fffffff R=7fff0000 CCIN=0000 CC=0054\nincb       A=7fffffff R=7fffff00 CCIN=0000 CC=0054\nincl       A=7fffffff R=80000000 CCIN=0001 CC=0895\nincw       A=7fffffff R=7fff0000 CCIN=0001 CC=0055\nincb       A=7fffffff R=7fffff00 CCIN=0001 CC=0055\nincl       A=80000000 R=80000001 CCIN=0000 CC=0080\nincw       A=80000000 R=80000001 CCIN=0000 CC=0000\nincb       A=80000000 R=80000001 CCIN=0000 CC=0000\nincl       A=80000000 R=80000001 CCIN=0001 CC=0081\nincw       A=80000000 R=80000001 CCIN=0001 CC=0001\nincb       A=80000000 R=80000001 CCIN=0001 CC=0001\nincl       A=80000000 R=80000001 CCIN=0000 CC=0080\nincw       A=80000000 R=80000001 CCIN=0000 CC=0000\nincb       A=80000000 R=80000001 CCIN=0000 CC=0000\nincl       A=80000000 R=80000001 CCIN=0001 CC=0081\nincw       A=80000000 R=80000001 CCIN=0001 CC=0001\nincb       A=80000000 R=80000001 CCIN=0001 CC=0001\nincl       A=80000000 R=80000001 CCIN=0000 CC=0080\nincw       A=80000000 R=80000001 CCIN=0000 CC=0000\nincb       A=80000000 R=80000001 CCIN=0000 CC=0000\nincl       A=80000000 R=80000001 CCIN=0001 CC=0081\nincw       A=80000000 R=80000001 CCIN=0001 CC=0001\nincb       A=80000000 R=80000001 CCIN=0001 CC=0001\nincl       A=12347fff R=12348000 CCIN=0000 CC=0014\nincw       A=12347fff R=12348000 CCIN=0000 CC=0894\nincb       A=12347fff R=12347f00 CCIN=0000 CC=0054\nincl       A=12347fff R=12348000 CCIN=0001 CC=0015\nincw       A=12347fff R=12348000 CCIN=0001 CC=0895\nincb       A=12347fff R=12347f00 CCIN=0001 CC=0055\nincl       A=12347fff R=12348000 CCIN=0000 CC=0014\nincw       A=12347fff R=12348000 CCIN=0000 CC=0894\nincb       A=12347fff R=12347f00 CCIN=0000 CC=0054\nincl       A=12347fff R=12348000 CCIN=0001 CC=0015\nincw       A=12347fff R=12348000 CCIN=0001 CC=0895\nincb       A=12347fff R=12347f00 CCIN=0001 CC=0055\nincl       A=12347fff R=12348000 CCIN=0000 CC=0014\nincw       A=12347fff R=12348000 CCIN=0000 CC=0894\nincb       A=12347fff R=12347f00 CCIN=0000 CC=0054\nincl       A=12347fff R=12348000 CCIN=0001 CC=0015\nincw       A=12347fff R=12348000 CCIN=0001 CC=0895\nincb       A=12347fff R=12347f00 CCIN=0001 CC=0055\nincl       A=12348000 R=12348001 CCIN=0000 CC=0000\nincw       A=12348000 R=12348001 CCIN=0000 CC=0080\nincb       A=12348000 R=12348001 CCIN=0000 CC=0000\nincl       A=12348000 R=12348001 CCIN=0001 CC=0001\nincw       A=12348000 R=12348001 CCIN=0001 CC=0081\nincb       A=12348000 R=12348001 CCIN=0001 CC=0001\nincl       A=12348000 R=12348001 CCIN=0000 CC=0000\nincw       A=12348000 R=12348001 CCIN=0000 CC=0080\nincb       A=12348000 R=12348001 CCIN=0000 CC=0000\nincl       A=12348000 R=12348001 CCIN=0001 CC=0001\nincw       A=12348000 R=12348001 CCIN=0001 CC=0081\nincb       A=12348000 R=12348001 CCIN=0001 CC=0001\nincl       A=12348000 R=12348001 CCIN=0000 CC=0000\nincw       A=12348000 R=12348001 CCIN=0000 CC=0080\nincb       A=12348000 R=12348001 CCIN=0000 CC=0000\nincl       A=12348000 R=12348001 CCIN=0001 CC=0001\nincw       A=12348000 R=12348001 CCIN=0001 CC=0081\nincb       A=12348000 R=12348001 CCIN=0001 CC=0001\nincl       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincw       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincb       A=12347f7f R=12347f80 CCIN=0000 CC=0890\nincl       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincw       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincb       A=12347f7f R=12347f80 CCIN=0001 CC=0891\nincl       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincw       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincb       A=12347f7f R=12347f80 CCIN=0000 CC=0890\nincl       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincw       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincb       A=12347f7f R=12347f80 CCIN=0001 CC=0891\nincl       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincw       A=12347f7f R=12347f80 CCIN=0000 CC=0010\nincb       A=12347f7f R=12347f80 CCIN=0000 CC=0890\nincl       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincw       A=12347f7f R=12347f80 CCIN=0001 CC=0011\nincb       A=12347f7f R=12347f80 CCIN=0001 CC=0891\nincl       A=12348080 R=12348081 CCIN=0000 CC=0004\nincw       A=12348080 R=12348081 CCIN=0000 CC=0084\nincb       A=12348080 R=12348081 CCIN=0000 CC=0084\nincl       A=12348080 R=12348081 CCIN=0001 CC=0005\nincw       A=12348080 R=12348081 CCIN=0001 CC=0085\nincb       A=12348080 R=12348081 CCIN=0001 CC=0085\nincl       A=12348080 R=12348081 CCIN=0000 CC=0004\nincw       A=12348080 R=12348081 CCIN=0000 CC=0084\nincb       A=12348080 R=12348081 CCIN=0000 CC=0084\nincl       A=12348080 R=12348081 CCIN=0001 CC=0005\nincw       A=12348080 R=12348081 CCIN=0001 CC=0085\nincb       A=12348080 R=12348081 CCIN=0001 CC=0085\nincl       A=12348080 R=12348081 CCIN=0000 CC=0004\nincw       A=12348080 R=12348081 CCIN=0000 CC=0084\nincb       A=12348080 R=12348081 CCIN=0000 CC=0084\nincl       A=12348080 R=12348081 CCIN=0001 CC=0005\nincw       A=12348080 R=12348081 CCIN=0001 CC=0085\nincb       A=12348080 R=12348081 CCIN=0001 CC=0085\ndecl       A=12345678 R=12345677 CCIN=0000 CC=0004\ndecw       A=12345678 R=12345677 CCIN=0000 CC=0004\ndecb       A=12345678 R=12345677 CCIN=0000 CC=0004\ndecl       A=12345678 R=12345677 CCIN=0001 CC=0005\ndecw       A=12345678 R=12345677 CCIN=0001 CC=0005\ndecb       A=12345678 R=12345677 CCIN=0001 CC=0005\ndecl       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecw       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecb       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecl       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecw       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecb       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecl       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecw       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecb       A=00012341 R=00012340 CCIN=0000 CC=0000\ndecl       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecw       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecb       A=00012341 R=00012340 CCIN=0001 CC=0001\ndecl       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecw       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecb       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecl       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecw       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecb       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecl       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecw       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecb       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecl       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecw       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecb       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecl       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecw       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecb       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecl       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecw       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecb       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecl       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecw       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecb       A=ffffffff R=fffffffe CCIN=0000 CC=0080\ndecl       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecw       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecb       A=ffffffff R=fffffffe CCIN=0001 CC=0081\ndecl       A=7fffffff R=7ffffffe CCIN=0000 CC=0000\ndecw       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecb       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecl       A=7fffffff R=7ffffffe CCIN=0001 CC=0001\ndecw       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecb       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecl       A=7fffffff R=7ffffffe CCIN=0000 CC=0000\ndecw       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecb       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecl       A=7fffffff R=7ffffffe CCIN=0001 CC=0001\ndecw       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecb       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecl       A=7fffffff R=7ffffffe CCIN=0000 CC=0000\ndecw       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecb       A=7fffffff R=7ffffffe CCIN=0000 CC=0080\ndecl       A=7fffffff R=7ffffffe CCIN=0001 CC=0001\ndecw       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecb       A=7fffffff R=7ffffffe CCIN=0001 CC=0081\ndecl       A=80000000 R=7fffffff CCIN=0000 CC=0814\ndecw       A=80000000 R=8000ffff CCIN=0000 CC=0094\ndecb       A=80000000 R=800000ff CCIN=0000 CC=0094\ndecl       A=80000000 R=7fffffff CCIN=0001 CC=0815\ndecw       A=80000000 R=8000ffff CCIN=0001 CC=0095\ndecb       A=80000000 R=800000ff CCIN=0001 CC=0095\ndecl       A=80000000 R=7fffffff CCIN=0000 CC=0814\ndecw       A=80000000 R=8000ffff CCIN=0000 CC=0094\ndecb       A=80000000 R=800000ff CCIN=0000 CC=0094\ndecl       A=80000000 R=7fffffff CCIN=0001 CC=0815\ndecw       A=80000000 R=8000ffff CCIN=0001 CC=0095\ndecb       A=80000000 R=800000ff CCIN=0001 CC=0095\ndecl       A=80000000 R=7fffffff CCIN=0000 CC=0814\ndecw       A=80000000 R=8000ffff CCIN=0000 CC=0094\ndecb       A=80000000 R=800000ff CCIN=0000 CC=0094\ndecl       A=80000000 R=7fffffff CCIN=0001 CC=0815\ndecw       A=80000000 R=8000ffff CCIN=0001 CC=0095\ndecb       A=80000000 R=800000ff CCIN=0001 CC=0095\ndecl       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecw       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecb       A=12347fff R=12347ffe CCIN=0000 CC=0080\ndecl       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecw       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecb       A=12347fff R=12347ffe CCIN=0001 CC=0081\ndecl       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecw       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecb       A=12347fff R=12347ffe CCIN=0000 CC=0080\ndecl       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecw       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecb       A=12347fff R=12347ffe CCIN=0001 CC=0081\ndecl       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecw       A=12347fff R=12347ffe CCIN=0000 CC=0000\ndecb       A=12347fff R=12347ffe CCIN=0000 CC=0080\ndecl       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecw       A=12347fff R=12347ffe CCIN=0001 CC=0001\ndecb       A=12347fff R=12347ffe CCIN=0001 CC=0081\ndecl       A=12348000 R=12347fff CCIN=0000 CC=0014\ndecw       A=12348000 R=12347fff CCIN=0000 CC=0814\ndecb       A=12348000 R=123480ff CCIN=0000 CC=0094\ndecl       A=12348000 R=12347fff CCIN=0001 CC=0015\ndecw       A=12348000 R=12347fff CCIN=0001 CC=0815\ndecb       A=12348000 R=123480ff CCIN=0001 CC=0095\ndecl       A=12348000 R=12347fff CCIN=0000 CC=0014\ndecw       A=12348000 R=12347fff CCIN=0000 CC=0814\ndecb       A=12348000 R=123480ff CCIN=0000 CC=0094\ndecl       A=12348000 R=12347fff CCIN=0001 CC=0015\ndecw       A=12348000 R=12347fff CCIN=0001 CC=0815\ndecb       A=12348000 R=123480ff CCIN=0001 CC=0095\ndecl       A=12348000 R=12347fff CCIN=0000 CC=0014\ndecw       A=12348000 R=12347fff CCIN=0000 CC=0814\ndecb       A=12348000 R=123480ff CCIN=0000 CC=0094\ndecl       A=12348000 R=12347fff CCIN=0001 CC=0015\ndecw       A=12348000 R=12347fff CCIN=0001 CC=0815\ndecb       A=12348000 R=123480ff CCIN=0001 CC=0095\ndecl       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecw       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecb       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecl       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecw       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecb       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecl       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecw       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecb       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecl       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecw       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecb       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecl       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecw       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecb       A=12347f7f R=12347f7e CCIN=0000 CC=0004\ndecl       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecw       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecb       A=12347f7f R=12347f7e CCIN=0001 CC=0005\ndecl       A=12348080 R=1234807f CCIN=0000 CC=0010\ndecw       A=12348080 R=1234807f CCIN=0000 CC=0090\ndecb       A=12348080 R=1234807f CCIN=0000 CC=0810\ndecl       A=12348080 R=1234807f CCIN=0001 CC=0011\ndecw       A=12348080 R=1234807f CCIN=0001 CC=0091\ndecb       A=12348080 R=1234807f CCIN=0001 CC=0811\ndecl       A=12348080 R=1234807f CCIN=0000 CC=0010\ndecw       A=12348080 R=1234807f CCIN=0000 CC=0090\ndecb       A=12348080 R=1234807f CCIN=0000 CC=0810\ndecl       A=12348080 R=1234807f CCIN=0001 CC=0011\ndecw       A=12348080 R=1234807f CCIN=0001 CC=0091\ndecb       A=12348080 R=1234807f CCIN=0001 CC=0811\ndecl       A=12348080 R=1234807f CCIN=0000 CC=0010\ndecw       A=12348080 R=1234807f CCIN=0000 CC=0090\ndecb       A=12348080 R=1234807f CCIN=0000 CC=0810\ndecl       A=12348080 R=1234807f CCIN=0001 CC=0011\ndecw       A=12348080 R=1234807f CCIN=0001 CC=0091\ndecb       A=12348080 R=1234807f CCIN=0001 CC=0811\nnegl       A=12345678 R=edcba988 CCIN=0000 CC=0095\nnegw       A=12345678 R=1234a988 CCIN=0000 CC=0095\nnegb       A=12345678 R=12345688 CCIN=0000 CC=0095\nnegl       A=12345678 R=edcba988 CCIN=0001 CC=0095\nnegw       A=12345678 R=1234a988 CCIN=0001 CC=0095\nnegb       A=12345678 R=12345688 CCIN=0001 CC=0095\nnegl       A=00012341 R=fffedcbf CCIN=0000 CC=0091\nnegw       A=00012341 R=0001dcbf CCIN=0000 CC=0091\nnegb       A=00012341 R=000123bf CCIN=0000 CC=0091\nnegl       A=00012341 R=fffedcbf CCIN=0001 CC=0091\nnegw       A=00012341 R=0001dcbf CCIN=0001 CC=0091\nnegb       A=00012341 R=000123bf CCIN=0001 CC=0091\nnegl       A=00012341 R=fffedcbf CCIN=0000 CC=0091\nnegw       A=00012341 R=0001dcbf CCIN=0000 CC=0091\nnegb       A=00012341 R=000123bf CCIN=0000 CC=0091\nnegl       A=00012341 R=fffedcbf CCIN=0001 CC=0091\nnegw       A=00012341 R=0001dcbf CCIN=0001 CC=0091\nnegb       A=00012341 R=000123bf CCIN=0001 CC=0091\nnegl       A=ffffffff R=00000001 CCIN=0000 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0000 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0000 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0001 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0001 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0001 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0000 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0000 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0000 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0001 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0001 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0001 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0000 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0000 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0000 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0001 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0001 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0001 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0000 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0000 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0000 CC=0011\nnegl       A=ffffffff R=00000001 CCIN=0001 CC=0011\nnegw       A=ffffffff R=ffff0001 CCIN=0001 CC=0011\nnegb       A=ffffffff R=ffffff01 CCIN=0001 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0000 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0000 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0000 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0001 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0001 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0001 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0000 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0000 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0000 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0001 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0001 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0001 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0000 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0000 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0000 CC=0011\nnegl       A=7fffffff R=80000001 CCIN=0001 CC=0091\nnegw       A=7fffffff R=7fff0001 CCIN=0001 CC=0011\nnegb       A=7fffffff R=7fffff01 CCIN=0001 CC=0011\nnegl       A=80000000 R=80000000 CCIN=0000 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegl       A=80000000 R=80000000 CCIN=0001 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegl       A=80000000 R=80000000 CCIN=0000 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegl       A=80000000 R=80000000 CCIN=0001 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegl       A=80000000 R=80000000 CCIN=0000 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0000 CC=0044\nnegl       A=80000000 R=80000000 CCIN=0001 CC=0885\nnegw       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegb       A=80000000 R=80000000 CCIN=0001 CC=0044\nnegl       A=12347fff R=edcb8001 CCIN=0000 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0000 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0000 CC=0011\nnegl       A=12347fff R=edcb8001 CCIN=0001 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0001 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0001 CC=0011\nnegl       A=12347fff R=edcb8001 CCIN=0000 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0000 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0000 CC=0011\nnegl       A=12347fff R=edcb8001 CCIN=0001 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0001 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0001 CC=0011\nnegl       A=12347fff R=edcb8001 CCIN=0000 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0000 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0000 CC=0011\nnegl       A=12347fff R=edcb8001 CCIN=0001 CC=0091\nnegw       A=12347fff R=12348001 CCIN=0001 CC=0091\nnegb       A=12347fff R=12347f01 CCIN=0001 CC=0011\nnegl       A=12348000 R=edcb8000 CCIN=0000 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0000 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0000 CC=0044\nnegl       A=12348000 R=edcb8000 CCIN=0001 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0001 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0001 CC=0044\nnegl       A=12348000 R=edcb8000 CCIN=0000 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0000 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0000 CC=0044\nnegl       A=12348000 R=edcb8000 CCIN=0001 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0001 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0001 CC=0044\nnegl       A=12348000 R=edcb8000 CCIN=0000 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0000 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0000 CC=0044\nnegl       A=12348000 R=edcb8000 CCIN=0001 CC=0085\nnegw       A=12348000 R=12348000 CCIN=0001 CC=0885\nnegb       A=12348000 R=12348000 CCIN=0001 CC=0044\nnegl       A=12347f7f R=edcb8081 CCIN=0000 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0000 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0000 CC=0095\nnegl       A=12347f7f R=edcb8081 CCIN=0001 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0001 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0001 CC=0095\nnegl       A=12347f7f R=edcb8081 CCIN=0000 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0000 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0000 CC=0095\nnegl       A=12347f7f R=edcb8081 CCIN=0001 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0001 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0001 CC=0095\nnegl       A=12347f7f R=edcb8081 CCIN=0000 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0000 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0000 CC=0095\nnegl       A=12347f7f R=edcb8081 CCIN=0001 CC=0095\nnegw       A=12347f7f R=12348081 CCIN=0001 CC=0095\nnegb       A=12347f7f R=12347f81 CCIN=0001 CC=0095\nnegl       A=12348080 R=edcb7f80 CCIN=0000 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0000 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0000 CC=0881\nnegl       A=12348080 R=edcb7f80 CCIN=0001 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0001 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0001 CC=0881\nnegl       A=12348080 R=edcb7f80 CCIN=0000 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0000 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0000 CC=0881\nnegl       A=12348080 R=edcb7f80 CCIN=0001 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0001 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0001 CC=0881\nnegl       A=12348080 R=edcb7f80 CCIN=0000 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0000 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0000 CC=0881\nnegl       A=12348080 R=edcb7f80 CCIN=0001 CC=0081\nnegw       A=12348080 R=12347f80 CCIN=0001 CC=0001\nnegb       A=12348080 R=12348080 CCIN=0001 CC=0881\nnotl       A=12345678 R=edcba987 CCIN=0000 CC=0000\nnotw       A=12345678 R=1234a987 CCIN=0000 CC=0000\nnotb       A=12345678 R=12345687 CCIN=0000 CC=0000\nnotl       A=12345678 R=edcba987 CCIN=0001 CC=0001\nnotw       A=12345678 R=1234a987 CCIN=0001 CC=0001\nnotb       A=12345678 R=12345687 CCIN=0001 CC=0001\nnotl       A=00012341 R=fffedcbe CCIN=0000 CC=0000\nnotw       A=00012341 R=0001dcbe CCIN=0000 CC=0000\nnotb       A=00012341 R=000123be CCIN=0000 CC=0000\nnotl       A=00012341 R=fffedcbe CCIN=0001 CC=0001\nnotw       A=00012341 R=0001dcbe CCIN=0001 CC=0001\nnotb       A=00012341 R=000123be CCIN=0001 CC=0001\nnotl       A=00012341 R=fffedcbe CCIN=0000 CC=0000\nnotw       A=00012341 R=0001dcbe CCIN=0000 CC=0000\nnotb       A=00012341 R=000123be CCIN=0000 CC=0000\nnotl       A=00012341 R=fffedcbe CCIN=0001 CC=0001\nnotw       A=00012341 R=0001dcbe CCIN=0001 CC=0001\nnotb       A=00012341 R=000123be CCIN=0001 CC=0001\nnotl       A=ffffffff R=00000000 CCIN=0000 CC=0000\nnotw       A=ffffffff R=ffff0000 CCIN=0000 CC=0000\nnotb       A=ffffffff R=ffffff00 CCIN=0000 CC=0000\nnotl       A=ffffffff R=00000000 CCIN=0001 CC=0001\nnotw       A=ffffffff R=ffff0000 CCIN=0001 CC=0001\nnotb       A=ffffffff R=ffffff00 CCIN=0001 CC=0001\nnotl       A=ffffffff R=00000000 CCIN=0000 CC=0000\nnotw       A=ffffffff R=ffff0000 CCIN=0000 CC=0000\nnotb       A=ffffffff R=ffffff00 CCIN=0000 CC=0000\nnotl       A=ffffffff R=00000000 CCIN=0001 CC=0001\nnotw       A=ffffffff R=ffff0000 CCIN=0001 CC=0001\nnotb       A=ffffffff R=ffffff00 CCIN=0001 CC=0001\nnotl       A=ffffffff R=00000000 CCIN=0000 CC=0000\nnotw       A=ffffffff R=ffff0000 CCIN=0000 CC=0000\nnotb       A=ffffffff R=ffffff00 CCIN=0000 CC=0000\nnotl       A=ffffffff R=00000000 CCIN=0001 CC=0001\nnotw       A=ffffffff R=ffff0000 CCIN=0001 CC=0001\nnotb       A=ffffffff R=ffffff00 CCIN=0001 CC=0001\nnotl       A=ffffffff R=00000000 CCIN=0000 CC=0000\nnotw       A=ffffffff R=ffff0000 CCIN=0000 CC=0000\nnotb       A=ffffffff R=ffffff00 CCIN=0000 CC=0000\nnotl       A=ffffffff R=00000000 CCIN=0001 CC=0001\nnotw       A=ffffffff R=ffff0000 CCIN=0001 CC=0001\nnotb       A=ffffffff R=ffffff00 CCIN=0001 CC=0001\nnotl       A=7fffffff R=80000000 CCIN=0000 CC=0000\nnotw       A=7fffffff R=7fff0000 CCIN=0000 CC=0000\nnotb       A=7fffffff R=7fffff00 CCIN=0000 CC=0000\nnotl       A=7fffffff R=80000000 CCIN=0001 CC=0001\nnotw       A=7fffffff R=7fff0000 CCIN=0001 CC=0001\nnotb       A=7fffffff R=7fffff00 CCIN=0001 CC=0001\nnotl       A=7fffffff R=80000000 CCIN=0000 CC=0000\nnotw       A=7fffffff R=7fff0000 CCIN=0000 CC=0000\nnotb       A=7fffffff R=7fffff00 CCIN=0000 CC=0000\nnotl       A=7fffffff R=80000000 CCIN=0001 CC=0001\nnotw       A=7fffffff R=7fff0000 CCIN=0001 CC=0001\nnotb       A=7fffffff R=7fffff00 CCIN=0001 CC=0001\nnotl       A=7fffffff R=80000000 CCIN=0000 CC=0000\nnotw       A=7fffffff R=7fff0000 CCIN=0000 CC=0000\nnotb       A=7fffffff R=7fffff00 CCIN=0000 CC=0000\nnotl       A=7fffffff R=80000000 CCIN=0001 CC=0001\nnotw       A=7fffffff R=7fff0000 CCIN=0001 CC=0001\nnotb       A=7fffffff R=7fffff00 CCIN=0001 CC=0001\nnotl       A=80000000 R=7fffffff CCIN=0000 CC=0000\nnotw       A=80000000 R=8000ffff CCIN=0000 CC=0000\nnotb       A=80000000 R=800000ff CCIN=0000 CC=0000\nnotl       A=80000000 R=7fffffff CCIN=0001 CC=0001\nnotw       A=80000000 R=8000ffff CCIN=0001 CC=0001\nnotb       A=80000000 R=800000ff CCIN=0001 CC=0001\nnotl       A=80000000 R=7fffffff CCIN=0000 CC=0000\nnotw       A=80000000 R=8000ffff CCIN=0000 CC=0000\nnotb       A=80000000 R=800000ff CCIN=0000 CC=0000\nnotl       A=80000000 R=7fffffff CCIN=0001 CC=0001\nnotw       A=80000000 R=8000ffff CCIN=0001 CC=0001\nnotb       A=80000000 R=800000ff CCIN=0001 CC=0001\nnotl       A=80000000 R=7fffffff CCIN=0000 CC=0000\nnotw       A=80000000 R=8000ffff CCIN=0000 CC=0000\nnotb       A=80000000 R=800000ff CCIN=0000 CC=0000\nnotl       A=80000000 R=7fffffff CCIN=0001 CC=0001\nnotw       A=80000000 R=8000ffff CCIN=0001 CC=0001\nnotb       A=80000000 R=800000ff CCIN=0001 CC=0001\nnotl       A=12347fff R=edcb8000 CCIN=0000 CC=0000\nnotw       A=12347fff R=12348000 CCIN=0000 CC=0000\nnotb       A=12347fff R=12347f00 CCIN=0000 CC=0000\nnotl       A=12347fff R=edcb8000 CCIN=0001 CC=0001\nnotw       A=12347fff R=12348000 CCIN=0001 CC=0001\nnotb       A=12347fff R=12347f00 CCIN=0001 CC=0001\nnotl       A=12347fff R=edcb8000 CCIN=0000 CC=0000\nnotw       A=12347fff R=12348000 CCIN=0000 CC=0000\nnotb       A=12347fff R=12347f00 CCIN=0000 CC=0000\nnotl       A=12347fff R=edcb8000 CCIN=0001 CC=0001\nnotw       A=12347fff R=12348000 CCIN=0001 CC=0001\nnotb       A=12347fff R=12347f00 CCIN=0001 CC=0001\nnotl       A=12347fff R=edcb8000 CCIN=0000 CC=0000\nnotw       A=12347fff R=12348000 CCIN=0000 CC=0000\nnotb       A=12347fff R=12347f00 CCIN=0000 CC=0000\nnotl       A=12347fff R=edcb8000 CCIN=0001 CC=0001\nnotw       A=12347fff R=12348000 CCIN=0001 CC=0001\nnotb       A=12347fff R=12347f00 CCIN=0001 CC=0001\nnotl       A=12348000 R=edcb7fff CCIN=0000 CC=0000\nnotw       A=12348000 R=12347fff CCIN=0000 CC=0000\nnotb       A=12348000 R=123480ff CCIN=0000 CC=0000\nnotl       A=12348000 R=edcb7fff CCIN=0001 CC=0001\nnotw       A=12348000 R=12347fff CCIN=0001 CC=0001\nnotb       A=12348000 R=123480ff CCIN=0001 CC=0001\nnotl       A=12348000 R=edcb7fff CCIN=0000 CC=0000\nnotw       A=12348000 R=12347fff CCIN=0000 CC=0000\nnotb       A=12348000 R=123480ff CCIN=0000 CC=0000\nnotl       A=12348000 R=edcb7fff CCIN=0001 CC=0001\nnotw       A=12348000 R=12347fff CCIN=0001 CC=0001\nnotb       A=12348000 R=123480ff CCIN=0001 CC=0001\nnotl       A=12348000 R=edcb7fff CCIN=0000 CC=0000\nnotw       A=12348000 R=12347fff CCIN=0000 CC=0000\nnotb       A=12348000 R=123480ff CCIN=0000 CC=0000\nnotl       A=12348000 R=edcb7fff CCIN=0001 CC=0001\nnotw       A=12348000 R=12347fff CCIN=0001 CC=0001\nnotb       A=12348000 R=123480ff CCIN=0001 CC=0001\nnotl       A=12347f7f R=edcb8080 CCIN=0000 CC=0000\nnotw       A=12347f7f R=12348080 CCIN=0000 CC=0000\nnotb       A=12347f7f R=12347f80 CCIN=0000 CC=0000\nnotl       A=12347f7f R=edcb8080 CCIN=0001 CC=0001\nnotw       A=12347f7f R=12348080 CCIN=0001 CC=0001\nnotb       A=12347f7f R=12347f80 CCIN=0001 CC=0001\nnotl       A=12347f7f R=edcb8080 CCIN=0000 CC=0000\nnotw       A=12347f7f R=12348080 CCIN=0000 CC=0000\nnotb       A=12347f7f R=12347f80 CCIN=0000 CC=0000\nnotl       A=12347f7f R=edcb8080 CCIN=0001 CC=0001\nnotw       A=12347f7f R=12348080 CCIN=0001 CC=0001\nnotb       A=12347f7f R=12347f80 CCIN=0001 CC=0001\nnotl       A=12347f7f R=edcb8080 CCIN=0000 CC=0000\nnotw       A=12347f7f R=12348080 CCIN=0000 CC=0000\nnotb       A=12347f7f R=12347f80 CCIN=0000 CC=0000\nnotl       A=12347f7f R=edcb8080 CCIN=0001 CC=0001\nnotw       A=12347f7f R=12348080 CCIN=0001 CC=0001\nnotb       A=12347f7f R=12347f80 CCIN=0001 CC=0001\nnotl       A=12348080 R=edcb7f7f CCIN=0000 CC=0000\nnotw       A=12348080 R=12347f7f CCIN=0000 CC=0000\nnotb       A=12348080 R=1234807f CCIN=0000 CC=0000\nnotl       A=12348080 R=edcb7f7f CCIN=0001 CC=0001\nnotw       A=12348080 R=12347f7f CCIN=0001 CC=0001\nnotb       A=12348080 R=1234807f CCIN=0001 CC=0001\nnotl       A=12348080 R=edcb7f7f CCIN=0000 CC=0000\nnotw       A=12348080 R=12347f7f CCIN=0000 CC=0000\nnotb       A=12348080 R=1234807f CCIN=0000 CC=0000\nnotl       A=12348080 R=edcb7f7f CCIN=0001 CC=0001\nnotw       A=12348080 R=12347f7f CCIN=0001 CC=0001\nnotb       A=12348080 R=1234807f CCIN=0001 CC=0001\nnotl       A=12348080 R=edcb7f7f CCIN=0000 CC=0000\nnotw       A=12348080 R=12347f7f CCIN=0000 CC=0000\nnotb       A=12348080 R=1234807f CCIN=0000 CC=0000\nnotl       A=12348080 R=edcb7f7f CCIN=0001 CC=0001\nnotw       A=12348080 R=12347f7f CCIN=0001 CC=0001\nnotb       A=12348080 R=1234807f CCIN=0001 CC=0001\nshll       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshlw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshlb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshll       A=12345678 B=00000001 R=2468acf0 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000001 R=1234acf0 CCIN=0000 CC=0884\nshlb       A=12345678 B=00000001 R=123456f0 CCIN=0000 CC=0884\nshll       A=12345678 B=00000002 R=48d159e0 CCIN=0000 CC=0000\nshlw       A=12345678 B=00000002 R=123459e0 CCIN=0000 CC=0001\nshlb       A=12345678 B=00000002 R=123456e0 CCIN=0000 CC=0081\nshll       A=12345678 B=00000003 R=91a2b3c0 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000003 R=1234b3c0 CCIN=0000 CC=0084\nshlb       A=12345678 B=00000003 R=123456c0 CCIN=0000 CC=0085\nshll       A=12345678 B=00000004 R=23456780 CCIN=0000 CC=0001\nshlw       A=12345678 B=00000004 R=12346780 CCIN=0000 CC=0001\nshlb       A=12345678 B=00000004 R=12345680 CCIN=0000 CC=0081\nshll       A=12345678 B=00000005 R=468acf00 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000005 R=1234cf00 CCIN=0000 CC=0084\nshlb       A=12345678 B=00000005 R=12345600 CCIN=0000 CC=0045\nshll       A=12345678 B=00000006 R=8d159e00 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000006 R=12349e00 CCIN=0000 CC=0085\nshlb       A=12345678 B=00000006 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000007 R=1a2b3c00 CCIN=0000 CC=0005\nshlw       A=12345678 B=00000007 R=12343c00 CCIN=0000 CC=0005\nshlb       A=12345678 B=00000007 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000008 R=34567800 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000008 R=12347800 CCIN=0000 CC=0004\nshlb       A=12345678 B=00000008 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000009 R=68acf000 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000009 R=1234f000 CCIN=0000 CC=0084\nshlb       A=12345678 B=00000009 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000a R=d159e000 CCIN=0000 CC=0084\nshlw       A=12345678 B=0000000a R=1234e000 CCIN=0000 CC=0085\nshlb       A=12345678 B=0000000a R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000b R=a2b3c000 CCIN=0000 CC=0085\nshlw       A=12345678 B=0000000b R=1234c000 CCIN=0000 CC=0085\nshlb       A=12345678 B=0000000b R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000c R=45678000 CCIN=0000 CC=0005\nshlw       A=12345678 B=0000000c R=12348000 CCIN=0000 CC=0085\nshlb       A=12345678 B=0000000c R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000d R=8acf0000 CCIN=0000 CC=0084\nshlw       A=12345678 B=0000000d R=12340000 CCIN=0000 CC=0045\nshlb       A=12345678 B=0000000d R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000e R=159e0000 CCIN=0000 CC=0005\nshlw       A=12345678 B=0000000e R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000000e R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000000f R=2b3c0000 CCIN=0000 CC=0004\nshlw       A=12345678 B=0000000f R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000000f R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000010 R=56780000 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000010 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000010 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000011 R=acf00000 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000011 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000011 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000012 R=59e00000 CCIN=0000 CC=0005\nshlw       A=12345678 B=00000012 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000012 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000013 R=b3c00000 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000013 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000013 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000014 R=67800000 CCIN=0000 CC=0005\nshlw       A=12345678 B=00000014 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000014 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000015 R=cf000000 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000015 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000015 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000016 R=9e000000 CCIN=0000 CC=0085\nshlw       A=12345678 B=00000016 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000016 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000017 R=3c000000 CCIN=0000 CC=0005\nshlw       A=12345678 B=00000017 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000017 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000018 R=78000000 CCIN=0000 CC=0004\nshlw       A=12345678 B=00000018 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000018 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=00000019 R=f0000000 CCIN=0000 CC=0084\nshlw       A=12345678 B=00000019 R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=00000019 R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001a R=e0000000 CCIN=0000 CC=0085\nshlw       A=12345678 B=0000001a R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001a R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001b R=c0000000 CCIN=0000 CC=0085\nshlw       A=12345678 B=0000001b R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001b R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001c R=80000000 CCIN=0000 CC=0085\nshlw       A=12345678 B=0000001c R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001c R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001d R=00000000 CCIN=0000 CC=0045\nshlw       A=12345678 B=0000001d R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001d R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001e R=00000000 CCIN=0000 CC=0044\nshlw       A=12345678 B=0000001e R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001e R=12345600 CCIN=0000 CC=0044\nshll       A=12345678 B=0000001f R=00000000 CCIN=0000 CC=0044\nshlw       A=12345678 B=0000001f R=12340000 CCIN=0000 CC=0044\nshlb       A=12345678 B=0000001f R=12345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshlw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshlb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshll       A=82345679 B=00000001 R=0468acf2 CCIN=0000 CC=0801\nshlw       A=82345679 B=00000001 R=8234acf2 CCIN=0000 CC=0880\nshlb       A=82345679 B=00000001 R=823456f2 CCIN=0000 CC=0880\nshll       A=82345679 B=00000002 R=08d159e4 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000002 R=823459e4 CCIN=0000 CC=0005\nshlb       A=82345679 B=00000002 R=823456e4 CCIN=0000 CC=0085\nshll       A=82345679 B=00000003 R=11a2b3c8 CCIN=0000 CC=0000\nshlw       A=82345679 B=00000003 R=8234b3c8 CCIN=0000 CC=0080\nshlb       A=82345679 B=00000003 R=823456c8 CCIN=0000 CC=0081\nshll       A=82345679 B=00000004 R=23456790 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000004 R=82346790 CCIN=0000 CC=0005\nshlb       A=82345679 B=00000004 R=82345690 CCIN=0000 CC=0085\nshll       A=82345679 B=00000005 R=468acf20 CCIN=0000 CC=0000\nshlw       A=82345679 B=00000005 R=8234cf20 CCIN=0000 CC=0080\nshlb       A=82345679 B=00000005 R=82345620 CCIN=0000 CC=0001\nshll       A=82345679 B=00000006 R=8d159e40 CCIN=0000 CC=0080\nshlw       A=82345679 B=00000006 R=82349e40 CCIN=0000 CC=0081\nshlb       A=82345679 B=00000006 R=82345640 CCIN=0000 CC=0000\nshll       A=82345679 B=00000007 R=1a2b3c80 CCIN=0000 CC=0001\nshlw       A=82345679 B=00000007 R=82343c80 CCIN=0000 CC=0001\nshlb       A=82345679 B=00000007 R=82345680 CCIN=0000 CC=0080\nshll       A=82345679 B=00000008 R=34567900 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000008 R=82347900 CCIN=0000 CC=0004\nshlb       A=82345679 B=00000008 R=82345600 CCIN=0000 CC=0045\nshll       A=82345679 B=00000009 R=68acf200 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000009 R=8234f200 CCIN=0000 CC=0084\nshlb       A=82345679 B=00000009 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000a R=d159e400 CCIN=0000 CC=0084\nshlw       A=82345679 B=0000000a R=8234e400 CCIN=0000 CC=0085\nshlb       A=82345679 B=0000000a R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000b R=a2b3c800 CCIN=0000 CC=0085\nshlw       A=82345679 B=0000000b R=8234c800 CCIN=0000 CC=0085\nshlb       A=82345679 B=0000000b R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000c R=45679000 CCIN=0000 CC=0005\nshlw       A=82345679 B=0000000c R=82349000 CCIN=0000 CC=0085\nshlb       A=82345679 B=0000000c R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000d R=8acf2000 CCIN=0000 CC=0084\nshlw       A=82345679 B=0000000d R=82342000 CCIN=0000 CC=0005\nshlb       A=82345679 B=0000000d R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000e R=159e4000 CCIN=0000 CC=0005\nshlw       A=82345679 B=0000000e R=82344000 CCIN=0000 CC=0004\nshlb       A=82345679 B=0000000e R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000000f R=2b3c8000 CCIN=0000 CC=0004\nshlw       A=82345679 B=0000000f R=82348000 CCIN=0000 CC=0084\nshlb       A=82345679 B=0000000f R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000010 R=56790000 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000010 R=82340000 CCIN=0000 CC=0045\nshlb       A=82345679 B=00000010 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000011 R=acf20000 CCIN=0000 CC=0084\nshlw       A=82345679 B=00000011 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000011 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000012 R=59e40000 CCIN=0000 CC=0005\nshlw       A=82345679 B=00000012 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000012 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000013 R=b3c80000 CCIN=0000 CC=0084\nshlw       A=82345679 B=00000013 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000013 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000014 R=67900000 CCIN=0000 CC=0005\nshlw       A=82345679 B=00000014 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000014 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000015 R=cf200000 CCIN=0000 CC=0084\nshlw       A=82345679 B=00000015 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000015 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000016 R=9e400000 CCIN=0000 CC=0085\nshlw       A=82345679 B=00000016 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000016 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000017 R=3c800000 CCIN=0000 CC=0005\nshlw       A=82345679 B=00000017 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000017 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000018 R=79000000 CCIN=0000 CC=0004\nshlw       A=82345679 B=00000018 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000018 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=00000019 R=f2000000 CCIN=0000 CC=0084\nshlw       A=82345679 B=00000019 R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=00000019 R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001a R=e4000000 CCIN=0000 CC=0085\nshlw       A=82345679 B=0000001a R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001a R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001b R=c8000000 CCIN=0000 CC=0085\nshlw       A=82345679 B=0000001b R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001b R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001c R=90000000 CCIN=0000 CC=0085\nshlw       A=82345679 B=0000001c R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001c R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001d R=20000000 CCIN=0000 CC=0005\nshlw       A=82345679 B=0000001d R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001d R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001e R=40000000 CCIN=0000 CC=0004\nshlw       A=82345679 B=0000001e R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001e R=82345600 CCIN=0000 CC=0044\nshll       A=82345679 B=0000001f R=80000000 CCIN=0000 CC=0084\nshlw       A=82345679 B=0000001f R=82340000 CCIN=0000 CC=0044\nshlb       A=82345679 B=0000001f R=82345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshrw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshrb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nshrl       A=12345678 B=00000001 R=091a2b3c CCIN=0000 CC=0004\nshrw       A=12345678 B=00000001 R=12342b3c CCIN=0000 CC=0004\nshrb       A=12345678 B=00000001 R=1234563c CCIN=0000 CC=0004\nshrl       A=12345678 B=00000002 R=048d159e CCIN=0000 CC=0000\nshrw       A=12345678 B=00000002 R=1234159e CCIN=0000 CC=0000\nshrb       A=12345678 B=00000002 R=1234561e CCIN=0000 CC=0004\nshrl       A=12345678 B=00000003 R=02468acf CCIN=0000 CC=0004\nshrw       A=12345678 B=00000003 R=12340acf CCIN=0000 CC=0004\nshrb       A=12345678 B=00000003 R=1234560f CCIN=0000 CC=0004\nshrl       A=12345678 B=00000004 R=01234567 CCIN=0000 CC=0001\nshrw       A=12345678 B=00000004 R=12340567 CCIN=0000 CC=0001\nshrb       A=12345678 B=00000004 R=12345607 CCIN=0000 CC=0001\nshrl       A=12345678 B=00000005 R=0091a2b3 CCIN=0000 CC=0001\nshrw       A=12345678 B=00000005 R=123402b3 CCIN=0000 CC=0001\nshrb       A=12345678 B=00000005 R=12345603 CCIN=0000 CC=0005\nshrl       A=12345678 B=00000006 R=0048d159 CCIN=0000 CC=0005\nshrw       A=12345678 B=00000006 R=12340159 CCIN=0000 CC=0005\nshrb       A=12345678 B=00000006 R=12345601 CCIN=0000 CC=0001\nshrl       A=12345678 B=00000007 R=002468ac CCIN=0000 CC=0005\nshrw       A=12345678 B=00000007 R=123400ac CCIN=0000 CC=0005\nshrb       A=12345678 B=00000007 R=12345600 CCIN=0000 CC=0045\nshrl       A=12345678 B=00000008 R=00123456 CCIN=0000 CC=0004\nshrw       A=12345678 B=00000008 R=12340056 CCIN=0000 CC=0004\nshrb       A=12345678 B=00000008 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000009 R=00091a2b CCIN=0000 CC=0004\nshrw       A=12345678 B=00000009 R=1234002b CCIN=0000 CC=0004\nshrb       A=12345678 B=00000009 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000a R=00048d15 CCIN=0000 CC=0001\nshrw       A=12345678 B=0000000a R=12340015 CCIN=0000 CC=0001\nshrb       A=12345678 B=0000000a R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000b R=0002468a CCIN=0000 CC=0001\nshrw       A=12345678 B=0000000b R=1234000a CCIN=0000 CC=0005\nshrb       A=12345678 B=0000000b R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000c R=00012345 CCIN=0000 CC=0000\nshrw       A=12345678 B=0000000c R=12340005 CCIN=0000 CC=0004\nshrb       A=12345678 B=0000000c R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000d R=000091a2 CCIN=0000 CC=0001\nshrw       A=12345678 B=0000000d R=12340002 CCIN=0000 CC=0001\nshrb       A=12345678 B=0000000d R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000e R=000048d1 CCIN=0000 CC=0004\nshrw       A=12345678 B=0000000e R=12340001 CCIN=0000 CC=0000\nshrb       A=12345678 B=0000000e R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000000f R=00002468 CCIN=0000 CC=0001\nshrw       A=12345678 B=0000000f R=12340000 CCIN=0000 CC=0045\nshrb       A=12345678 B=0000000f R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000010 R=00001234 CCIN=0000 CC=0000\nshrw       A=12345678 B=00000010 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000010 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000011 R=0000091a CCIN=0000 CC=0000\nshrw       A=12345678 B=00000011 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000011 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000012 R=0000048d CCIN=0000 CC=0004\nshrw       A=12345678 B=00000012 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000012 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000013 R=00000246 CCIN=0000 CC=0001\nshrw       A=12345678 B=00000013 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000013 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000014 R=00000123 CCIN=0000 CC=0000\nshrw       A=12345678 B=00000014 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000014 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000015 R=00000091 CCIN=0000 CC=0001\nshrw       A=12345678 B=00000015 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000015 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000016 R=00000048 CCIN=0000 CC=0005\nshrw       A=12345678 B=00000016 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000016 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000017 R=00000024 CCIN=0000 CC=0004\nshrw       A=12345678 B=00000017 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000017 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000018 R=00000012 CCIN=0000 CC=0004\nshrw       A=12345678 B=00000018 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000018 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=00000019 R=00000009 CCIN=0000 CC=0004\nshrw       A=12345678 B=00000019 R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=00000019 R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001a R=00000004 CCIN=0000 CC=0001\nshrw       A=12345678 B=0000001a R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001a R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001b R=00000002 CCIN=0000 CC=0000\nshrw       A=12345678 B=0000001b R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001b R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001c R=00000001 CCIN=0000 CC=0000\nshrw       A=12345678 B=0000001c R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001c R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001d R=00000000 CCIN=0000 CC=0045\nshrw       A=12345678 B=0000001d R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001d R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001e R=00000000 CCIN=0000 CC=0044\nshrw       A=12345678 B=0000001e R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001e R=12345600 CCIN=0000 CC=0044\nshrl       A=12345678 B=0000001f R=00000000 CCIN=0000 CC=0044\nshrw       A=12345678 B=0000001f R=12340000 CCIN=0000 CC=0044\nshrb       A=12345678 B=0000001f R=12345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshrw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshrb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nshrl       A=82345679 B=00000001 R=411a2b3c CCIN=0000 CC=0805\nshrw       A=82345679 B=00000001 R=82342b3c CCIN=0000 CC=0005\nshrb       A=82345679 B=00000001 R=8234563c CCIN=0000 CC=0005\nshrl       A=82345679 B=00000002 R=208d159e CCIN=0000 CC=0000\nshrw       A=82345679 B=00000002 R=8234159e CCIN=0000 CC=0000\nshrb       A=82345679 B=00000002 R=8234561e CCIN=0000 CC=0004\nshrl       A=82345679 B=00000003 R=10468acf CCIN=0000 CC=0004\nshrw       A=82345679 B=00000003 R=82340acf CCIN=0000 CC=0004\nshrb       A=82345679 B=00000003 R=8234560f CCIN=0000 CC=0004\nshrl       A=82345679 B=00000004 R=08234567 CCIN=0000 CC=0001\nshrw       A=82345679 B=00000004 R=82340567 CCIN=0000 CC=0001\nshrb       A=82345679 B=00000004 R=82345607 CCIN=0000 CC=0001\nshrl       A=82345679 B=00000005 R=0411a2b3 CCIN=0000 CC=0001\nshrw       A=82345679 B=00000005 R=823402b3 CCIN=0000 CC=0001\nshrb       A=82345679 B=00000005 R=82345603 CCIN=0000 CC=0005\nshrl       A=82345679 B=00000006 R=0208d159 CCIN=0000 CC=0005\nshrw       A=82345679 B=00000006 R=82340159 CCIN=0000 CC=0005\nshrb       A=82345679 B=00000006 R=82345601 CCIN=0000 CC=0001\nshrl       A=82345679 B=00000007 R=010468ac CCIN=0000 CC=0005\nshrw       A=82345679 B=00000007 R=823400ac CCIN=0000 CC=0005\nshrb       A=82345679 B=00000007 R=82345600 CCIN=0000 CC=0045\nshrl       A=82345679 B=00000008 R=00823456 CCIN=0000 CC=0004\nshrw       A=82345679 B=00000008 R=82340056 CCIN=0000 CC=0004\nshrb       A=82345679 B=00000008 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000009 R=00411a2b CCIN=0000 CC=0004\nshrw       A=82345679 B=00000009 R=8234002b CCIN=0000 CC=0004\nshrb       A=82345679 B=00000009 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000a R=00208d15 CCIN=0000 CC=0001\nshrw       A=82345679 B=0000000a R=82340015 CCIN=0000 CC=0001\nshrb       A=82345679 B=0000000a R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000b R=0010468a CCIN=0000 CC=0001\nshrw       A=82345679 B=0000000b R=8234000a CCIN=0000 CC=0005\nshrb       A=82345679 B=0000000b R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000c R=00082345 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000000c R=82340005 CCIN=0000 CC=0004\nshrb       A=82345679 B=0000000c R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000d R=000411a2 CCIN=0000 CC=0001\nshrw       A=82345679 B=0000000d R=82340002 CCIN=0000 CC=0001\nshrb       A=82345679 B=0000000d R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000e R=000208d1 CCIN=0000 CC=0004\nshrw       A=82345679 B=0000000e R=82340001 CCIN=0000 CC=0000\nshrb       A=82345679 B=0000000e R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000000f R=00010468 CCIN=0000 CC=0001\nshrw       A=82345679 B=0000000f R=82340000 CCIN=0000 CC=0045\nshrb       A=82345679 B=0000000f R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000010 R=00008234 CCIN=0000 CC=0000\nshrw       A=82345679 B=00000010 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000010 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000011 R=0000411a CCIN=0000 CC=0000\nshrw       A=82345679 B=00000011 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000011 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000012 R=0000208d CCIN=0000 CC=0004\nshrw       A=82345679 B=00000012 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000012 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000013 R=00001046 CCIN=0000 CC=0001\nshrw       A=82345679 B=00000013 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000013 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000014 R=00000823 CCIN=0000 CC=0000\nshrw       A=82345679 B=00000014 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000014 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000015 R=00000411 CCIN=0000 CC=0005\nshrw       A=82345679 B=00000015 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000015 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000016 R=00000208 CCIN=0000 CC=0001\nshrw       A=82345679 B=00000016 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000016 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000017 R=00000104 CCIN=0000 CC=0000\nshrw       A=82345679 B=00000017 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000017 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000018 R=00000082 CCIN=0000 CC=0004\nshrw       A=82345679 B=00000018 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000018 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=00000019 R=00000041 CCIN=0000 CC=0004\nshrw       A=82345679 B=00000019 R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=00000019 R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001a R=00000020 CCIN=0000 CC=0001\nshrw       A=82345679 B=0000001a R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001a R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001b R=00000010 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000001b R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001b R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001c R=00000008 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000001c R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001c R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001d R=00000004 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000001d R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001d R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001e R=00000002 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000001e R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001e R=82345600 CCIN=0000 CC=0044\nshrl       A=82345679 B=0000001f R=00000001 CCIN=0000 CC=0000\nshrw       A=82345679 B=0000001f R=82340000 CCIN=0000 CC=0044\nshrb       A=82345679 B=0000001f R=82345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nsarw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nsarb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nsarl       A=12345678 B=00000001 R=091a2b3c CCIN=0000 CC=0004\nsarw       A=12345678 B=00000001 R=12342b3c CCIN=0000 CC=0004\nsarb       A=12345678 B=00000001 R=1234563c CCIN=0000 CC=0004\nsarl       A=12345678 B=00000002 R=048d159e CCIN=0000 CC=0000\nsarw       A=12345678 B=00000002 R=1234159e CCIN=0000 CC=0000\nsarb       A=12345678 B=00000002 R=1234561e CCIN=0000 CC=0004\nsarl       A=12345678 B=00000003 R=02468acf CCIN=0000 CC=0004\nsarw       A=12345678 B=00000003 R=12340acf CCIN=0000 CC=0004\nsarb       A=12345678 B=00000003 R=1234560f CCIN=0000 CC=0004\nsarl       A=12345678 B=00000004 R=01234567 CCIN=0000 CC=0001\nsarw       A=12345678 B=00000004 R=12340567 CCIN=0000 CC=0001\nsarb       A=12345678 B=00000004 R=12345607 CCIN=0000 CC=0001\nsarl       A=12345678 B=00000005 R=0091a2b3 CCIN=0000 CC=0001\nsarw       A=12345678 B=00000005 R=123402b3 CCIN=0000 CC=0001\nsarb       A=12345678 B=00000005 R=12345603 CCIN=0000 CC=0005\nsarl       A=12345678 B=00000006 R=0048d159 CCIN=0000 CC=0005\nsarw       A=12345678 B=00000006 R=12340159 CCIN=0000 CC=0005\nsarb       A=12345678 B=00000006 R=12345601 CCIN=0000 CC=0001\nsarl       A=12345678 B=00000007 R=002468ac CCIN=0000 CC=0005\nsarw       A=12345678 B=00000007 R=123400ac CCIN=0000 CC=0005\nsarb       A=12345678 B=00000007 R=12345600 CCIN=0000 CC=0045\nsarl       A=12345678 B=00000008 R=00123456 CCIN=0000 CC=0004\nsarw       A=12345678 B=00000008 R=12340056 CCIN=0000 CC=0004\nsarb       A=12345678 B=00000008 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000009 R=00091a2b CCIN=0000 CC=0004\nsarw       A=12345678 B=00000009 R=1234002b CCIN=0000 CC=0004\nsarb       A=12345678 B=00000009 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000a R=00048d15 CCIN=0000 CC=0001\nsarw       A=12345678 B=0000000a R=12340015 CCIN=0000 CC=0001\nsarb       A=12345678 B=0000000a R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000b R=0002468a CCIN=0000 CC=0001\nsarw       A=12345678 B=0000000b R=1234000a CCIN=0000 CC=0005\nsarb       A=12345678 B=0000000b R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000c R=00012345 CCIN=0000 CC=0000\nsarw       A=12345678 B=0000000c R=12340005 CCIN=0000 CC=0004\nsarb       A=12345678 B=0000000c R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000d R=000091a2 CCIN=0000 CC=0001\nsarw       A=12345678 B=0000000d R=12340002 CCIN=0000 CC=0001\nsarb       A=12345678 B=0000000d R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000e R=000048d1 CCIN=0000 CC=0004\nsarw       A=12345678 B=0000000e R=12340001 CCIN=0000 CC=0000\nsarb       A=12345678 B=0000000e R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000000f R=00002468 CCIN=0000 CC=0001\nsarw       A=12345678 B=0000000f R=12340000 CCIN=0000 CC=0045\nsarb       A=12345678 B=0000000f R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000010 R=00001234 CCIN=0000 CC=0000\nsarw       A=12345678 B=00000010 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000010 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000011 R=0000091a CCIN=0000 CC=0000\nsarw       A=12345678 B=00000011 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000011 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000012 R=0000048d CCIN=0000 CC=0004\nsarw       A=12345678 B=00000012 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000012 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000013 R=00000246 CCIN=0000 CC=0001\nsarw       A=12345678 B=00000013 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000013 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000014 R=00000123 CCIN=0000 CC=0000\nsarw       A=12345678 B=00000014 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000014 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000015 R=00000091 CCIN=0000 CC=0001\nsarw       A=12345678 B=00000015 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000015 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000016 R=00000048 CCIN=0000 CC=0005\nsarw       A=12345678 B=00000016 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000016 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000017 R=00000024 CCIN=0000 CC=0004\nsarw       A=12345678 B=00000017 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000017 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000018 R=00000012 CCIN=0000 CC=0004\nsarw       A=12345678 B=00000018 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000018 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=00000019 R=00000009 CCIN=0000 CC=0004\nsarw       A=12345678 B=00000019 R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=00000019 R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001a R=00000004 CCIN=0000 CC=0001\nsarw       A=12345678 B=0000001a R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001a R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001b R=00000002 CCIN=0000 CC=0000\nsarw       A=12345678 B=0000001b R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001b R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001c R=00000001 CCIN=0000 CC=0000\nsarw       A=12345678 B=0000001c R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001c R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001d R=00000000 CCIN=0000 CC=0045\nsarw       A=12345678 B=0000001d R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001d R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001e R=00000000 CCIN=0000 CC=0044\nsarw       A=12345678 B=0000001e R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001e R=12345600 CCIN=0000 CC=0044\nsarl       A=12345678 B=0000001f R=00000000 CCIN=0000 CC=0044\nsarw       A=12345678 B=0000001f R=12340000 CCIN=0000 CC=0044\nsarb       A=12345678 B=0000001f R=12345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nsarw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nsarb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nsarl       A=82345679 B=00000001 R=c11a2b3c CCIN=0000 CC=0085\nsarw       A=82345679 B=00000001 R=82342b3c CCIN=0000 CC=0005\nsarb       A=82345679 B=00000001 R=8234563c CCIN=0000 CC=0005\nsarl       A=82345679 B=00000002 R=e08d159e CCIN=0000 CC=0080\nsarw       A=82345679 B=00000002 R=8234159e CCIN=0000 CC=0000\nsarb       A=82345679 B=00000002 R=8234561e CCIN=0000 CC=0004\nsarl       A=82345679 B=00000003 R=f0468acf CCIN=0000 CC=0084\nsarw       A=82345679 B=00000003 R=82340acf CCIN=0000 CC=0004\nsarb       A=82345679 B=00000003 R=8234560f CCIN=0000 CC=0004\nsarl       A=82345679 B=00000004 R=f8234567 CCIN=0000 CC=0081\nsarw       A=82345679 B=00000004 R=82340567 CCIN=0000 CC=0001\nsarb       A=82345679 B=00000004 R=82345607 CCIN=0000 CC=0001\nsarl       A=82345679 B=00000005 R=fc11a2b3 CCIN=0000 CC=0081\nsarw       A=82345679 B=00000005 R=823402b3 CCIN=0000 CC=0001\nsarb       A=82345679 B=00000005 R=82345603 CCIN=0000 CC=0005\nsarl       A=82345679 B=00000006 R=fe08d159 CCIN=0000 CC=0085\nsarw       A=82345679 B=00000006 R=82340159 CCIN=0000 CC=0005\nsarb       A=82345679 B=00000006 R=82345601 CCIN=0000 CC=0001\nsarl       A=82345679 B=00000007 R=ff0468ac CCIN=0000 CC=0085\nsarw       A=82345679 B=00000007 R=823400ac CCIN=0000 CC=0005\nsarb       A=82345679 B=00000007 R=82345600 CCIN=0000 CC=0045\nsarl       A=82345679 B=00000008 R=ff823456 CCIN=0000 CC=0084\nsarw       A=82345679 B=00000008 R=82340056 CCIN=0000 CC=0004\nsarb       A=82345679 B=00000008 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000009 R=ffc11a2b CCIN=0000 CC=0084\nsarw       A=82345679 B=00000009 R=8234002b CCIN=0000 CC=0004\nsarb       A=82345679 B=00000009 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000a R=ffe08d15 CCIN=0000 CC=0081\nsarw       A=82345679 B=0000000a R=82340015 CCIN=0000 CC=0001\nsarb       A=82345679 B=0000000a R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000b R=fff0468a CCIN=0000 CC=0081\nsarw       A=82345679 B=0000000b R=8234000a CCIN=0000 CC=0005\nsarb       A=82345679 B=0000000b R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000c R=fff82345 CCIN=0000 CC=0080\nsarw       A=82345679 B=0000000c R=82340005 CCIN=0000 CC=0004\nsarb       A=82345679 B=0000000c R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000d R=fffc11a2 CCIN=0000 CC=0081\nsarw       A=82345679 B=0000000d R=82340002 CCIN=0000 CC=0001\nsarb       A=82345679 B=0000000d R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000e R=fffe08d1 CCIN=0000 CC=0084\nsarw       A=82345679 B=0000000e R=82340001 CCIN=0000 CC=0000\nsarb       A=82345679 B=0000000e R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000000f R=ffff0468 CCIN=0000 CC=0081\nsarw       A=82345679 B=0000000f R=82340000 CCIN=0000 CC=0045\nsarb       A=82345679 B=0000000f R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000010 R=ffff8234 CCIN=0000 CC=0080\nsarw       A=82345679 B=00000010 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000010 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000011 R=ffffc11a CCIN=0000 CC=0080\nsarw       A=82345679 B=00000011 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000011 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000012 R=ffffe08d CCIN=0000 CC=0084\nsarw       A=82345679 B=00000012 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000012 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000013 R=fffff046 CCIN=0000 CC=0081\nsarw       A=82345679 B=00000013 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000013 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000014 R=fffff823 CCIN=0000 CC=0080\nsarw       A=82345679 B=00000014 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000014 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000015 R=fffffc11 CCIN=0000 CC=0085\nsarw       A=82345679 B=00000015 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000015 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000016 R=fffffe08 CCIN=0000 CC=0081\nsarw       A=82345679 B=00000016 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000016 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000017 R=ffffff04 CCIN=0000 CC=0080\nsarw       A=82345679 B=00000017 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000017 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000018 R=ffffff82 CCIN=0000 CC=0084\nsarw       A=82345679 B=00000018 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000018 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=00000019 R=ffffffc1 CCIN=0000 CC=0080\nsarw       A=82345679 B=00000019 R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=00000019 R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001a R=ffffffe0 CCIN=0000 CC=0081\nsarw       A=82345679 B=0000001a R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001a R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001b R=fffffff0 CCIN=0000 CC=0084\nsarw       A=82345679 B=0000001b R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001b R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001c R=fffffff8 CCIN=0000 CC=0080\nsarw       A=82345679 B=0000001c R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001c R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001d R=fffffffc CCIN=0000 CC=0084\nsarw       A=82345679 B=0000001d R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001d R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001e R=fffffffe CCIN=0000 CC=0080\nsarw       A=82345679 B=0000001e R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001e R=82345600 CCIN=0000 CC=0044\nsarl       A=82345679 B=0000001f R=ffffffff CCIN=0000 CC=0084\nsarw       A=82345679 B=0000001f R=82340000 CCIN=0000 CC=0044\nsarb       A=82345679 B=0000001f R=82345600 CCIN=0000 CC=0044\nroll       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nroll       A=12345678 B=00000001 R=2468acf0 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000001 R=1234acf0 CCIN=0000 CC=0800\nrolb       A=12345678 B=00000001 R=123456f0 CCIN=0000 CC=0800\nroll       A=12345678 B=00000002 R=48d159e0 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000002 R=123459e1 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000002 R=123456e1 CCIN=0000 CC=0001\nroll       A=12345678 B=00000003 R=91a2b3c0 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000003 R=1234b3c2 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000003 R=123456c3 CCIN=0000 CC=0001\nroll       A=12345678 B=00000004 R=23456781 CCIN=0000 CC=0001\nrolw       A=12345678 B=00000004 R=12346785 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000004 R=12345687 CCIN=0000 CC=0001\nroll       A=12345678 B=00000005 R=468acf02 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000005 R=1234cf0a CCIN=0000 CC=0000\nrolb       A=12345678 B=00000005 R=1234560f CCIN=0000 CC=0001\nroll       A=12345678 B=00000006 R=8d159e04 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000006 R=12349e15 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000006 R=1234561e CCIN=0000 CC=0000\nroll       A=12345678 B=00000007 R=1a2b3c09 CCIN=0000 CC=0001\nrolw       A=12345678 B=00000007 R=12343c2b CCIN=0000 CC=0001\nrolb       A=12345678 B=00000007 R=1234563c CCIN=0000 CC=0000\nroll       A=12345678 B=00000008 R=34567812 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000008 R=12347856 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nroll       A=12345678 B=00000009 R=68acf024 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000009 R=1234f0ac CCIN=0000 CC=0000\nrolb       A=12345678 B=00000009 R=123456f0 CCIN=0000 CC=0000\nroll       A=12345678 B=0000000a R=d159e048 CCIN=0000 CC=0000\nrolw       A=12345678 B=0000000a R=1234e159 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000000a R=123456e1 CCIN=0000 CC=0001\nroll       A=12345678 B=0000000b R=a2b3c091 CCIN=0000 CC=0001\nrolw       A=12345678 B=0000000b R=1234c2b3 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000000b R=123456c3 CCIN=0000 CC=0001\nroll       A=12345678 B=0000000c R=45678123 CCIN=0000 CC=0001\nrolw       A=12345678 B=0000000c R=12348567 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000000c R=12345687 CCIN=0000 CC=0001\nroll       A=12345678 B=0000000d R=8acf0246 CCIN=0000 CC=0000\nrolw       A=12345678 B=0000000d R=12340acf CCIN=0000 CC=0001\nrolb       A=12345678 B=0000000d R=1234560f CCIN=0000 CC=0001\nroll       A=12345678 B=0000000e R=159e048d CCIN=0000 CC=0001\nrolw       A=12345678 B=0000000e R=1234159e CCIN=0000 CC=0000\nrolb       A=12345678 B=0000000e R=1234561e CCIN=0000 CC=0000\nroll       A=12345678 B=0000000f R=2b3c091a CCIN=0000 CC=0000\nrolw       A=12345678 B=0000000f R=12342b3c CCIN=0000 CC=0000\nrolb       A=12345678 B=0000000f R=1234563c CCIN=0000 CC=0000\nroll       A=12345678 B=00000010 R=56781234 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nroll       A=12345678 B=00000011 R=acf02468 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000011 R=1234acf0 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000011 R=123456f0 CCIN=0000 CC=0000\nroll       A=12345678 B=00000012 R=59e048d1 CCIN=0000 CC=0001\nrolw       A=12345678 B=00000012 R=123459e1 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000012 R=123456e1 CCIN=0000 CC=0001\nroll       A=12345678 B=00000013 R=b3c091a2 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000013 R=1234b3c2 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000013 R=123456c3 CCIN=0000 CC=0001\nroll       A=12345678 B=00000014 R=67812345 CCIN=0000 CC=0001\nrolw       A=12345678 B=00000014 R=12346785 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000014 R=12345687 CCIN=0000 CC=0001\nroll       A=12345678 B=00000015 R=cf02468a CCIN=0000 CC=0000\nrolw       A=12345678 B=00000015 R=1234cf0a CCIN=0000 CC=0000\nrolb       A=12345678 B=00000015 R=1234560f CCIN=0000 CC=0001\nroll       A=12345678 B=00000016 R=9e048d15 CCIN=0000 CC=0001\nrolw       A=12345678 B=00000016 R=12349e15 CCIN=0000 CC=0001\nrolb       A=12345678 B=00000016 R=1234561e CCIN=0000 CC=0000\nroll       A=12345678 B=00000017 R=3c091a2b CCIN=0000 CC=0001\nrolw       A=12345678 B=00000017 R=12343c2b CCIN=0000 CC=0001\nrolb       A=12345678 B=00000017 R=1234563c CCIN=0000 CC=0000\nroll       A=12345678 B=00000018 R=78123456 CCIN=0000 CC=0000\nrolw       A=12345678 B=00000018 R=12347856 CCIN=0000 CC=0000\nrolb       A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nroll       A=12345678 B=00000019 R=f02468ac CCIN=0000 CC=0000\nrolw       A=12345678 B=00000019 R=1234f0ac CCIN=0000 CC=0000\nrolb       A=12345678 B=00000019 R=123456f0 CCIN=0000 CC=0000\nroll       A=12345678 B=0000001a R=e048d159 CCIN=0000 CC=0001\nrolw       A=12345678 B=0000001a R=1234e159 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000001a R=123456e1 CCIN=0000 CC=0001\nroll       A=12345678 B=0000001b R=c091a2b3 CCIN=0000 CC=0001\nrolw       A=12345678 B=0000001b R=1234c2b3 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000001b R=123456c3 CCIN=0000 CC=0001\nroll       A=12345678 B=0000001c R=81234567 CCIN=0000 CC=0001\nrolw       A=12345678 B=0000001c R=12348567 CCIN=0000 CC=0001\nrolb       A=12345678 B=0000001c R=12345687 CCIN=0000 CC=0001\nroll       A=12345678 B=0000001d R=02468acf CCIN=0000 CC=0001\nrolw       A=12345678 B=0000001d R=12340acf CCIN=0000 CC=0001\nrolb       A=12345678 B=0000001d R=1234560f CCIN=0000 CC=0001\nroll       A=12345678 B=0000001e R=048d159e CCIN=0000 CC=0000\nrolw       A=12345678 B=0000001e R=1234159e CCIN=0000 CC=0000\nrolb       A=12345678 B=0000001e R=1234561e CCIN=0000 CC=0000\nroll       A=12345678 B=0000001f R=091a2b3c CCIN=0000 CC=0000\nrolw       A=12345678 B=0000001f R=12342b3c CCIN=0000 CC=0000\nrolb       A=12345678 B=0000001f R=1234563c CCIN=0000 CC=0000\nroll       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrolb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nroll       A=82345679 B=00000001 R=0468acf3 CCIN=0000 CC=0801\nrolw       A=82345679 B=00000001 R=8234acf2 CCIN=0000 CC=0800\nrolb       A=82345679 B=00000001 R=823456f2 CCIN=0000 CC=0800\nroll       A=82345679 B=00000002 R=08d159e6 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000002 R=823459e5 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000002 R=823456e5 CCIN=0000 CC=0001\nroll       A=82345679 B=00000003 R=11a2b3cc CCIN=0000 CC=0000\nrolw       A=82345679 B=00000003 R=8234b3ca CCIN=0000 CC=0000\nrolb       A=82345679 B=00000003 R=823456cb CCIN=0000 CC=0001\nroll       A=82345679 B=00000004 R=23456798 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000004 R=82346795 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000004 R=82345697 CCIN=0000 CC=0001\nroll       A=82345679 B=00000005 R=468acf30 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000005 R=8234cf2a CCIN=0000 CC=0000\nrolb       A=82345679 B=00000005 R=8234562f CCIN=0000 CC=0001\nroll       A=82345679 B=00000006 R=8d159e60 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000006 R=82349e55 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000006 R=8234565e CCIN=0000 CC=0000\nroll       A=82345679 B=00000007 R=1a2b3cc1 CCIN=0000 CC=0001\nrolw       A=82345679 B=00000007 R=82343cab CCIN=0000 CC=0001\nrolb       A=82345679 B=00000007 R=823456bc CCIN=0000 CC=0000\nroll       A=82345679 B=00000008 R=34567982 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000008 R=82347956 CCIN=0000 CC=0000\nrolb       A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0001\nroll       A=82345679 B=00000009 R=68acf304 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000009 R=8234f2ac CCIN=0000 CC=0000\nrolb       A=82345679 B=00000009 R=823456f2 CCIN=0000 CC=0000\nroll       A=82345679 B=0000000a R=d159e608 CCIN=0000 CC=0000\nrolw       A=82345679 B=0000000a R=8234e559 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000000a R=823456e5 CCIN=0000 CC=0001\nroll       A=82345679 B=0000000b R=a2b3cc11 CCIN=0000 CC=0001\nrolw       A=82345679 B=0000000b R=8234cab3 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000000b R=823456cb CCIN=0000 CC=0001\nroll       A=82345679 B=0000000c R=45679823 CCIN=0000 CC=0001\nrolw       A=82345679 B=0000000c R=82349567 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000000c R=82345697 CCIN=0000 CC=0001\nroll       A=82345679 B=0000000d R=8acf3046 CCIN=0000 CC=0000\nrolw       A=82345679 B=0000000d R=82342acf CCIN=0000 CC=0001\nrolb       A=82345679 B=0000000d R=8234562f CCIN=0000 CC=0001\nroll       A=82345679 B=0000000e R=159e608d CCIN=0000 CC=0001\nrolw       A=82345679 B=0000000e R=8234559e CCIN=0000 CC=0000\nrolb       A=82345679 B=0000000e R=8234565e CCIN=0000 CC=0000\nroll       A=82345679 B=0000000f R=2b3cc11a CCIN=0000 CC=0000\nrolw       A=82345679 B=0000000f R=8234ab3c CCIN=0000 CC=0000\nrolb       A=82345679 B=0000000f R=823456bc CCIN=0000 CC=0000\nroll       A=82345679 B=00000010 R=56798234 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0001\nroll       A=82345679 B=00000011 R=acf30468 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000011 R=8234acf2 CCIN=0000 CC=0000\nrolb       A=82345679 B=00000011 R=823456f2 CCIN=0000 CC=0000\nroll       A=82345679 B=00000012 R=59e608d1 CCIN=0000 CC=0001\nrolw       A=82345679 B=00000012 R=823459e5 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000012 R=823456e5 CCIN=0000 CC=0001\nroll       A=82345679 B=00000013 R=b3cc11a2 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000013 R=8234b3ca CCIN=0000 CC=0000\nrolb       A=82345679 B=00000013 R=823456cb CCIN=0000 CC=0001\nroll       A=82345679 B=00000014 R=67982345 CCIN=0000 CC=0001\nrolw       A=82345679 B=00000014 R=82346795 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000014 R=82345697 CCIN=0000 CC=0001\nroll       A=82345679 B=00000015 R=cf30468a CCIN=0000 CC=0000\nrolw       A=82345679 B=00000015 R=8234cf2a CCIN=0000 CC=0000\nrolb       A=82345679 B=00000015 R=8234562f CCIN=0000 CC=0001\nroll       A=82345679 B=00000016 R=9e608d15 CCIN=0000 CC=0001\nrolw       A=82345679 B=00000016 R=82349e55 CCIN=0000 CC=0001\nrolb       A=82345679 B=00000016 R=8234565e CCIN=0000 CC=0000\nroll       A=82345679 B=00000017 R=3cc11a2b CCIN=0000 CC=0001\nrolw       A=82345679 B=00000017 R=82343cab CCIN=0000 CC=0001\nrolb       A=82345679 B=00000017 R=823456bc CCIN=0000 CC=0000\nroll       A=82345679 B=00000018 R=79823456 CCIN=0000 CC=0000\nrolw       A=82345679 B=00000018 R=82347956 CCIN=0000 CC=0000\nrolb       A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0001\nroll       A=82345679 B=00000019 R=f30468ac CCIN=0000 CC=0000\nrolw       A=82345679 B=00000019 R=8234f2ac CCIN=0000 CC=0000\nrolb       A=82345679 B=00000019 R=823456f2 CCIN=0000 CC=0000\nroll       A=82345679 B=0000001a R=e608d159 CCIN=0000 CC=0001\nrolw       A=82345679 B=0000001a R=8234e559 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000001a R=823456e5 CCIN=0000 CC=0001\nroll       A=82345679 B=0000001b R=cc11a2b3 CCIN=0000 CC=0001\nrolw       A=82345679 B=0000001b R=8234cab3 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000001b R=823456cb CCIN=0000 CC=0001\nroll       A=82345679 B=0000001c R=98234567 CCIN=0000 CC=0001\nrolw       A=82345679 B=0000001c R=82349567 CCIN=0000 CC=0001\nrolb       A=82345679 B=0000001c R=82345697 CCIN=0000 CC=0001\nroll       A=82345679 B=0000001d R=30468acf CCIN=0000 CC=0001\nrolw       A=82345679 B=0000001d R=82342acf CCIN=0000 CC=0001\nrolb       A=82345679 B=0000001d R=8234562f CCIN=0000 CC=0001\nroll       A=82345679 B=0000001e R=608d159e CCIN=0000 CC=0000\nrolw       A=82345679 B=0000001e R=8234559e CCIN=0000 CC=0000\nrolb       A=82345679 B=0000001e R=8234565e CCIN=0000 CC=0000\nroll       A=82345679 B=0000001f R=c11a2b3c CCIN=0000 CC=0000\nrolw       A=82345679 B=0000001f R=8234ab3c CCIN=0000 CC=0000\nrolb       A=82345679 B=0000001f R=823456bc CCIN=0000 CC=0000\nrorl       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrorb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrorl       A=12345678 B=00000001 R=091a2b3c CCIN=0000 CC=0000\nrorw       A=12345678 B=00000001 R=12342b3c CCIN=0000 CC=0000\nrorb       A=12345678 B=00000001 R=1234563c CCIN=0000 CC=0000\nrorl       A=12345678 B=00000002 R=048d159e CCIN=0000 CC=0000\nrorw       A=12345678 B=00000002 R=1234159e CCIN=0000 CC=0000\nrorb       A=12345678 B=00000002 R=1234561e CCIN=0000 CC=0000\nrorl       A=12345678 B=00000003 R=02468acf CCIN=0000 CC=0000\nrorw       A=12345678 B=00000003 R=12340acf CCIN=0000 CC=0000\nrorb       A=12345678 B=00000003 R=1234560f CCIN=0000 CC=0000\nrorl       A=12345678 B=00000004 R=81234567 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000004 R=12348567 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000004 R=12345687 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000005 R=c091a2b3 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000005 R=1234c2b3 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000005 R=123456c3 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000006 R=e048d159 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000006 R=1234e159 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000006 R=123456e1 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000007 R=f02468ac CCIN=0000 CC=0001\nrorw       A=12345678 B=00000007 R=1234f0ac CCIN=0000 CC=0001\nrorb       A=12345678 B=00000007 R=123456f0 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000008 R=78123456 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000008 R=12347856 CCIN=0000 CC=0000\nrorb       A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nrorl       A=12345678 B=00000009 R=3c091a2b CCIN=0000 CC=0000\nrorw       A=12345678 B=00000009 R=12343c2b CCIN=0000 CC=0000\nrorb       A=12345678 B=00000009 R=1234563c CCIN=0000 CC=0000\nrorl       A=12345678 B=0000000a R=9e048d15 CCIN=0000 CC=0001\nrorw       A=12345678 B=0000000a R=12349e15 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000000a R=1234561e CCIN=0000 CC=0000\nrorl       A=12345678 B=0000000b R=cf02468a CCIN=0000 CC=0001\nrorw       A=12345678 B=0000000b R=1234cf0a CCIN=0000 CC=0001\nrorb       A=12345678 B=0000000b R=1234560f CCIN=0000 CC=0000\nrorl       A=12345678 B=0000000c R=67812345 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000000c R=12346785 CCIN=0000 CC=0000\nrorb       A=12345678 B=0000000c R=12345687 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000000d R=b3c091a2 CCIN=0000 CC=0001\nrorw       A=12345678 B=0000000d R=1234b3c2 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000000d R=123456c3 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000000e R=59e048d1 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000000e R=123459e1 CCIN=0000 CC=0000\nrorb       A=12345678 B=0000000e R=123456e1 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000000f R=acf02468 CCIN=0000 CC=0001\nrorw       A=12345678 B=0000000f R=1234acf0 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000000f R=123456f0 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000010 R=56781234 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nrorb       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nrorl       A=12345678 B=00000011 R=2b3c091a CCIN=0000 CC=0000\nrorw       A=12345678 B=00000011 R=12342b3c CCIN=0000 CC=0000\nrorb       A=12345678 B=00000011 R=1234563c CCIN=0000 CC=0000\nrorl       A=12345678 B=00000012 R=159e048d CCIN=0000 CC=0000\nrorw       A=12345678 B=00000012 R=1234159e CCIN=0000 CC=0000\nrorb       A=12345678 B=00000012 R=1234561e CCIN=0000 CC=0000\nrorl       A=12345678 B=00000013 R=8acf0246 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000013 R=12340acf CCIN=0000 CC=0000\nrorb       A=12345678 B=00000013 R=1234560f CCIN=0000 CC=0000\nrorl       A=12345678 B=00000014 R=45678123 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000014 R=12348567 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000014 R=12345687 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000015 R=a2b3c091 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000015 R=1234c2b3 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000015 R=123456c3 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000016 R=d159e048 CCIN=0000 CC=0001\nrorw       A=12345678 B=00000016 R=1234e159 CCIN=0000 CC=0001\nrorb       A=12345678 B=00000016 R=123456e1 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000017 R=68acf024 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000017 R=1234f0ac CCIN=0000 CC=0001\nrorb       A=12345678 B=00000017 R=123456f0 CCIN=0000 CC=0001\nrorl       A=12345678 B=00000018 R=34567812 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000018 R=12347856 CCIN=0000 CC=0000\nrorb       A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nrorl       A=12345678 B=00000019 R=1a2b3c09 CCIN=0000 CC=0000\nrorw       A=12345678 B=00000019 R=12343c2b CCIN=0000 CC=0000\nrorb       A=12345678 B=00000019 R=1234563c CCIN=0000 CC=0000\nrorl       A=12345678 B=0000001a R=8d159e04 CCIN=0000 CC=0001\nrorw       A=12345678 B=0000001a R=12349e15 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000001a R=1234561e CCIN=0000 CC=0000\nrorl       A=12345678 B=0000001b R=468acf02 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000001b R=1234cf0a CCIN=0000 CC=0001\nrorb       A=12345678 B=0000001b R=1234560f CCIN=0000 CC=0000\nrorl       A=12345678 B=0000001c R=23456781 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000001c R=12346785 CCIN=0000 CC=0000\nrorb       A=12345678 B=0000001c R=12345687 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000001d R=91a2b3c0 CCIN=0000 CC=0001\nrorw       A=12345678 B=0000001d R=1234b3c2 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000001d R=123456c3 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000001e R=48d159e0 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000001e R=123459e1 CCIN=0000 CC=0000\nrorb       A=12345678 B=0000001e R=123456e1 CCIN=0000 CC=0001\nrorl       A=12345678 B=0000001f R=2468acf0 CCIN=0000 CC=0000\nrorw       A=12345678 B=0000001f R=1234acf0 CCIN=0000 CC=0001\nrorb       A=12345678 B=0000001f R=123456f0 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrorb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrorl       A=82345679 B=00000001 R=c11a2b3c CCIN=0000 CC=0001\nrorw       A=82345679 B=00000001 R=8234ab3c CCIN=0000 CC=0801\nrorb       A=82345679 B=00000001 R=823456bc CCIN=0000 CC=0801\nrorl       A=82345679 B=00000002 R=608d159e CCIN=0000 CC=0000\nrorw       A=82345679 B=00000002 R=8234559e CCIN=0000 CC=0000\nrorb       A=82345679 B=00000002 R=8234565e CCIN=0000 CC=0000\nrorl       A=82345679 B=00000003 R=30468acf CCIN=0000 CC=0000\nrorw       A=82345679 B=00000003 R=82342acf CCIN=0000 CC=0000\nrorb       A=82345679 B=00000003 R=8234562f CCIN=0000 CC=0000\nrorl       A=82345679 B=00000004 R=98234567 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000004 R=82349567 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000004 R=82345697 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000005 R=cc11a2b3 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000005 R=8234cab3 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000005 R=823456cb CCIN=0000 CC=0001\nrorl       A=82345679 B=00000006 R=e608d159 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000006 R=8234e559 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000006 R=823456e5 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000007 R=f30468ac CCIN=0000 CC=0001\nrorw       A=82345679 B=00000007 R=8234f2ac CCIN=0000 CC=0001\nrorb       A=82345679 B=00000007 R=823456f2 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000008 R=79823456 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000008 R=82347956 CCIN=0000 CC=0000\nrorb       A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0000\nrorl       A=82345679 B=00000009 R=3cc11a2b CCIN=0000 CC=0000\nrorw       A=82345679 B=00000009 R=82343cab CCIN=0000 CC=0000\nrorb       A=82345679 B=00000009 R=823456bc CCIN=0000 CC=0001\nrorl       A=82345679 B=0000000a R=9e608d15 CCIN=0000 CC=0001\nrorw       A=82345679 B=0000000a R=82349e55 CCIN=0000 CC=0001\nrorb       A=82345679 B=0000000a R=8234565e CCIN=0000 CC=0000\nrorl       A=82345679 B=0000000b R=cf30468a CCIN=0000 CC=0001\nrorw       A=82345679 B=0000000b R=8234cf2a CCIN=0000 CC=0001\nrorb       A=82345679 B=0000000b R=8234562f CCIN=0000 CC=0000\nrorl       A=82345679 B=0000000c R=67982345 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000000c R=82346795 CCIN=0000 CC=0000\nrorb       A=82345679 B=0000000c R=82345697 CCIN=0000 CC=0001\nrorl       A=82345679 B=0000000d R=b3cc11a2 CCIN=0000 CC=0001\nrorw       A=82345679 B=0000000d R=8234b3ca CCIN=0000 CC=0001\nrorb       A=82345679 B=0000000d R=823456cb CCIN=0000 CC=0001\nrorl       A=82345679 B=0000000e R=59e608d1 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000000e R=823459e5 CCIN=0000 CC=0000\nrorb       A=82345679 B=0000000e R=823456e5 CCIN=0000 CC=0001\nrorl       A=82345679 B=0000000f R=acf30468 CCIN=0000 CC=0001\nrorw       A=82345679 B=0000000f R=8234acf2 CCIN=0000 CC=0001\nrorb       A=82345679 B=0000000f R=823456f2 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000010 R=56798234 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0000\nrorb       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0000\nrorl       A=82345679 B=00000011 R=2b3cc11a CCIN=0000 CC=0000\nrorw       A=82345679 B=00000011 R=8234ab3c CCIN=0000 CC=0001\nrorb       A=82345679 B=00000011 R=823456bc CCIN=0000 CC=0001\nrorl       A=82345679 B=00000012 R=159e608d CCIN=0000 CC=0000\nrorw       A=82345679 B=00000012 R=8234559e CCIN=0000 CC=0000\nrorb       A=82345679 B=00000012 R=8234565e CCIN=0000 CC=0000\nrorl       A=82345679 B=00000013 R=8acf3046 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000013 R=82342acf CCIN=0000 CC=0000\nrorb       A=82345679 B=00000013 R=8234562f CCIN=0000 CC=0000\nrorl       A=82345679 B=00000014 R=45679823 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000014 R=82349567 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000014 R=82345697 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000015 R=a2b3cc11 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000015 R=8234cab3 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000015 R=823456cb CCIN=0000 CC=0001\nrorl       A=82345679 B=00000016 R=d159e608 CCIN=0000 CC=0001\nrorw       A=82345679 B=00000016 R=8234e559 CCIN=0000 CC=0001\nrorb       A=82345679 B=00000016 R=823456e5 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000017 R=68acf304 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000017 R=8234f2ac CCIN=0000 CC=0001\nrorb       A=82345679 B=00000017 R=823456f2 CCIN=0000 CC=0001\nrorl       A=82345679 B=00000018 R=34567982 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000018 R=82347956 CCIN=0000 CC=0000\nrorb       A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0000\nrorl       A=82345679 B=00000019 R=1a2b3cc1 CCIN=0000 CC=0000\nrorw       A=82345679 B=00000019 R=82343cab CCIN=0000 CC=0000\nrorb       A=82345679 B=00000019 R=823456bc CCIN=0000 CC=0001\nrorl       A=82345679 B=0000001a R=8d159e60 CCIN=0000 CC=0001\nrorw       A=82345679 B=0000001a R=82349e55 CCIN=0000 CC=0001\nrorb       A=82345679 B=0000001a R=8234565e CCIN=0000 CC=0000\nrorl       A=82345679 B=0000001b R=468acf30 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000001b R=8234cf2a CCIN=0000 CC=0001\nrorb       A=82345679 B=0000001b R=8234562f CCIN=0000 CC=0000\nrorl       A=82345679 B=0000001c R=23456798 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000001c R=82346795 CCIN=0000 CC=0000\nrorb       A=82345679 B=0000001c R=82345697 CCIN=0000 CC=0001\nrorl       A=82345679 B=0000001d R=11a2b3cc CCIN=0000 CC=0000\nrorw       A=82345679 B=0000001d R=8234b3ca CCIN=0000 CC=0001\nrorb       A=82345679 B=0000001d R=823456cb CCIN=0000 CC=0001\nrorl       A=82345679 B=0000001e R=08d159e6 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000001e R=823459e5 CCIN=0000 CC=0000\nrorb       A=82345679 B=0000001e R=823456e5 CCIN=0000 CC=0001\nrorl       A=82345679 B=0000001f R=0468acf3 CCIN=0000 CC=0000\nrorw       A=82345679 B=0000001f R=8234acf2 CCIN=0000 CC=0001\nrorb       A=82345679 B=0000001f R=823456f2 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000001 R=091a2b3c CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000001 R=12342b3c CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000001 R=1234563c CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000001 R=891a2b3c CCIN=0001 CC=0800\nrcrw       A=12345678 B=00000001 R=1234ab3c CCIN=0001 CC=0800\nrcrb       A=12345678 B=00000001 R=123456bc CCIN=0001 CC=0800\nrcrl       A=12345678 B=00000002 R=048d159e CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000002 R=1234159e CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000002 R=1234561e CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000002 R=448d159e CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000002 R=1234559e CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000002 R=1234565e CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000003 R=02468acf CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000003 R=12340acf CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000003 R=1234560f CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000003 R=22468acf CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000003 R=12342acf CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000003 R=1234562f CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000004 R=01234567 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000004 R=12340567 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000004 R=12345607 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000004 R=11234567 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000004 R=12341567 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000004 R=12345617 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000005 R=8091a2b3 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000005 R=123482b3 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000005 R=12345683 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000005 R=8891a2b3 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000005 R=12348ab3 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000005 R=1234568b CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000006 R=c048d159 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000006 R=1234c159 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000006 R=123456c1 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000006 R=c448d159 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000006 R=1234c559 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000006 R=123456c5 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000007 R=e02468ac CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000007 R=1234e0ac CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000007 R=123456e0 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000007 R=e22468ac CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000007 R=1234e2ac CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000007 R=123456e2 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000008 R=f0123456 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000008 R=1234f056 CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000008 R=123456f0 CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000008 R=f1123456 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000008 R=1234f156 CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000008 R=123456f1 CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000009 R=78091a2b CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000009 R=1234782b CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000009 R=78891a2b CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000009 R=123478ab CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000009 R=12345678 CCIN=0001 CC=0001\nrcrl       A=12345678 B=0000000a R=3c048d15 CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000000a R=12343c15 CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000000a R=1234563c CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000000a R=3c448d15 CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000000a R=12343c55 CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000000a R=123456bc CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000000b R=9e02468a CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000000b R=12349e0a CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000000b R=1234561e CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000000b R=9e22468a CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000000b R=12349e2a CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000000b R=1234565e CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000000c R=cf012345 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000000c R=1234cf05 CCIN=0000 CC=0000\nrcrb       A=12345678 B=0000000c R=1234560f CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000000c R=cf112345 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000000c R=1234cf15 CCIN=0001 CC=0000\nrcrb       A=12345678 B=0000000c R=1234562f CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000000d R=678091a2 CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000000d R=12346782 CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000000d R=12345607 CCIN=0000 CC=0001\nrcrl       A=12345678 B=0000000d R=678891a2 CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000000d R=1234678a CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000000d R=12345617 CCIN=0001 CC=0001\nrcrl       A=12345678 B=0000000e R=b3c048d1 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000000e R=1234b3c1 CCIN=0000 CC=0000\nrcrb       A=12345678 B=0000000e R=12345683 CCIN=0000 CC=0001\nrcrl       A=12345678 B=0000000e R=b3c448d1 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000000e R=1234b3c5 CCIN=0001 CC=0000\nrcrb       A=12345678 B=0000000e R=1234568b CCIN=0001 CC=0001\nrcrl       A=12345678 B=0000000f R=59e02468 CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000000f R=123459e0 CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000000f R=123456c1 CCIN=0000 CC=0001\nrcrl       A=12345678 B=0000000f R=59e22468 CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000000f R=123459e2 CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000000f R=123456c5 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000010 R=acf01234 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000010 R=1234acf0 CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000010 R=123456e0 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000010 R=acf11234 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000010 R=1234acf1 CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000010 R=123456e2 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000011 R=5678091a CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000011 R=123456f0 CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000011 R=5678891a CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000011 R=12345678 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000011 R=123456f1 CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000012 R=2b3c048d CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000012 R=12342b3c CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000012 R=2b3c448d CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000012 R=1234ab3c CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000012 R=12345678 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000013 R=159e0246 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000013 R=1234159e CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000013 R=1234563c CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000013 R=159e2246 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000013 R=1234559e CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000013 R=123456bc CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000014 R=8acf0123 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000014 R=12340acf CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000014 R=1234561e CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000014 R=8acf1123 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000014 R=12342acf CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000014 R=1234565e CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000015 R=45678091 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000015 R=12340567 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000015 R=1234560f CCIN=0000 CC=0000\nrcrl       A=12345678 B=00000015 R=45678891 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000015 R=12341567 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000015 R=1234562f CCIN=0001 CC=0000\nrcrl       A=12345678 B=00000016 R=a2b3c048 CCIN=0000 CC=0001\nrcrw       A=12345678 B=00000016 R=123482b3 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000016 R=12345607 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000016 R=a2b3c448 CCIN=0001 CC=0001\nrcrw       A=12345678 B=00000016 R=12348ab3 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000016 R=12345617 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000017 R=d159e024 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000017 R=1234c159 CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000017 R=12345683 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000017 R=d159e224 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000017 R=1234c559 CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000017 R=1234568b CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000018 R=68acf012 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000018 R=1234e0ac CCIN=0000 CC=0001\nrcrb       A=12345678 B=00000018 R=123456c1 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000018 R=68acf112 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000018 R=1234e2ac CCIN=0001 CC=0001\nrcrb       A=12345678 B=00000018 R=123456c5 CCIN=0001 CC=0001\nrcrl       A=12345678 B=00000019 R=34567809 CCIN=0000 CC=0000\nrcrw       A=12345678 B=00000019 R=1234f056 CCIN=0000 CC=0000\nrcrb       A=12345678 B=00000019 R=123456e0 CCIN=0000 CC=0001\nrcrl       A=12345678 B=00000019 R=34567889 CCIN=0001 CC=0000\nrcrw       A=12345678 B=00000019 R=1234f156 CCIN=0001 CC=0000\nrcrb       A=12345678 B=00000019 R=123456e2 CCIN=0001 CC=0001\nrcrl       A=12345678 B=0000001a R=1a2b3c04 CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000001a R=1234782b CCIN=0000 CC=0000\nrcrb       A=12345678 B=0000001a R=123456f0 CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000001a R=1a2b3c44 CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000001a R=123478ab CCIN=0001 CC=0000\nrcrb       A=12345678 B=0000001a R=123456f1 CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000001b R=8d159e02 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000001b R=12343c15 CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000001b R=8d159e22 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000001b R=12343c55 CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000001b R=12345678 CCIN=0001 CC=0001\nrcrl       A=12345678 B=0000001c R=468acf01 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000001c R=12349e0a CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000001c R=1234563c CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000001c R=468acf11 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000001c R=12349e2a CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000001c R=123456bc CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000001d R=23456780 CCIN=0000 CC=0001\nrcrw       A=12345678 B=0000001d R=1234cf05 CCIN=0000 CC=0000\nrcrb       A=12345678 B=0000001d R=1234561e CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000001d R=23456788 CCIN=0001 CC=0001\nrcrw       A=12345678 B=0000001d R=1234cf15 CCIN=0001 CC=0000\nrcrb       A=12345678 B=0000001d R=1234565e CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000001e R=91a2b3c0 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000001e R=12346782 CCIN=0000 CC=0001\nrcrb       A=12345678 B=0000001e R=1234560f CCIN=0000 CC=0000\nrcrl       A=12345678 B=0000001e R=91a2b3c4 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000001e R=1234678a CCIN=0001 CC=0001\nrcrb       A=12345678 B=0000001e R=1234562f CCIN=0001 CC=0000\nrcrl       A=12345678 B=0000001f R=48d159e0 CCIN=0000 CC=0000\nrcrw       A=12345678 B=0000001f R=1234b3c1 CCIN=0000 CC=0000\nrcrb       A=12345678 B=0000001f R=12345607 CCIN=0000 CC=0001\nrcrl       A=12345678 B=0000001f R=48d159e2 CCIN=0001 CC=0000\nrcrw       A=12345678 B=0000001f R=1234b3c5 CCIN=0001 CC=0000\nrcrb       A=12345678 B=0000001f R=12345617 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000001 R=411a2b3c CCIN=0000 CC=0801\nrcrw       A=82345679 B=00000001 R=82342b3c CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000001 R=8234563c CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000001 R=c11a2b3c CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000001 R=8234ab3c CCIN=0001 CC=0801\nrcrb       A=82345679 B=00000001 R=823456bc CCIN=0001 CC=0801\nrcrl       A=82345679 B=00000002 R=a08d159e CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000002 R=8234959e CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000002 R=8234569e CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000002 R=e08d159e CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000002 R=8234d59e CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000002 R=823456de CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000003 R=50468acf CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000003 R=82344acf CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000003 R=8234564f CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000003 R=70468acf CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000003 R=82346acf CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000003 R=8234566f CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000004 R=28234567 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000004 R=82342567 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000004 R=82345627 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000004 R=38234567 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000004 R=82343567 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000004 R=82345637 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000005 R=9411a2b3 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000005 R=823492b3 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000005 R=82345693 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000005 R=9c11a2b3 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000005 R=82349ab3 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000005 R=8234569b CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000006 R=ca08d159 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000006 R=8234c959 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000006 R=823456c9 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000006 R=ce08d159 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000006 R=8234cd59 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000006 R=823456cd CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000007 R=e50468ac CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000007 R=8234e4ac CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000007 R=823456e4 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000007 R=e70468ac CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000007 R=8234e6ac CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000007 R=823456e6 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000008 R=f2823456 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000008 R=8234f256 CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000008 R=823456f2 CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000008 R=f3823456 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000008 R=8234f356 CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000008 R=823456f3 CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000009 R=79411a2b CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000009 R=8234792b CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000009 R=79c11a2b CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000009 R=823479ab CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000009 R=82345679 CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000000a R=3ca08d15 CCIN=0000 CC=0001\nrcrw       A=82345679 B=0000000a R=82343c95 CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000000a R=8234563c CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000000a R=3ce08d15 CCIN=0001 CC=0001\nrcrw       A=82345679 B=0000000a R=82343cd5 CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000000a R=823456bc CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000000b R=9e50468a CCIN=0000 CC=0001\nrcrw       A=82345679 B=0000000b R=82349e4a CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000000b R=8234569e CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000000b R=9e70468a CCIN=0001 CC=0001\nrcrw       A=82345679 B=0000000b R=82349e6a CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000000b R=823456de CCIN=0001 CC=0000\nrcrl       A=82345679 B=0000000c R=cf282345 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000000c R=8234cf25 CCIN=0000 CC=0000\nrcrb       A=82345679 B=0000000c R=8234564f CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000000c R=cf382345 CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000000c R=8234cf35 CCIN=0001 CC=0000\nrcrb       A=82345679 B=0000000c R=8234566f CCIN=0001 CC=0000\nrcrl       A=82345679 B=0000000d R=679411a2 CCIN=0000 CC=0001\nrcrw       A=82345679 B=0000000d R=82346792 CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000000d R=82345627 CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000000d R=679c11a2 CCIN=0001 CC=0001\nrcrw       A=82345679 B=0000000d R=8234679a CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000000d R=82345637 CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000000e R=b3ca08d1 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000000e R=8234b3c9 CCIN=0000 CC=0000\nrcrb       A=82345679 B=0000000e R=82345693 CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000000e R=b3ce08d1 CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000000e R=8234b3cd CCIN=0001 CC=0000\nrcrb       A=82345679 B=0000000e R=8234569b CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000000f R=59e50468 CCIN=0000 CC=0001\nrcrw       A=82345679 B=0000000f R=823459e4 CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000000f R=823456c9 CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000000f R=59e70468 CCIN=0001 CC=0001\nrcrw       A=82345679 B=0000000f R=823459e6 CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000000f R=823456cd CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000010 R=acf28234 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000010 R=8234acf2 CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000010 R=823456e4 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000010 R=acf38234 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000010 R=8234acf3 CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000010 R=823456e6 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000011 R=5679411a CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000011 R=823456f2 CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000011 R=5679c11a CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000011 R=82345679 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000011 R=823456f3 CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000012 R=2b3ca08d CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000012 R=82342b3c CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000012 R=2b3ce08d CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000012 R=8234ab3c CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000012 R=82345679 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000013 R=159e5046 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000013 R=8234959e CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000013 R=8234563c CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000013 R=159e7046 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000013 R=8234d59e CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000013 R=823456bc CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000014 R=8acf2823 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000014 R=82344acf CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000014 R=8234569e CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000014 R=8acf3823 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000014 R=82346acf CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000014 R=823456de CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000015 R=45679411 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000015 R=82342567 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000015 R=8234564f CCIN=0000 CC=0000\nrcrl       A=82345679 B=00000015 R=45679c11 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000015 R=82343567 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000015 R=8234566f CCIN=0001 CC=0000\nrcrl       A=82345679 B=00000016 R=a2b3ca08 CCIN=0000 CC=0001\nrcrw       A=82345679 B=00000016 R=823492b3 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000016 R=82345627 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000016 R=a2b3ce08 CCIN=0001 CC=0001\nrcrw       A=82345679 B=00000016 R=82349ab3 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000016 R=82345637 CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000017 R=d159e504 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000017 R=8234c959 CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000017 R=82345693 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000017 R=d159e704 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000017 R=8234cd59 CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000017 R=8234569b CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000018 R=68acf282 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000018 R=8234e4ac CCIN=0000 CC=0001\nrcrb       A=82345679 B=00000018 R=823456c9 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000018 R=68acf382 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000018 R=8234e6ac CCIN=0001 CC=0001\nrcrb       A=82345679 B=00000018 R=823456cd CCIN=0001 CC=0001\nrcrl       A=82345679 B=00000019 R=34567941 CCIN=0000 CC=0000\nrcrw       A=82345679 B=00000019 R=8234f256 CCIN=0000 CC=0000\nrcrb       A=82345679 B=00000019 R=823456e4 CCIN=0000 CC=0001\nrcrl       A=82345679 B=00000019 R=345679c1 CCIN=0001 CC=0000\nrcrw       A=82345679 B=00000019 R=8234f356 CCIN=0001 CC=0000\nrcrb       A=82345679 B=00000019 R=823456e6 CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000001a R=1a2b3ca0 CCIN=0000 CC=0001\nrcrw       A=82345679 B=0000001a R=8234792b CCIN=0000 CC=0000\nrcrb       A=82345679 B=0000001a R=823456f2 CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000001a R=1a2b3ce0 CCIN=0001 CC=0001\nrcrw       A=82345679 B=0000001a R=823479ab CCIN=0001 CC=0000\nrcrb       A=82345679 B=0000001a R=823456f3 CCIN=0001 CC=0000\nrcrl       A=82345679 B=0000001b R=8d159e50 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000001b R=82343c95 CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000001b R=8d159e70 CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000001b R=82343cd5 CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000001b R=82345679 CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000001c R=468acf28 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000001c R=82349e4a CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000001c R=8234563c CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000001c R=468acf38 CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000001c R=82349e6a CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000001c R=823456bc CCIN=0001 CC=0001\nrcrl       A=82345679 B=0000001d R=23456794 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000001d R=8234cf25 CCIN=0000 CC=0000\nrcrb       A=82345679 B=0000001d R=8234569e CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000001d R=2345679c CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000001d R=8234cf35 CCIN=0001 CC=0000\nrcrb       A=82345679 B=0000001d R=823456de CCIN=0001 CC=0000\nrcrl       A=82345679 B=0000001e R=11a2b3ca CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000001e R=82346792 CCIN=0000 CC=0001\nrcrb       A=82345679 B=0000001e R=8234564f CCIN=0000 CC=0000\nrcrl       A=82345679 B=0000001e R=11a2b3ce CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000001e R=8234679a CCIN=0001 CC=0001\nrcrb       A=82345679 B=0000001e R=8234566f CCIN=0001 CC=0000\nrcrl       A=82345679 B=0000001f R=08d159e5 CCIN=0000 CC=0000\nrcrw       A=82345679 B=0000001f R=8234b3c9 CCIN=0000 CC=0000\nrcrb       A=82345679 B=0000001f R=82345627 CCIN=0000 CC=0001\nrcrl       A=82345679 B=0000001f R=08d159e7 CCIN=0001 CC=0000\nrcrw       A=82345679 B=0000001f R=8234b3cd CCIN=0001 CC=0000\nrcrb       A=82345679 B=0000001f R=82345637 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nrcll       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000000 R=12345678 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000001 R=2468acf0 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000001 R=1234acf0 CCIN=0000 CC=0800\nrclb       A=12345678 B=00000001 R=123456f0 CCIN=0000 CC=0800\nrcll       A=12345678 B=00000001 R=2468acf1 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000001 R=1234acf1 CCIN=0001 CC=0800\nrclb       A=12345678 B=00000001 R=123456f1 CCIN=0001 CC=0800\nrcll       A=12345678 B=00000002 R=48d159e0 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000002 R=123459e0 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000002 R=123456e0 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000002 R=48d159e2 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000002 R=123459e2 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000002 R=123456e2 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000003 R=91a2b3c0 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000003 R=1234b3c1 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000003 R=123456c1 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000003 R=91a2b3c4 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000003 R=1234b3c5 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000003 R=123456c5 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000004 R=23456780 CCIN=0000 CC=0001\nrclw       A=12345678 B=00000004 R=12346782 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000004 R=12345683 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000004 R=23456788 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000004 R=1234678a CCIN=0001 CC=0001\nrclb       A=12345678 B=00000004 R=1234568b CCIN=0001 CC=0001\nrcll       A=12345678 B=00000005 R=468acf01 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000005 R=1234cf05 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000005 R=12345607 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000005 R=468acf11 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000005 R=1234cf15 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000005 R=12345617 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000006 R=8d159e02 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000006 R=12349e0a CCIN=0000 CC=0001\nrclb       A=12345678 B=00000006 R=1234560f CCIN=0000 CC=0000\nrcll       A=12345678 B=00000006 R=8d159e22 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000006 R=12349e2a CCIN=0001 CC=0001\nrclb       A=12345678 B=00000006 R=1234562f CCIN=0001 CC=0000\nrcll       A=12345678 B=00000007 R=1a2b3c04 CCIN=0000 CC=0001\nrclw       A=12345678 B=00000007 R=12343c15 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000007 R=1234561e CCIN=0000 CC=0000\nrcll       A=12345678 B=00000007 R=1a2b3c44 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000007 R=12343c55 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000007 R=1234565e CCIN=0001 CC=0000\nrcll       A=12345678 B=00000008 R=34567809 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000008 R=1234782b CCIN=0000 CC=0000\nrclb       A=12345678 B=00000008 R=1234563c CCIN=0000 CC=0000\nrcll       A=12345678 B=00000008 R=34567889 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000008 R=123478ab CCIN=0001 CC=0000\nrclb       A=12345678 B=00000008 R=123456bc CCIN=0001 CC=0000\nrcll       A=12345678 B=00000009 R=68acf012 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000009 R=1234f056 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0000\nrcll       A=12345678 B=00000009 R=68acf112 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000009 R=1234f156 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000009 R=12345678 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000000a R=d159e024 CCIN=0000 CC=0000\nrclw       A=12345678 B=0000000a R=1234e0ac CCIN=0000 CC=0001\nrclb       A=12345678 B=0000000a R=123456f0 CCIN=0000 CC=0000\nrcll       A=12345678 B=0000000a R=d159e224 CCIN=0001 CC=0000\nrclw       A=12345678 B=0000000a R=1234e2ac CCIN=0001 CC=0001\nrclb       A=12345678 B=0000000a R=123456f1 CCIN=0001 CC=0000\nrcll       A=12345678 B=0000000b R=a2b3c048 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000000b R=1234c159 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000000b R=123456e0 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000000b R=a2b3c448 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000000b R=1234c559 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000000b R=123456e2 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000000c R=45678091 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000000c R=123482b3 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000000c R=123456c1 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000000c R=45678891 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000000c R=12348ab3 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000000c R=123456c5 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000000d R=8acf0123 CCIN=0000 CC=0000\nrclw       A=12345678 B=0000000d R=12340567 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000000d R=12345683 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000000d R=8acf1123 CCIN=0001 CC=0000\nrclw       A=12345678 B=0000000d R=12341567 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000000d R=1234568b CCIN=0001 CC=0001\nrcll       A=12345678 B=0000000e R=159e0246 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000000e R=12340acf CCIN=0000 CC=0000\nrclb       A=12345678 B=0000000e R=12345607 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000000e R=159e2246 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000000e R=12342acf CCIN=0001 CC=0000\nrclb       A=12345678 B=0000000e R=12345617 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000000f R=2b3c048d CCIN=0000 CC=0000\nrclw       A=12345678 B=0000000f R=1234159e CCIN=0000 CC=0000\nrclb       A=12345678 B=0000000f R=1234560f CCIN=0000 CC=0000\nrcll       A=12345678 B=0000000f R=2b3c448d CCIN=0001 CC=0000\nrclw       A=12345678 B=0000000f R=1234559e CCIN=0001 CC=0000\nrclb       A=12345678 B=0000000f R=1234562f CCIN=0001 CC=0000\nrcll       A=12345678 B=00000010 R=5678091a CCIN=0000 CC=0000\nrclw       A=12345678 B=00000010 R=12342b3c CCIN=0000 CC=0000\nrclb       A=12345678 B=00000010 R=1234561e CCIN=0000 CC=0000\nrcll       A=12345678 B=00000010 R=5678891a CCIN=0001 CC=0000\nrclw       A=12345678 B=00000010 R=1234ab3c CCIN=0001 CC=0000\nrclb       A=12345678 B=00000010 R=1234565e CCIN=0001 CC=0000\nrcll       A=12345678 B=00000011 R=acf01234 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000011 R=1234563c CCIN=0000 CC=0000\nrcll       A=12345678 B=00000011 R=acf11234 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000011 R=12345678 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000011 R=123456bc CCIN=0001 CC=0000\nrcll       A=12345678 B=00000012 R=59e02468 CCIN=0000 CC=0001\nrclw       A=12345678 B=00000012 R=1234acf0 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0000\nrcll       A=12345678 B=00000012 R=59e22468 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000012 R=1234acf1 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000012 R=12345678 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000013 R=b3c048d1 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000013 R=123459e0 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000013 R=123456f0 CCIN=0000 CC=0000\nrcll       A=12345678 B=00000013 R=b3c448d1 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000013 R=123459e2 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000013 R=123456f1 CCIN=0001 CC=0000\nrcll       A=12345678 B=00000014 R=678091a2 CCIN=0000 CC=0001\nrclw       A=12345678 B=00000014 R=1234b3c1 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000014 R=123456e0 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000014 R=678891a2 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000014 R=1234b3c5 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000014 R=123456e2 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000015 R=cf012345 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000015 R=12346782 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000015 R=123456c1 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000015 R=cf112345 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000015 R=1234678a CCIN=0001 CC=0001\nrclb       A=12345678 B=00000015 R=123456c5 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000016 R=9e02468a CCIN=0000 CC=0001\nrclw       A=12345678 B=00000016 R=1234cf05 CCIN=0000 CC=0000\nrclb       A=12345678 B=00000016 R=12345683 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000016 R=9e22468a CCIN=0001 CC=0001\nrclw       A=12345678 B=00000016 R=1234cf15 CCIN=0001 CC=0000\nrclb       A=12345678 B=00000016 R=1234568b CCIN=0001 CC=0001\nrcll       A=12345678 B=00000017 R=3c048d15 CCIN=0000 CC=0001\nrclw       A=12345678 B=00000017 R=12349e0a CCIN=0000 CC=0001\nrclb       A=12345678 B=00000017 R=12345607 CCIN=0000 CC=0001\nrcll       A=12345678 B=00000017 R=3c448d15 CCIN=0001 CC=0001\nrclw       A=12345678 B=00000017 R=12349e2a CCIN=0001 CC=0001\nrclb       A=12345678 B=00000017 R=12345617 CCIN=0001 CC=0001\nrcll       A=12345678 B=00000018 R=78091a2b CCIN=0000 CC=0000\nrclw       A=12345678 B=00000018 R=12343c15 CCIN=0000 CC=0001\nrclb       A=12345678 B=00000018 R=1234560f CCIN=0000 CC=0000\nrcll       A=12345678 B=00000018 R=78891a2b CCIN=0001 CC=0000\nrclw       A=12345678 B=00000018 R=12343c55 CCIN=0001 CC=0001\nrclb       A=12345678 B=00000018 R=1234562f CCIN=0001 CC=0000\nrcll       A=12345678 B=00000019 R=f0123456 CCIN=0000 CC=0000\nrclw       A=12345678 B=00000019 R=1234782b CCIN=0000 CC=0000\nrclb       A=12345678 B=00000019 R=1234561e CCIN=0000 CC=0000\nrcll       A=12345678 B=00000019 R=f1123456 CCIN=0001 CC=0000\nrclw       A=12345678 B=00000019 R=123478ab CCIN=0001 CC=0000\nrclb       A=12345678 B=00000019 R=1234565e CCIN=0001 CC=0000\nrcll       A=12345678 B=0000001a R=e02468ac CCIN=0000 CC=0001\nrclw       A=12345678 B=0000001a R=1234f056 CCIN=0000 CC=0000\nrclb       A=12345678 B=0000001a R=1234563c CCIN=0000 CC=0000\nrcll       A=12345678 B=0000001a R=e22468ac CCIN=0001 CC=0001\nrclw       A=12345678 B=0000001a R=1234f156 CCIN=0001 CC=0000\nrclb       A=12345678 B=0000001a R=123456bc CCIN=0001 CC=0000\nrcll       A=12345678 B=0000001b R=c048d159 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000001b R=1234e0ac CCIN=0000 CC=0001\nrclb       A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nrcll       A=12345678 B=0000001b R=c448d159 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000001b R=1234e2ac CCIN=0001 CC=0001\nrclb       A=12345678 B=0000001b R=12345678 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000001c R=8091a2b3 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000001c R=1234c159 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000001c R=123456f0 CCIN=0000 CC=0000\nrcll       A=12345678 B=0000001c R=8891a2b3 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000001c R=1234c559 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000001c R=123456f1 CCIN=0001 CC=0000\nrcll       A=12345678 B=0000001d R=01234567 CCIN=0000 CC=0001\nrclw       A=12345678 B=0000001d R=123482b3 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000001d R=123456e0 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000001d R=11234567 CCIN=0001 CC=0001\nrclw       A=12345678 B=0000001d R=12348ab3 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000001d R=123456e2 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000001e R=02468acf CCIN=0000 CC=0000\nrclw       A=12345678 B=0000001e R=12340567 CCIN=0000 CC=0001\nrclb       A=12345678 B=0000001e R=123456c1 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000001e R=22468acf CCIN=0001 CC=0000\nrclw       A=12345678 B=0000001e R=12341567 CCIN=0001 CC=0001\nrclb       A=12345678 B=0000001e R=123456c5 CCIN=0001 CC=0001\nrcll       A=12345678 B=0000001f R=048d159e CCIN=0000 CC=0000\nrclw       A=12345678 B=0000001f R=12340acf CCIN=0000 CC=0000\nrclb       A=12345678 B=0000001f R=12345683 CCIN=0000 CC=0001\nrcll       A=12345678 B=0000001f R=448d159e CCIN=0001 CC=0000\nrclw       A=12345678 B=0000001f R=12342acf CCIN=0001 CC=0000\nrclb       A=12345678 B=0000001f R=1234568b CCIN=0001 CC=0001\nrcll       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0000\nrcll       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrclw       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000000 R=82345679 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000001 R=0468acf2 CCIN=0000 CC=0801\nrclw       A=82345679 B=00000001 R=8234acf2 CCIN=0000 CC=0800\nrclb       A=82345679 B=00000001 R=823456f2 CCIN=0000 CC=0800\nrcll       A=82345679 B=00000001 R=0468acf3 CCIN=0001 CC=0801\nrclw       A=82345679 B=00000001 R=8234acf3 CCIN=0001 CC=0800\nrclb       A=82345679 B=00000001 R=823456f3 CCIN=0001 CC=0800\nrcll       A=82345679 B=00000002 R=08d159e5 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000002 R=823459e4 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000002 R=823456e4 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000002 R=08d159e7 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000002 R=823459e6 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000002 R=823456e6 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000003 R=11a2b3ca CCIN=0000 CC=0000\nrclw       A=82345679 B=00000003 R=8234b3c9 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000003 R=823456c9 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000003 R=11a2b3ce CCIN=0001 CC=0000\nrclw       A=82345679 B=00000003 R=8234b3cd CCIN=0001 CC=0000\nrclb       A=82345679 B=00000003 R=823456cd CCIN=0001 CC=0001\nrcll       A=82345679 B=00000004 R=23456794 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000004 R=82346792 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000004 R=82345693 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000004 R=2345679c CCIN=0001 CC=0000\nrclw       A=82345679 B=00000004 R=8234679a CCIN=0001 CC=0001\nrclb       A=82345679 B=00000004 R=8234569b CCIN=0001 CC=0001\nrcll       A=82345679 B=00000005 R=468acf28 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000005 R=8234cf25 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000005 R=82345627 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000005 R=468acf38 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000005 R=8234cf35 CCIN=0001 CC=0000\nrclb       A=82345679 B=00000005 R=82345637 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000006 R=8d159e50 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000006 R=82349e4a CCIN=0000 CC=0001\nrclb       A=82345679 B=00000006 R=8234564f CCIN=0000 CC=0000\nrcll       A=82345679 B=00000006 R=8d159e70 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000006 R=82349e6a CCIN=0001 CC=0001\nrclb       A=82345679 B=00000006 R=8234566f CCIN=0001 CC=0000\nrcll       A=82345679 B=00000007 R=1a2b3ca0 CCIN=0000 CC=0001\nrclw       A=82345679 B=00000007 R=82343c95 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000007 R=8234569e CCIN=0000 CC=0000\nrcll       A=82345679 B=00000007 R=1a2b3ce0 CCIN=0001 CC=0001\nrclw       A=82345679 B=00000007 R=82343cd5 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000007 R=823456de CCIN=0001 CC=0000\nrcll       A=82345679 B=00000008 R=34567941 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000008 R=8234792b CCIN=0000 CC=0000\nrclb       A=82345679 B=00000008 R=8234563c CCIN=0000 CC=0001\nrcll       A=82345679 B=00000008 R=345679c1 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000008 R=823479ab CCIN=0001 CC=0000\nrclb       A=82345679 B=00000008 R=823456bc CCIN=0001 CC=0001\nrcll       A=82345679 B=00000009 R=68acf282 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000009 R=8234f256 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0000\nrcll       A=82345679 B=00000009 R=68acf382 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000009 R=8234f356 CCIN=0001 CC=0000\nrclb       A=82345679 B=00000009 R=82345679 CCIN=0001 CC=0001\nrcll       A=82345679 B=0000000a R=d159e504 CCIN=0000 CC=0000\nrclw       A=82345679 B=0000000a R=8234e4ac CCIN=0000 CC=0001\nrclb       A=82345679 B=0000000a R=823456f2 CCIN=0000 CC=0000\nrcll       A=82345679 B=0000000a R=d159e704 CCIN=0001 CC=0000\nrclw       A=82345679 B=0000000a R=8234e6ac CCIN=0001 CC=0001\nrclb       A=82345679 B=0000000a R=823456f3 CCIN=0001 CC=0000\nrcll       A=82345679 B=0000000b R=a2b3ca08 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000000b R=8234c959 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000000b R=823456e4 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000000b R=a2b3ce08 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000000b R=8234cd59 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000000b R=823456e6 CCIN=0001 CC=0001\nrcll       A=82345679 B=0000000c R=45679411 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000000c R=823492b3 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000000c R=823456c9 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000000c R=45679c11 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000000c R=82349ab3 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000000c R=823456cd CCIN=0001 CC=0001\nrcll       A=82345679 B=0000000d R=8acf2823 CCIN=0000 CC=0000\nrclw       A=82345679 B=0000000d R=82342567 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000000d R=82345693 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000000d R=8acf3823 CCIN=0001 CC=0000\nrclw       A=82345679 B=0000000d R=82343567 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000000d R=8234569b CCIN=0001 CC=0001\nrcll       A=82345679 B=0000000e R=159e5046 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000000e R=82344acf CCIN=0000 CC=0000\nrclb       A=82345679 B=0000000e R=82345627 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000000e R=159e7046 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000000e R=82346acf CCIN=0001 CC=0000\nrclb       A=82345679 B=0000000e R=82345637 CCIN=0001 CC=0001\nrcll       A=82345679 B=0000000f R=2b3ca08d CCIN=0000 CC=0000\nrclw       A=82345679 B=0000000f R=8234959e CCIN=0000 CC=0000\nrclb       A=82345679 B=0000000f R=8234564f CCIN=0000 CC=0000\nrcll       A=82345679 B=0000000f R=2b3ce08d CCIN=0001 CC=0000\nrclw       A=82345679 B=0000000f R=8234d59e CCIN=0001 CC=0000\nrclb       A=82345679 B=0000000f R=8234566f CCIN=0001 CC=0000\nrcll       A=82345679 B=00000010 R=5679411a CCIN=0000 CC=0000\nrclw       A=82345679 B=00000010 R=82342b3c CCIN=0000 CC=0001\nrclb       A=82345679 B=00000010 R=8234569e CCIN=0000 CC=0000\nrcll       A=82345679 B=00000010 R=5679c11a CCIN=0001 CC=0000\nrclw       A=82345679 B=00000010 R=8234ab3c CCIN=0001 CC=0001\nrclb       A=82345679 B=00000010 R=823456de CCIN=0001 CC=0000\nrcll       A=82345679 B=00000011 R=acf28234 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000011 R=8234563c CCIN=0000 CC=0001\nrcll       A=82345679 B=00000011 R=acf38234 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000011 R=82345679 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000011 R=823456bc CCIN=0001 CC=0001\nrcll       A=82345679 B=00000012 R=59e50468 CCIN=0000 CC=0001\nrclw       A=82345679 B=00000012 R=8234acf2 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0000\nrcll       A=82345679 B=00000012 R=59e70468 CCIN=0001 CC=0001\nrclw       A=82345679 B=00000012 R=8234acf3 CCIN=0001 CC=0000\nrclb       A=82345679 B=00000012 R=82345679 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000013 R=b3ca08d1 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000013 R=823459e4 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000013 R=823456f2 CCIN=0000 CC=0000\nrcll       A=82345679 B=00000013 R=b3ce08d1 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000013 R=823459e6 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000013 R=823456f3 CCIN=0001 CC=0000\nrcll       A=82345679 B=00000014 R=679411a2 CCIN=0000 CC=0001\nrclw       A=82345679 B=00000014 R=8234b3c9 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000014 R=823456e4 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000014 R=679c11a2 CCIN=0001 CC=0001\nrclw       A=82345679 B=00000014 R=8234b3cd CCIN=0001 CC=0000\nrclb       A=82345679 B=00000014 R=823456e6 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000015 R=cf282345 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000015 R=82346792 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000015 R=823456c9 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000015 R=cf382345 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000015 R=8234679a CCIN=0001 CC=0001\nrclb       A=82345679 B=00000015 R=823456cd CCIN=0001 CC=0001\nrcll       A=82345679 B=00000016 R=9e50468a CCIN=0000 CC=0001\nrclw       A=82345679 B=00000016 R=8234cf25 CCIN=0000 CC=0000\nrclb       A=82345679 B=00000016 R=82345693 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000016 R=9e70468a CCIN=0001 CC=0001\nrclw       A=82345679 B=00000016 R=8234cf35 CCIN=0001 CC=0000\nrclb       A=82345679 B=00000016 R=8234569b CCIN=0001 CC=0001\nrcll       A=82345679 B=00000017 R=3ca08d15 CCIN=0000 CC=0001\nrclw       A=82345679 B=00000017 R=82349e4a CCIN=0000 CC=0001\nrclb       A=82345679 B=00000017 R=82345627 CCIN=0000 CC=0001\nrcll       A=82345679 B=00000017 R=3ce08d15 CCIN=0001 CC=0001\nrclw       A=82345679 B=00000017 R=82349e6a CCIN=0001 CC=0001\nrclb       A=82345679 B=00000017 R=82345637 CCIN=0001 CC=0001\nrcll       A=82345679 B=00000018 R=79411a2b CCIN=0000 CC=0000\nrclw       A=82345679 B=00000018 R=82343c95 CCIN=0000 CC=0001\nrclb       A=82345679 B=00000018 R=8234564f CCIN=0000 CC=0000\nrcll       A=82345679 B=00000018 R=79c11a2b CCIN=0001 CC=0000\nrclw       A=82345679 B=00000018 R=82343cd5 CCIN=0001 CC=0001\nrclb       A=82345679 B=00000018 R=8234566f CCIN=0001 CC=0000\nrcll       A=82345679 B=00000019 R=f2823456 CCIN=0000 CC=0000\nrclw       A=82345679 B=00000019 R=8234792b CCIN=0000 CC=0000\nrclb       A=82345679 B=00000019 R=8234569e CCIN=0000 CC=0000\nrcll       A=82345679 B=00000019 R=f3823456 CCIN=0001 CC=0000\nrclw       A=82345679 B=00000019 R=823479ab CCIN=0001 CC=0000\nrclb       A=82345679 B=00000019 R=823456de CCIN=0001 CC=0000\nrcll       A=82345679 B=0000001a R=e50468ac CCIN=0000 CC=0001\nrclw       A=82345679 B=0000001a R=8234f256 CCIN=0000 CC=0000\nrclb       A=82345679 B=0000001a R=8234563c CCIN=0000 CC=0001\nrcll       A=82345679 B=0000001a R=e70468ac CCIN=0001 CC=0001\nrclw       A=82345679 B=0000001a R=8234f356 CCIN=0001 CC=0000\nrclb       A=82345679 B=0000001a R=823456bc CCIN=0001 CC=0001\nrcll       A=82345679 B=0000001b R=ca08d159 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000001b R=8234e4ac CCIN=0000 CC=0001\nrclb       A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nrcll       A=82345679 B=0000001b R=ce08d159 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000001b R=8234e6ac CCIN=0001 CC=0001\nrclb       A=82345679 B=0000001b R=82345679 CCIN=0001 CC=0001\nrcll       A=82345679 B=0000001c R=9411a2b3 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000001c R=8234c959 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000001c R=823456f2 CCIN=0000 CC=0000\nrcll       A=82345679 B=0000001c R=9c11a2b3 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000001c R=8234cd59 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000001c R=823456f3 CCIN=0001 CC=0000\nrcll       A=82345679 B=0000001d R=28234567 CCIN=0000 CC=0001\nrclw       A=82345679 B=0000001d R=823492b3 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000001d R=823456e4 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000001d R=38234567 CCIN=0001 CC=0001\nrclw       A=82345679 B=0000001d R=82349ab3 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000001d R=823456e6 CCIN=0001 CC=0001\nrcll       A=82345679 B=0000001e R=50468acf CCIN=0000 CC=0000\nrclw       A=82345679 B=0000001e R=82342567 CCIN=0000 CC=0001\nrclb       A=82345679 B=0000001e R=823456c9 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000001e R=70468acf CCIN=0001 CC=0000\nrclw       A=82345679 B=0000001e R=82343567 CCIN=0001 CC=0001\nrclb       A=82345679 B=0000001e R=823456cd CCIN=0001 CC=0001\nrcll       A=82345679 B=0000001f R=a08d159e CCIN=0000 CC=0000\nrclw       A=82345679 B=0000001f R=82344acf CCIN=0000 CC=0000\nrclb       A=82345679 B=0000001f R=82345693 CCIN=0000 CC=0001\nrcll       A=82345679 B=0000001f R=e08d159e CCIN=0001 CC=0000\nrclw       A=82345679 B=0000001f R=82346acf CCIN=0001 CC=0000\nrclb       A=82345679 B=0000001f R=8234569b CCIN=0001 CC=0001\nshldl      A=12345678 B=21ad3d34 C=00000000 R=12345678 CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000001 R=2468acf0 CCIN=0000 CC=0004\nshldl      A=12345678 B=21ad3d34 C=00000002 R=48d159e0 CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000003 R=91a2b3c1 CCIN=0000 CC=0080\nshldl      A=12345678 B=21ad3d34 C=00000004 R=23456782 CCIN=0000 CC=0005\nshldl      A=12345678 B=21ad3d34 C=00000005 R=468acf04 CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000006 R=8d159e08 CCIN=0000 CC=0080\nshldl      A=12345678 B=21ad3d34 C=00000007 R=1a2b3c10 CCIN=0000 CC=0001\nshldl      A=12345678 B=21ad3d34 C=00000008 R=34567821 CCIN=0000 CC=0004\nshldl      A=12345678 B=21ad3d34 C=00000009 R=68acf043 CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=0000000a R=d159e086 CCIN=0000 CC=0080\nshldl      A=12345678 B=21ad3d34 C=0000000b R=a2b3c10d CCIN=0000 CC=0081\nshldl      A=12345678 B=21ad3d34 C=0000000c R=4567821a CCIN=0000 CC=0001\nshldl      A=12345678 B=21ad3d34 C=0000000d R=8acf0435 CCIN=0000 CC=0084\nshldl      A=12345678 B=21ad3d34 C=0000000e R=159e086b CCIN=0000 CC=0001\nshldl      A=12345678 B=21ad3d34 C=0000000f R=2b3c10d6 CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000010 R=567821ad CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000011 R=acf0435a CCIN=0000 CC=0084\nshldl      A=12345678 B=21ad3d34 C=00000012 R=59e086b4 CCIN=0000 CC=0005\nshldl      A=12345678 B=21ad3d34 C=00000013 R=b3c10d69 CCIN=0000 CC=0084\nshldl      A=12345678 B=21ad3d34 C=00000014 R=67821ad3 CCIN=0000 CC=0001\nshldl      A=12345678 B=21ad3d34 C=00000015 R=cf0435a7 CCIN=0000 CC=0080\nshldl      A=12345678 B=21ad3d34 C=00000016 R=9e086b4f CCIN=0000 CC=0081\nshldl      A=12345678 B=21ad3d34 C=00000017 R=3c10d69e CCIN=0000 CC=0001\nshldl      A=12345678 B=21ad3d34 C=00000018 R=7821ad3d CCIN=0000 CC=0000\nshldl      A=12345678 B=21ad3d34 C=00000019 R=f0435a7a CCIN=0000 CC=0080\nshldl      A=12345678 B=21ad3d34 C=0000001a R=e086b4f4 CCIN=0000 CC=0081\nshldl      A=12345678 B=21ad3d34 C=0000001b R=c10d69e9 CCIN=0000 CC=0081\nshldl      A=12345678 B=21ad3d34 C=0000001c R=821ad3d3 CCIN=0000 CC=0081\nshldl      A=12345678 B=21ad3d34 C=0000001d R=0435a7a6 CCIN=0000 CC=0005\nshldl      A=12345678 B=21ad3d34 C=0000001e R=086b4f4d CCIN=0000 CC=0004\nshldl      A=12345678 B=21ad3d34 C=0000001f R=10d69e9a CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000000 R=82345679 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=00000001 R=0468acf3 CCIN=0000 CC=0805\nshldl      A=82345679 B=813f3421 C=00000002 R=08d159e6 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=00000003 R=11a2b3cc CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000004 R=23456798 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=00000005 R=468acf30 CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000006 R=8d159e60 CCIN=0000 CC=0084\nshldl      A=82345679 B=813f3421 C=00000007 R=1a2b3cc0 CCIN=0000 CC=0005\nshldl      A=82345679 B=813f3421 C=00000008 R=34567981 CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000009 R=68acf302 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=0000000a R=d159e604 CCIN=0000 CC=0080\nshldl      A=82345679 B=813f3421 C=0000000b R=a2b3cc09 CCIN=0000 CC=0085\nshldl      A=82345679 B=813f3421 C=0000000c R=45679813 CCIN=0000 CC=0001\nshldl      A=82345679 B=813f3421 C=0000000d R=8acf3027 CCIN=0000 CC=0084\nshldl      A=82345679 B=813f3421 C=0000000e R=159e604f CCIN=0000 CC=0001\nshldl      A=82345679 B=813f3421 C=0000000f R=2b3cc09f CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000010 R=5679813f CCIN=0000 CC=0004\nshldl      A=82345679 B=813f3421 C=00000011 R=acf3027e CCIN=0000 CC=0084\nshldl      A=82345679 B=813f3421 C=00000012 R=59e604fc CCIN=0000 CC=0005\nshldl      A=82345679 B=813f3421 C=00000013 R=b3cc09f9 CCIN=0000 CC=0084\nshldl      A=82345679 B=813f3421 C=00000014 R=679813f3 CCIN=0000 CC=0005\nshldl      A=82345679 B=813f3421 C=00000015 R=cf3027e6 CCIN=0000 CC=0080\nshldl      A=82345679 B=813f3421 C=00000016 R=9e604fcd CCIN=0000 CC=0081\nshldl      A=82345679 B=813f3421 C=00000017 R=3cc09f9a CCIN=0000 CC=0005\nshldl      A=82345679 B=813f3421 C=00000018 R=79813f34 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=00000019 R=f3027e68 CCIN=0000 CC=0080\nshldl      A=82345679 B=813f3421 C=0000001a R=e604fcd0 CCIN=0000 CC=0081\nshldl      A=82345679 B=813f3421 C=0000001b R=cc09f9a1 CCIN=0000 CC=0081\nshldl      A=82345679 B=813f3421 C=0000001c R=9813f342 CCIN=0000 CC=0085\nshldl      A=82345679 B=813f3421 C=0000001d R=3027e684 CCIN=0000 CC=0005\nshldl      A=82345679 B=813f3421 C=0000001e R=604fcd08 CCIN=0000 CC=0000\nshldl      A=82345679 B=813f3421 C=0000001f R=c09f9a10 CCIN=0000 CC=0080\nshrdl      A=12345678 B=21ad3d34 C=00000000 R=12345678 CCIN=0000 CC=0000\nshrdl      A=12345678 B=21ad3d34 C=00000001 R=091a2b3c CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=00000002 R=048d159e CCIN=0000 CC=0000\nshrdl      A=12345678 B=21ad3d34 C=00000003 R=82468acf CCIN=0000 CC=0084\nshrdl      A=12345678 B=21ad3d34 C=00000004 R=41234567 CCIN=0000 CC=0001\nshrdl      A=12345678 B=21ad3d34 C=00000005 R=a091a2b3 CCIN=0000 CC=0081\nshrdl      A=12345678 B=21ad3d34 C=00000006 R=d048d159 CCIN=0000 CC=0085\nshrdl      A=12345678 B=21ad3d34 C=00000007 R=682468ac CCIN=0000 CC=0005\nshrdl      A=12345678 B=21ad3d34 C=00000008 R=34123456 CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=00000009 R=9a091a2b CCIN=0000 CC=0084\nshrdl      A=12345678 B=21ad3d34 C=0000000a R=4d048d15 CCIN=0000 CC=0001\nshrdl      A=12345678 B=21ad3d34 C=0000000b R=a682468a CCIN=0000 CC=0081\nshrdl      A=12345678 B=21ad3d34 C=0000000c R=d3412345 CCIN=0000 CC=0080\nshrdl      A=12345678 B=21ad3d34 C=0000000d R=e9a091a2 CCIN=0000 CC=0081\nshrdl      A=12345678 B=21ad3d34 C=0000000e R=f4d048d1 CCIN=0000 CC=0084\nshrdl      A=12345678 B=21ad3d34 C=0000000f R=7a682468 CCIN=0000 CC=0001\nshrdl      A=12345678 B=21ad3d34 C=00000010 R=3d341234 CCIN=0000 CC=0000\nshrdl      A=12345678 B=21ad3d34 C=00000011 R=9e9a091a CCIN=0000 CC=0080\nshrdl      A=12345678 B=21ad3d34 C=00000012 R=4f4d048d CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=00000013 R=a7a68246 CCIN=0000 CC=0081\nshrdl      A=12345678 B=21ad3d34 C=00000014 R=d3d34123 CCIN=0000 CC=0080\nshrdl      A=12345678 B=21ad3d34 C=00000015 R=69e9a091 CCIN=0000 CC=0001\nshrdl      A=12345678 B=21ad3d34 C=00000016 R=b4f4d048 CCIN=0000 CC=0085\nshrdl      A=12345678 B=21ad3d34 C=00000017 R=5a7a6824 CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=00000018 R=ad3d3412 CCIN=0000 CC=0084\nshrdl      A=12345678 B=21ad3d34 C=00000019 R=d69e9a09 CCIN=0000 CC=0084\nshrdl      A=12345678 B=21ad3d34 C=0000001a R=6b4f4d04 CCIN=0000 CC=0001\nshrdl      A=12345678 B=21ad3d34 C=0000001b R=35a7a682 CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=0000001c R=1ad3d341 CCIN=0000 CC=0004\nshrdl      A=12345678 B=21ad3d34 C=0000001d R=0d69e9a0 CCIN=0000 CC=0005\nshrdl      A=12345678 B=21ad3d34 C=0000001e R=86b4f4d0 CCIN=0000 CC=0080\nshrdl      A=12345678 B=21ad3d34 C=0000001f R=435a7a68 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=00000000 R=82345679 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=00000001 R=c11a2b3c CCIN=0000 CC=0085\nshrdl      A=82345679 B=813f3421 C=00000002 R=608d159e CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=00000003 R=30468acf CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=00000004 R=18234567 CCIN=0000 CC=0001\nshrdl      A=82345679 B=813f3421 C=00000005 R=0c11a2b3 CCIN=0000 CC=0001\nshrdl      A=82345679 B=813f3421 C=00000006 R=8608d159 CCIN=0000 CC=0085\nshrdl      A=82345679 B=813f3421 C=00000007 R=430468ac CCIN=0000 CC=0005\nshrdl      A=82345679 B=813f3421 C=00000008 R=21823456 CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=00000009 R=10c11a2b CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=0000000a R=08608d15 CCIN=0000 CC=0001\nshrdl      A=82345679 B=813f3421 C=0000000b R=8430468a CCIN=0000 CC=0081\nshrdl      A=82345679 B=813f3421 C=0000000c R=42182345 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=0000000d R=a10c11a2 CCIN=0000 CC=0081\nshrdl      A=82345679 B=813f3421 C=0000000e R=d08608d1 CCIN=0000 CC=0084\nshrdl      A=82345679 B=813f3421 C=0000000f R=68430468 CCIN=0000 CC=0001\nshrdl      A=82345679 B=813f3421 C=00000010 R=34218234 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=00000011 R=9a10c11a CCIN=0000 CC=0080\nshrdl      A=82345679 B=813f3421 C=00000012 R=cd08608d CCIN=0000 CC=0084\nshrdl      A=82345679 B=813f3421 C=00000013 R=e6843046 CCIN=0000 CC=0081\nshrdl      A=82345679 B=813f3421 C=00000014 R=f3421823 CCIN=0000 CC=0080\nshrdl      A=82345679 B=813f3421 C=00000015 R=f9a10c11 CCIN=0000 CC=0085\nshrdl      A=82345679 B=813f3421 C=00000016 R=fcd08608 CCIN=0000 CC=0081\nshrdl      A=82345679 B=813f3421 C=00000017 R=7e684304 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=00000018 R=3f342182 CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=00000019 R=9f9a10c1 CCIN=0000 CC=0080\nshrdl      A=82345679 B=813f3421 C=0000001a R=4fcd0860 CCIN=0000 CC=0005\nshrdl      A=82345679 B=813f3421 C=0000001b R=27e68430 CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=0000001c R=13f34218 CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=0000001d R=09f9a10c CCIN=0000 CC=0004\nshrdl      A=82345679 B=813f3421 C=0000001e R=04fcd086 CCIN=0000 CC=0000\nshrdl      A=82345679 B=813f3421 C=0000001f R=027e6843 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000001 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000001 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000002 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000002 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000003 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000003 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000004 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000004 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000005 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000005 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000006 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000006 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000007 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000007 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000000a R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=0000000a R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000000b R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000000b R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=0000000c R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=0000000c R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000000d R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000000d R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=0000000e R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=0000000e R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000000f R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000000f R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000013 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000013 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000014 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000014 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000015 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000015 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000016 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000016 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=00000017 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000017 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=00000019 R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=00000019 R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000001a R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000001a R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=0000001c R=12345678 CCIN=0000 CC=0001\nbtw        A=12345678 B=0000001c R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000001d R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000001d R=12345678 CCIN=0000 CC=0000\nbtl        A=12345678 B=0000001e R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000001e R=12345678 CCIN=0000 CC=0001\nbtl        A=12345678 B=0000001f R=12345678 CCIN=0000 CC=0000\nbtw        A=12345678 B=0000001f R=12345678 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000001 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000001 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000002 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000002 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000003 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000003 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000004 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000004 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000005 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000005 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000006 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000006 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000007 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000007 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000000a R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=0000000a R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000000b R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000000b R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=0000000c R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=0000000c R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000000d R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000000d R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=0000000e R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=0000000e R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000000f R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000000f R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000013 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000013 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000014 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000014 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000015 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000015 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000016 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000016 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=00000017 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000017 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=00000019 R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=00000019 R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000001a R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000001a R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=0000001c R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000001c R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000001d R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000001d R=82345679 CCIN=0000 CC=0000\nbtl        A=82345679 B=0000001e R=82345679 CCIN=0000 CC=0000\nbtw        A=82345679 B=0000001e R=82345679 CCIN=0000 CC=0001\nbtl        A=82345679 B=0000001f R=82345679 CCIN=0000 CC=0001\nbtw        A=82345679 B=0000001f R=82345679 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000000 R=12345679 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000000 R=12345679 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000001 R=1234567a CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000001 R=1234567a CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000002 R=1234567c CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000002 R=1234567c CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000003 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000003 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000004 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000004 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000005 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000005 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000006 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000006 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000007 R=123456f8 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000007 R=123456f8 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000008 R=12345778 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000008 R=12345778 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000009 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000000a R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=0000000a R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000000b R=12345e78 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000000b R=12345e78 CCIN=0000 CC=0000\nbtsl       A=12345678 B=0000000c R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=0000000c R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000000d R=12347678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000000d R=12347678 CCIN=0000 CC=0000\nbtsl       A=12345678 B=0000000e R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=0000000e R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000000f R=1234d678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000000f R=1234d678 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000010 R=12355678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000010 R=12345679 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000011 R=12365678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000011 R=1234567a CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000012 R=1234567c CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000013 R=123c5678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000013 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000014 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000014 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000015 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000015 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000016 R=12745678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000016 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=00000017 R=12b45678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000017 R=123456f8 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000018 R=13345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=00000018 R=12345778 CCIN=0000 CC=0000\nbtsl       A=12345678 B=00000019 R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=00000019 R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000001a R=16345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000001a R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000001b R=1a345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000001b R=12345e78 CCIN=0000 CC=0000\nbtsl       A=12345678 B=0000001c R=12345678 CCIN=0000 CC=0001\nbtsw       A=12345678 B=0000001c R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000001d R=32345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000001d R=12347678 CCIN=0000 CC=0000\nbtsl       A=12345678 B=0000001e R=52345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000001e R=12345678 CCIN=0000 CC=0001\nbtsl       A=12345678 B=0000001f R=92345678 CCIN=0000 CC=0000\nbtsw       A=12345678 B=0000001f R=1234d678 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000000 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000001 R=8234567b CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000001 R=8234567b CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000002 R=8234567d CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000002 R=8234567d CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000003 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000003 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000004 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000004 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000005 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000005 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000006 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000006 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000007 R=823456f9 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000007 R=823456f9 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000008 R=82345779 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000008 R=82345779 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000009 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000000a R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=0000000a R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000000b R=82345e79 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000000b R=82345e79 CCIN=0000 CC=0000\nbtsl       A=82345679 B=0000000c R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=0000000c R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000000d R=82347679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000000d R=82347679 CCIN=0000 CC=0000\nbtsl       A=82345679 B=0000000e R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=0000000e R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000000f R=8234d679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000000f R=8234d679 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000010 R=82355679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000011 R=82365679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000011 R=8234567b CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000012 R=8234567d CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000013 R=823c5679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000013 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000014 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000014 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000015 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000015 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000016 R=82745679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000016 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=00000017 R=82b45679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000017 R=823456f9 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000018 R=83345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=00000018 R=82345779 CCIN=0000 CC=0000\nbtsl       A=82345679 B=00000019 R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=00000019 R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000001a R=86345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000001a R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000001b R=8a345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000001b R=82345e79 CCIN=0000 CC=0000\nbtsl       A=82345679 B=0000001c R=92345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000001c R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000001d R=a2345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000001d R=82347679 CCIN=0000 CC=0000\nbtsl       A=82345679 B=0000001e R=c2345679 CCIN=0000 CC=0000\nbtsw       A=82345679 B=0000001e R=82345679 CCIN=0000 CC=0001\nbtsl       A=82345679 B=0000001f R=82345679 CCIN=0000 CC=0001\nbtsw       A=82345679 B=0000001f R=8234d679 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000000 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000001 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000001 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000002 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000002 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000003 R=12345670 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000003 R=12345670 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000004 R=12345668 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000004 R=12345668 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000005 R=12345658 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000005 R=12345658 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000006 R=12345638 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000006 R=12345638 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000007 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000007 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000008 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000009 R=12345478 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000009 R=12345478 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000000a R=12345278 CCIN=0000 CC=0001\nbtrw       A=12345678 B=0000000a R=12345278 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000000b R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000000b R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=0000000c R=12344678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=0000000c R=12344678 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000000d R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000000d R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=0000000e R=12341678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=0000000e R=12341678 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000000f R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000000f R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000010 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000011 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000012 R=12305678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000012 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000013 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000013 R=12345670 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000014 R=12245678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000014 R=12345668 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000015 R=12145678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000015 R=12345658 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000016 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000016 R=12345638 CCIN=0000 CC=0001\nbtrl       A=12345678 B=00000017 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000017 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=00000018 R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=00000019 R=10345678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=00000019 R=12345478 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000001a R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000001a R=12345278 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000001b R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=0000001c R=02345678 CCIN=0000 CC=0001\nbtrw       A=12345678 B=0000001c R=12344678 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000001d R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000001d R=12345678 CCIN=0000 CC=0000\nbtrl       A=12345678 B=0000001e R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000001e R=12341678 CCIN=0000 CC=0001\nbtrl       A=12345678 B=0000001f R=12345678 CCIN=0000 CC=0000\nbtrw       A=12345678 B=0000001f R=12345678 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000000 R=82345678 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000000 R=82345678 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000001 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000001 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000002 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000002 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000003 R=82345671 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000003 R=82345671 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000004 R=82345669 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000004 R=82345669 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000005 R=82345659 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000005 R=82345659 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000006 R=82345639 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000006 R=82345639 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000007 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000007 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000008 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000009 R=82345479 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000009 R=82345479 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000000a R=82345279 CCIN=0000 CC=0001\nbtrw       A=82345679 B=0000000a R=82345279 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000000b R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000000b R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=0000000c R=82344679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=0000000c R=82344679 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000000d R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000000d R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=0000000e R=82341679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=0000000e R=82341679 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000000f R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000000f R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000010 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000010 R=82345678 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000011 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000012 R=82305679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000012 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000013 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000013 R=82345671 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000014 R=82245679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000014 R=82345669 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000015 R=82145679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000015 R=82345659 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000016 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000016 R=82345639 CCIN=0000 CC=0001\nbtrl       A=82345679 B=00000017 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000017 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=00000018 R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=00000019 R=80345679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=00000019 R=82345479 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000001a R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000001a R=82345279 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000001b R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=0000001c R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000001c R=82344679 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000001d R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000001d R=82345679 CCIN=0000 CC=0000\nbtrl       A=82345679 B=0000001e R=82345679 CCIN=0000 CC=0000\nbtrw       A=82345679 B=0000001e R=82341679 CCIN=0000 CC=0001\nbtrl       A=82345679 B=0000001f R=02345679 CCIN=0000 CC=0001\nbtrw       A=82345679 B=0000001f R=82345679 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000000 R=12345679 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000000 R=12345679 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000001 R=1234567a CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000001 R=1234567a CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000002 R=1234567c CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000002 R=1234567c CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000003 R=12345670 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000003 R=12345670 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000004 R=12345668 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000004 R=12345668 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000005 R=12345658 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000005 R=12345658 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000006 R=12345638 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000006 R=12345638 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000007 R=123456f8 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000007 R=123456f8 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000008 R=12345778 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000008 R=12345778 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000009 R=12345478 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000009 R=12345478 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000000a R=12345278 CCIN=0000 CC=0001\nbtcw       A=12345678 B=0000000a R=12345278 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000000b R=12345e78 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000000b R=12345e78 CCIN=0000 CC=0000\nbtcl       A=12345678 B=0000000c R=12344678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=0000000c R=12344678 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000000d R=12347678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000000d R=12347678 CCIN=0000 CC=0000\nbtcl       A=12345678 B=0000000e R=12341678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=0000000e R=12341678 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000000f R=1234d678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000000f R=1234d678 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000010 R=12355678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000010 R=12345679 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000011 R=12365678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000011 R=1234567a CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000012 R=12305678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000012 R=1234567c CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000013 R=123c5678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000013 R=12345670 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000014 R=12245678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000014 R=12345668 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000015 R=12145678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000015 R=12345658 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000016 R=12745678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000016 R=12345638 CCIN=0000 CC=0001\nbtcl       A=12345678 B=00000017 R=12b45678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000017 R=123456f8 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000018 R=13345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=00000018 R=12345778 CCIN=0000 CC=0000\nbtcl       A=12345678 B=00000019 R=10345678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=00000019 R=12345478 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000001a R=16345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000001a R=12345278 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000001b R=1a345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000001b R=12345e78 CCIN=0000 CC=0000\nbtcl       A=12345678 B=0000001c R=02345678 CCIN=0000 CC=0001\nbtcw       A=12345678 B=0000001c R=12344678 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000001d R=32345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000001d R=12347678 CCIN=0000 CC=0000\nbtcl       A=12345678 B=0000001e R=52345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000001e R=12341678 CCIN=0000 CC=0001\nbtcl       A=12345678 B=0000001f R=92345678 CCIN=0000 CC=0000\nbtcw       A=12345678 B=0000001f R=1234d678 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000000 R=82345678 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000000 R=82345678 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000001 R=8234567b CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000001 R=8234567b CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000002 R=8234567d CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000002 R=8234567d CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000003 R=82345671 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000003 R=82345671 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000004 R=82345669 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000004 R=82345669 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000005 R=82345659 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000005 R=82345659 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000006 R=82345639 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000006 R=82345639 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000007 R=823456f9 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000007 R=823456f9 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000008 R=82345779 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000008 R=82345779 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000009 R=82345479 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000009 R=82345479 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000000a R=82345279 CCIN=0000 CC=0001\nbtcw       A=82345679 B=0000000a R=82345279 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000000b R=82345e79 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000000b R=82345e79 CCIN=0000 CC=0000\nbtcl       A=82345679 B=0000000c R=82344679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=0000000c R=82344679 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000000d R=82347679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000000d R=82347679 CCIN=0000 CC=0000\nbtcl       A=82345679 B=0000000e R=82341679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=0000000e R=82341679 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000000f R=8234d679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000000f R=8234d679 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000010 R=82355679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000010 R=82345678 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000011 R=82365679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000011 R=8234567b CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000012 R=82305679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000012 R=8234567d CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000013 R=823c5679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000013 R=82345671 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000014 R=82245679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000014 R=82345669 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000015 R=82145679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000015 R=82345659 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000016 R=82745679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000016 R=82345639 CCIN=0000 CC=0001\nbtcl       A=82345679 B=00000017 R=82b45679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000017 R=823456f9 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000018 R=83345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=00000018 R=82345779 CCIN=0000 CC=0000\nbtcl       A=82345679 B=00000019 R=80345679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=00000019 R=82345479 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000001a R=86345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000001a R=82345279 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000001b R=8a345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000001b R=82345e79 CCIN=0000 CC=0000\nbtcl       A=82345679 B=0000001c R=92345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000001c R=82344679 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000001d R=a2345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000001d R=82347679 CCIN=0000 CC=0000\nbtcl       A=82345679 B=0000001e R=c2345679 CCIN=0000 CC=0000\nbtcw       A=82345679 B=0000001e R=82341679 CCIN=0000 CC=0001\nbtcl       A=82345679 B=0000001f R=02345679 CCIN=0000 CC=0001\nbtcw       A=82345679 B=0000001f R=8234d679 CCIN=0000 CC=0000\nbsrw       A=00000000 R=12345678 1\nbsrw       A=12340128 R=12340008 0\nbsfw       A=00000000 R=12345678 1\nbsfw       A=12340128 R=12340003 0\nbsrl       A=00000000 R=12345678 1\nbsrl       A=00340128 R=00000015 0\nbsfl       A=00000000 R=12345678 1\nbsfl       A=00340128 R=00000003 0\nimulb      A=1234561d B=00000004 R=12340074 CC=0000\nimulb      A=00000003 B=fffffffc R=0000fff4 CC=0000\nimulb      A=00000080 B=00000080 R=00004000 CC=0801\nimulb      A=00000010 B=00000010 R=00000100 CC=0801\nimulw      AH=00000000 AL=1234001d B=0000002d RH=00000000 RL=12340519 CC=0000\nimulw      AH=00000000 AL=00000017 B=ffffffd3 RH=0000ffff RL=0000fbf5 CC=0000\nimulw      AH=00000000 AL=00008000 B=00008000 RH=00004000 RL=00000000 CC=0801\nimulw      AH=00000000 AL=00000100 B=00000100 RH=00000001 RL=00000000 CC=0801\nimull      AH=00000000 AL=1234001d B=0000002d RH=00000003 RL=33240519 CC=0801\nimull      AH=00000000 AL=00000017 B=ffffffd3 RH=ffffffff RL=fffffbf5 CC=0000\nimull      AH=00000000 AL=80000000 B=80000000 RH=40000000 RL=00000000 CC=0801\nimull      AH=00000000 AL=00010000 B=00010000 RH=00000001 RL=00000000 CC=0801\nmulb       A=1234561d B=00000004 R=12340074 CC=0000\nmulb       A=00000003 B=fffffffc R=000002f4 CC=0801\nmulb       A=00000080 B=00000080 R=00004000 CC=0801\nmulb       A=00000010 B=00000010 R=00000100 CC=0801\nmulw       AH=00000000 AL=1234001d B=0000002d RH=00000000 RL=12340519 CC=0000\nmulw       AH=00000000 AL=00000017 B=ffffffd3 RH=00000016 RL=0000fbf5 CC=0801\nmulw       AH=00000000 AL=00008000 B=00008000 RH=00004000 RL=00000000 CC=0801\nmulw       AH=00000000 AL=00000100 B=00000100 RH=00000001 RL=00000000 CC=0801\nmull       AH=00000000 AL=1234001d B=0000002d RH=00000003 RL=33240519 CC=0801\nmull       AH=00000000 AL=00000017 B=ffffffd3 RH=00000016 RL=fffffbf5 CC=0801\nmull       AH=00000000 AL=80000000 B=80000000 RH=40000000 RL=00000000 CC=0801\nmull       AH=00000000 AL=00010000 B=00010000 RH=00000001 RL=00000000 CC=0801\nimulw      A=1234001d B=0000002d R=12340519 CC=0000\nimulw      A=00000017 B=ffffffd3 R=0000fbf5 CC=0000\nimulw      A=00008000 B=00008000 R=00000000 CC=0801\nimulw      A=00000100 B=00000100 R=00000000 CC=0801\nimull      A=1234001d B=0000002d R=33240519 CC=0801\nimull      A=00000017 B=ffffffd3 R=fffffbf5 CC=0000\nimull      A=80000000 B=80000000 R=00000000 CC=0801\nimull      A=00010000 B=00010000 R=00000000 CC=0801\nimulw im   A=0000002d B=00001234 R=00003324 CC=0000\nimulw im   A=ffffffd3 B=00000017 R=0000fbf5 CC=0000\nimulw im   A=00008000 B=80000000 R=00000000 CC=0000\nimulw im   A=00007fff B=00001000 R=0000f000 CC=0000\nimull im   A=0000002d B=00001234 R=00033324 CC=0000\nimull im   A=ffffffd3 B=00000017 R=fffffbf5 CC=0000\nimull im   A=00008000 B=80000000 R=00000000 CC=0000\nimull im   A=00007fff B=00001000 R=07fff000 CC=0000\nidivb      A=12341678 B=0000127e R=1234522d CC=0000\nidivb      A=43210123 B=fffffffb R=432101c6 CC=0000\nidivb      A=12340004 B=ffffffff R=123400fc CC=0000\nidivw      AH=00000000 AL=12345678 B=0000303b RH=0000263d RL=12340001 CC=0000\nidivw      AH=00000000 AL=ffffa549 B=ffffffd3 RH=0000000d RL=fffffc54 CC=0000\nidivw      AH=00000000 AL=12348000 B=ffffffff RH=00000000 RL=12348000 CC=0000\nidivw      AH=00012343 AL=12345678 B=81238567 RH=000120a6 RL=1234b65e CC=0000\nidivl      AH=00000000 AL=12345678 B=0000303b RH=00001198 RL=000060a0 CC=0000\nidivl      AH=00000000 AL=fffc70f9 B=ffffffd3 RH=0000002b RL=fa4fb93a CC=0000\nidivl      AH=00000000 AL=80000000 B=ffffffff RH=00000000 RL=80000000 CC=0000\nidivl      AH=00012343 AL=12345678 B=81234567 RH=4ba84b51 RL=fffdb441 CC=0000\ndivb       A=12341678 B=0000127e R=1234522d CC=0000\ndivb       A=43210123 B=fffffffb R=43212801 CC=0000\ndivb       A=12340004 B=ffffffff R=12340400 CC=0000\ndivw       AH=00000000 AL=12345678 B=0000303b RH=0000263d RL=12340001 CC=0000\ndivw       AH=00000000 AL=ffffa549 B=ffffffd3 RH=0000a549 RL=ffff0000 CC=0000\ndivw       AH=00000000 AL=12348000 B=ffffffff RH=00008000 RL=12340000 CC=0000\ndivw       AH=00012343 AL=12345678 B=81238567 RH=000145ab RL=123443ab CC=0000\ndivl       AH=00000000 AL=12345678 B=0000303b RH=00001198 RL=000060a0 CC=0000\ndivl       AH=00000000 AL=fffc70f9 B=ffffffd3 RH=fffc70f9 RL=00000000 CC=0000\ndivl       AH=00000000 AL=80000000 B=ffffffff RH=80000000 RL=00000000 CC=0000\ndivl       AH=00012343 AL=12345678 B=81234567 RH=2100133c RL=00024164 CC=0000\njne        0\nsetne      1\ncmovnel    R=12345678\ncmovnew    R=12345678\njne        1\nsetne      0\ncmovnel    R=00000001\ncmovnew    R=00000001\nje         1\nsete       0\ncmovel     R=00000001\ncmovew     R=00000001\nje         0\nsete       1\ncmovel     R=12345678\ncmovew     R=12345678\njl         0\nsetl       1\ncmovll     R=12345678\ncmovlw     R=12345678\njl         0\nsetl       0\ncmovll     R=12345678\ncmovlw     R=12345678\njl         0\nsetl       0\ncmovll     R=12345678\ncmovlw     R=12345678\njle        1\nsetle      1\ncmovlel    R=00000001\ncmovlew    R=00000001\njle        0\nsetle      1\ncmovlel    R=12345678\ncmovlew    R=12345678\njle        0\nsetle      0\ncmovlel    R=12345678\ncmovlew    R=12345678\njge        1\nsetge      0\ncmovgel    R=00000001\ncmovgew    R=00000001\njge        1\nsetge      1\ncmovgel    R=00000001\ncmovgew    R=00000001\njge        1\nsetge      0\ncmovgel    R=12345678\ncmovgew    R=12345678\njg         0\nsetg       0\ncmovgl     R=12345678\ncmovgw     R=12345678\njg         1\nsetg       0\ncmovgl     R=00000001\ncmovgw     R=00000001\njg         1\nsetg       1\ncmovgl     R=00000001\ncmovgw     R=00000001\njb         0\nsetb       1\ncmovbl     R=12345678\ncmovbw     R=12345678\njb         0\nsetb       0\ncmovbl     R=12345678\ncmovbw     R=12345678\njb         1\nsetb       1\ncmovbl     R=00000001\ncmovbw     R=00000001\njbe        1\nsetbe      1\ncmovbel    R=00000001\ncmovbew    R=00000001\njbe        0\nsetbe      1\ncmovbel    R=12345678\ncmovbew    R=12345678\njbe        1\nsetbe      1\ncmovbel    R=00000001\ncmovbew    R=00000001\njae        1\nsetae      0\ncmovael    R=00000001\ncmovaew    R=00000001\njae        1\nsetae      1\ncmovael    R=00000001\ncmovaew    R=00000001\njae        0\nsetae      0\ncmovael    R=12345678\ncmovaew    R=12345678\nja         0\nseta       0\ncmoval     R=12345678\ncmovaw     R=12345678\nja         1\nseta       0\ncmoval     R=00000001\ncmovaw     R=00000001\nja         0\nseta       0\ncmoval     R=12345678\ncmovaw     R=12345678\njp         1\nsetp       1\ncmovpl     R=00000001\ncmovpw     R=00000001\njp         0\nsetp       1\ncmovpl     R=12345678\ncmovpw     R=12345678\njnp        0\nsetnp      0\ncmovnpl    R=12345678\ncmovnpw    R=12345678\njnp        1\nsetnp      0\ncmovnpl    R=00000001\ncmovnpw    R=00000001\njo         0\nseto       0\ncmovol     R=12345678\ncmovow     R=12345678\njo         0\nseto       0\ncmovol     R=00000001\ncmovow     R=00000001\njno        1\nsetno      1\ncmovnol    R=00000001\ncmovnow    R=00000001\njno        1\nsetno      1\ncmovnol    R=12345678\ncmovnow    R=12345678\njs         0\nsets       1\ncmovsl     R=00000001\ncmovsw     R=00000001\njs         0\nsets       0\ncmovsl     R=12345678\ncmovsw     R=12345678\njs         0\nsets       0\ncmovsl     R=12345678\ncmovsw     R=12345678\njns        1\nsetns      0\ncmovnsl    R=12345678\ncmovnsw    R=12345678\njns        1\nsetns      1\ncmovnsl    R=00000001\ncmovnsw    R=00000001\njns        1\nsetns      1\ncmovnsl    R=00000001\ncmovnsw    R=00000001\na=2.000000 b=3.000000 a+b=5.000000\na=2.000000 b=3.000000 a-b=-1.000000\na=2.000000 b=3.000000 a*b=6.000000\na=2.000000 b=3.000000 a/b=0.666667\na=2.000000 b=3.000000 fmod(a, b)=2.000000\na=2.000000 sqrt(a)=1.414214\na=2.000000 sin(a)=0.909297\na=2.000000 cos(a)=-0.416147\na=2.000000 tan(a)=-2.185040\na=2.000000 log(a)=0.693147\na=2.000000 exp(a)=7.389056\na=2.000000 b=3.000000 atan2(a, b)=0.588003\na=2.000000 asin(sin(a))=1.141593\na=2.000000 acos(cos(a))=2.000000\na=2.000000 atan(tan(a))=-1.141593\na=1.400000 b=-5.000000 a+b=-3.600000\na=1.400000 b=-5.000000 a-b=6.400000\na=1.400000 b=-5.000000 a*b=-7.000000\na=1.400000 b=-5.000000 a/b=-0.280000\na=1.400000 b=-5.000000 fmod(a, b)=1.400000\na=1.400000 sqrt(a)=1.183216\na=1.400000 sin(a)=0.985450\na=1.400000 cos(a)=0.169967\na=1.400000 tan(a)=5.797884\na=1.400000 log(a)=0.336472\na=1.400000 exp(a)=4.055200\na=1.400000 b=-5.000000 atan2(a, b)=2.868584\na=1.400000 asin(sin(a))=1.400000\na=1.400000 acos(cos(a))=1.400000\na=1.400000 atan(tan(a))=1.400000\nfcom(2.000000 -1.000000)=0000\nfucom(2.000000 -1.000000)=0000\nfcomi(2.000000 -1.000000)=0000 00\nfucomi(2.000000 -1.000000)=0000 00\nfxam(2.000000)=0400\nfcom(2.000000 2.000000)=4000\nfucom(2.000000 2.000000)=4000\nfcomi(2.000000 2.000000)=0000 40\nfucomi(2.000000 2.000000)=0000 40\nfxam(2.000000)=0400\nfcom(2.000000 3.000000)=0100\nfucom(2.000000 3.000000)=0100\nfcomi(2.000000 3.000000)=0000 01\nfucomi(2.000000 3.000000)=0000 01\nfxam(2.000000)=0400\nfcom(2.000000 -nan)=4500\nfucom(2.000000 -nan)=4500\nfcomi(2.000000 -nan)=0000 45\nfucomi(2.000000 -nan)=0000 45\nfxam(2.000000)=0400\nfcom(-nan -1.000000)=4500\nfucom(-nan -1.000000)=4500\nfcomi(-nan -1.000000)=0000 45\nfucomi(-nan -1.000000)=0000 45\nfxam(-nan)=0300\nfcom(-inf -1.000000)=0100\nfucom(-inf -1.000000)=0100\nfcomi(-inf -1.000000)=0000 01\nfucomi(-inf -1.000000)=0000 01\nfxam(-inf)=0700\nfcom(inf -1.000000)=0000\nfucom(inf -1.000000)=0000\nfcomi(inf -1.000000)=0000 00\nfucomi(inf -1.000000)=0000 00\nfxam(inf)=0500\n(float)0.500000 = 0.500000\n(long double)0.500000 = 0.500000\na=3fe0000000000000\nla=8000000000000000 3ffe\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(short)a = 1\n(int)a = 1\n(int64_t)a = 0000000000000001\nrint(a) = 1.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(float)-0.500000 = -0.500000\n(long double)-0.500000 = -0.500000\na=bfe0000000000000\nla=8000000000000000 bffe\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(short)a = -1\n(int)a = -1\n(int64_t)a = ffffffffffffffff\nrint(a) = -1.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(float)0.142857 = 0.142857\n(long double)0.142857 = 0.142857\na=3fc2492492492492\nla=9249249249249000 3ffc\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(short)a = 1\n(int)a = 1\n(int64_t)a = 0000000000000001\nrint(a) = 1.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = 0.000000\n(float)-0.111111 = -0.111111\n(long double)-0.111111 = -0.111111\na=bfbc71c71c71c71c\nla=e38e38e38e38e000 bffb\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(short)a = -1\n(int)a = -1\n(int64_t)a = ffffffffffffffff\nrint(a) = -1.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(short)a = 0\n(int)a = 0\n(int64_t)a = 0000000000000000\nrint(a) = -0.000000\n(float)32768.000000 = 32768.000000\n(long double)32768.000000 = 32768.000000\na=40e0000000000000\nla=8000000000000000 400e\n(short)a = -32768\n(int)a = 32768\n(int64_t)a = 0000000000008000\nrint(a) = 32768.000000\n(short)a = -32768\n(int)a = 32768\n(int64_t)a = 0000000000008000\nrint(a) = 32768.000000\n(short)a = -32768\n(int)a = 32768\n(int64_t)a = 0000000000008000\nrint(a) = 32768.000000\n(short)a = -32768\n(int)a = 32768\n(int64_t)a = 0000000000008000\nrint(a) = 32768.000000\n(float)-100000000000000000000.000000 = -100000002004087734272.000000\n(long double)-100000000000000000000.000000 = -100000000000000000000.000000\na=c415af1d78b58c40\nla=ad78ebc5ac620000 c041\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -100000000000000000000.000000\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -100000000000000000000.000000\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -100000000000000000000.000000\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -100000000000000000000.000000\n(float)-inf = -inf\n(long double)-inf = -inf\na=fff0000000000000\nla=8000000000000000 ffff\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -inf\n(float)inf = inf\n(long double)inf = inf\na=7ff0000000000000\nla=8000000000000000 7fff\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = inf\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = inf\n(float)-nan = -nan\n(long double)-nan = -nan\na=fff8000000000000\nla=c000000000000000 ffff\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -nan\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -nan\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -nan\n(short)a = -32768\n(int)a = -2147483648\n(int64_t)a = 8000000000000000\nrint(a) = -nan\nfld1= 1.000000\nfldl2t= 3.321928\nfldl2e= 1.442695\nfldpi= 3.141593\nfldlg2= 0.301030\nfldln2= 0.693147\nfldz= 0.000000\nfcmovb eflags=0x0000-> 1.000000\nfcmove eflags=0x0000-> 1.000000\nfcmovbe eflags=0x0000-> 1.000000\nfcmovnb eflags=0x0000-> 2.000000\nfcmovne eflags=0x0000-> 2.000000\nfcmovnbe eflags=0x0000-> 2.000000\nfcmovb eflags=0x0001-> 2.000000\nfcmove eflags=0x0001-> 1.000000\nfcmovbe eflags=0x0001-> 2.000000\nfcmovnb eflags=0x0001-> 1.000000\nfcmovne eflags=0x0001-> 2.000000\nfcmovnbe eflags=0x0001-> 1.000000\nfcmovb eflags=0x0040-> 1.000000\nfcmove eflags=0x0040-> 2.000000\nfcmovbe eflags=0x0040-> 2.000000\nfcmovnb eflags=0x0040-> 2.000000\nfcmovne eflags=0x0040-> 1.000000\nfcmovnbe eflags=0x0040-> 1.000000\nfcmovb eflags=0x0041-> 2.000000\nfcmove eflags=0x0041-> 2.000000\nfcmovbe eflags=0x0041-> 2.000000\nfcmovnb eflags=0x0041-> 1.000000\nfcmovne eflags=0x0041-> 1.000000\nfcmovnbe eflags=0x0041-> 1.000000\nfcmovu eflags=0x0000-> 1.000000\nfcmovu eflags=0x0004-> 2.000000\nfcmovnu eflags=0x0000-> 2.000000\nfcmovnu eflags=0x0004-> 1.000000\nxchgl      A=fbca7654 B=12345678\nxchgw      A=12347654 B=fbca5678\nxchgb      A=12345654 B=fbca7678\nxchgl      A=fbca7654 B=12345678\nxchgw      A=12347654 B=fbca5678\nxchgb      A=12345654 B=fbca7678\nxaddl      A=fbca7654 B=0dfecccc\nxaddw      A=12347654 B=fbcacccc\nxaddb      A=12345654 B=fbca76cc\nxaddl same res=2468acf0\nxaddl      A=fbca7654 B=0dfecccc\nxaddw      A=12347654 B=fbcacccc\nxaddb      A=12345654 B=fbca76cc\ncmpxchgl   EAX=fbca7654 A=12345678 C=12345678\ncmpxchgw   EAX=fbca7654 A=12345678 C=fbca5678\ncmpxchgb   EAX=fbca7654 A=12345678 C=fbca7678\ncmpxchgl   EAX=fffefdfc A=12345678 C=fbca7654\ncmpxchgw   EAX=fffefdfc A=12345678 C=fbca7654\ncmpxchgb   EAX=fffefdfc A=12345678 C=fbca7654\ncmpxchg8b: eax=65423456 edx=000fbca7 op1=000fbca765423456 CC=00\ncmpxchg8b: eax=6789abcd edx=00012345 op1=0006532432432434 CC=40\nstosb      ESI=00000800 EDI=00000811 EAX=12345678 ECX=00000011 EFL=0000\nstosw      ESI=00000800 EDI=00000812 EAX=12345678 ECX=00000011 EFL=0000\nstosl      ESI=00000800 EDI=00000814 EAX=12345678 ECX=00000011 EFL=0000\nstosb      ESI=00000800 EDI=0000080f EAX=12345678 ECX=00000011 EFL=0000\nstosw      ESI=00000800 EDI=0000080e EAX=12345678 ECX=00000011 EFL=0000\nstosl      ESI=00000800 EDI=0000080c EAX=12345678 ECX=00000011 EFL=0000\nrep stosb  ESI=00000800 EDI=00000821 EAX=12345678 ECX=00000000 EFL=0000\nrep stosw  ESI=00000800 EDI=00000832 EAX=12345678 ECX=00000000 EFL=0000\nrep stosl  ESI=00000800 EDI=00000854 EAX=12345678 ECX=00000000 EFL=0000\nrep stosb  ESI=00000800 EDI=000007ff EAX=12345678 ECX=00000000 EFL=0000\nrep stosw  ESI=00000800 EDI=000007ee EAX=12345678 ECX=00000000 EFL=0000\nrep stosl  ESI=00000800 EDI=000007cc EAX=12345678 ECX=00000000 EFL=0000\nlodsb      ESI=00000801 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsw      ESI=00000802 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsl      ESI=00000804 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsb      ESI=000007ff EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsw      ESI=000007fe EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsl      ESI=000007fc EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nrep lodsb  ESI=00000811 EDI=00000810 EAX=12345678 ECX=00000000 EFL=0000\nrep lodsw  ESI=00000822 EDI=00000810 EAX=12345678 ECX=00000000 EFL=0000\nrep lodsl  ESI=00000844 EDI=00000810 EAX=12345678 ECX=00000000 EFL=0000\nrep lodsb  ESI=000007ef EDI=00000810 EAX=12345678 ECX=00000000 EFL=0000\nrep lodsw  ESI=000007de EDI=00000810 EAX=12345678 ECX=00000000 EFL=0000\nrep lodsl  ESI=000007bc EDI=00000810 EAX=19181716 ECX=00000000 EFL=0000\nmovsb      ESI=00000801 EDI=00000811 EAX=12345678 ECX=00000011 EFL=0000\nmovsw      ESI=00000802 EDI=00000812 EAX=12345678 ECX=00000011 EFL=0000\nmovsl      ESI=00000804 EDI=00000814 EAX=12345678 ECX=00000011 EFL=0000\nmovsb      ESI=000007ff EDI=0000080f EAX=12345678 ECX=00000011 EFL=0000\nmovsw      ESI=000007fe EDI=0000080e EAX=12345678 ECX=00000011 EFL=0000\nmovsl      ESI=000007fc EDI=0000080c EAX=12345678 ECX=00000011 EFL=0000\nrep movsb  ESI=00000811 EDI=00000821 EAX=12345678 ECX=00000000 EFL=0000\nrep movsw  ESI=00000822 EDI=00000832 EAX=12345678 ECX=00000000 EFL=0000\nrep movsl  ESI=00000844 EDI=00000854 EAX=12345678 ECX=00000000 EFL=0000\nrep movsb  ESI=000007ef EDI=000007ff EAX=12345678 ECX=00000000 EFL=0000\nrep movsw  ESI=000007de EDI=000007ee EAX=12345678 ECX=00000000 EFL=0000\nrep movsl  ESI=000007bc EDI=000007cc EAX=12345678 ECX=00000000 EFL=0000\nlodsb      ESI=00000801 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsw      ESI=00000802 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsl      ESI=00000804 EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsb      ESI=000007ff EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsw      ESI=000007fe EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nlodsl      ESI=000007fc EDI=00000810 EAX=12345678 ECX=00000011 EFL=0000\nscasb      ESI=00000800 EDI=00000811 EAX=12345678 ECX=00000011 EFL=0044\nscasw      ESI=00000800 EDI=00000812 EAX=12345678 ECX=00000011 EFL=0044\nscasl      ESI=00000800 EDI=00000814 EAX=12345678 ECX=00000011 EFL=0044\nscasb      ESI=00000800 EDI=0000080f EAX=12345678 ECX=00000011 EFL=0044\nscasw      ESI=00000800 EDI=0000080e EAX=12345678 ECX=00000011 EFL=0044\nscasl      ESI=00000800 EDI=0000080c EAX=12345678 ECX=00000011 EFL=0044\nrepz scasb ESI=00000800 EDI=00000812 EAX=12345678 ECX=0000000f EFL=0004\nrepz scasw ESI=00000800 EDI=00000814 EAX=12345678 ECX=0000000f EFL=0004\nrepz scasl ESI=00000800 EDI=00000854 EAX=12345678 ECX=00000000 EFL=0044\nrepz scasb ESI=00000800 EDI=0000080e EAX=12345678 ECX=0000000f EFL=0004\nrepz scasw ESI=00000800 EDI=0000080c EAX=12345678 ECX=0000000f EFL=0004\nrepz scasl ESI=00000800 EDI=000007d8 EAX=12345678 ECX=00000003 EFL=0085\nrepnz scasb ESI=00000800 EDI=00000811 EAX=12345678 ECX=00000010 EFL=0044\nrepnz scasw ESI=00000800 EDI=00000812 EAX=12345678 ECX=00000010 EFL=0044\nrepnz scasl ESI=00000800 EDI=00000814 EAX=12345678 ECX=00000010 EFL=0044\nrepnz scasb ESI=00000800 EDI=0000080f EAX=12345678 ECX=00000010 EFL=0044\nrepnz scasw ESI=00000800 EDI=0000080e EAX=12345678 ECX=00000010 EFL=0044\nrepnz scasl ESI=00000800 EDI=0000080c EAX=12345678 ECX=00000010 EFL=0044\ncmpsb      ESI=00000801 EDI=00000811 EAX=12345678 ECX=00000011 EFL=0044\ncmpsw      ESI=00000802 EDI=00000812 EAX=12345678 ECX=00000011 EFL=0044\ncmpsl      ESI=00000804 EDI=00000814 EAX=12345678 ECX=00000011 EFL=0044\ncmpsb      ESI=000007ff EDI=0000080f EAX=12345678 ECX=00000011 EFL=0044\ncmpsw      ESI=000007fe EDI=0000080e EAX=12345678 ECX=00000011 EFL=0044\ncmpsl      ESI=000007fc EDI=0000080c EAX=12345678 ECX=00000011 EFL=0044\nrepz cmpsb ESI=00000811 EDI=00000821 EAX=12345678 ECX=00000000 EFL=0044\nrepz cmpsw ESI=00000822 EDI=00000832 EAX=12345678 ECX=00000000 EFL=0044\nrepz cmpsl ESI=00000844 EDI=00000854 EAX=12345678 ECX=00000000 EFL=0044\nrepz cmpsb ESI=000007ef EDI=000007ff EAX=12345678 ECX=00000000 EFL=0044\nrepz cmpsw ESI=000007de EDI=000007ee EAX=12345678 ECX=00000000 EFL=0044\nrepz cmpsl ESI=000007d8 EDI=000007e8 EAX=12345678 ECX=00000007 EFL=0014\nrepnz cmpsb ESI=00000801 EDI=00000811 EAX=12345678 ECX=00000010 EFL=0044\nrepnz cmpsw ESI=00000802 EDI=00000812 EAX=12345678 ECX=00000010 EFL=0044\nrepnz cmpsl ESI=00000804 EDI=00000814 EAX=12345678 ECX=00000010 EFL=0044\nrepnz cmpsb ESI=000007ff EDI=0000080f EAX=12345678 ECX=00000010 EFL=0044\nrepnz cmpsw ESI=000007fe EDI=0000080e EAX=12345678 ECX=00000010 EFL=0044\nrepnz cmpsl ESI=000007fc EDI=0000080c EAX=12345678 ECX=00000010 EFL=0044\nlea 0x4000 = 00004000\nlea (%%eax) = 00000001\nlea (%%ebx) = 00000002\nlea (%%ecx) = 00000004\nlea (%%edx) = 00000008\nlea (%%esi) = 00000010\nlea (%%edi) = 00000020\nlea 0x40(%%eax) = 00000041\nlea 0x40(%%ebx) = 00000042\nlea 0x40(%%ecx) = 00000044\nlea 0x40(%%edx) = 00000048\nlea 0x40(%%esi) = 00000050\nlea 0x40(%%edi) = 00000060\nlea 0x4000(%%eax) = 00004001\nlea 0x4000(%%ebx) = 00004002\nlea 0x4000(%%ecx) = 00004004\nlea 0x4000(%%edx) = 00004008\nlea 0x4000(%%esi) = 00004010\nlea 0x4000(%%edi) = 00004020\nlea (%%eax, %%ecx) = 00000005\nlea (%%ebx, %%edx) = 0000000a\nlea (%%ecx, %%ecx) = 00000008\nlea (%%edx, %%ecx) = 0000000c\nlea (%%esi, %%ecx) = 00000014\nlea (%%edi, %%ecx) = 00000024\nlea 0x40(%%eax, %%ecx) = 00000045\nlea 0x4000(%%ebx, %%edx) = 0000400a\nlea (%%ecx, %%ecx, 2) = 0000000c\nlea (%%edx, %%ecx, 4) = 00000018\nlea (%%esi, %%ecx, 8) = 00000030\nlea (,%%eax, 2) = 00000002\nlea (,%%ebx, 4) = 00000008\nlea (,%%ecx, 8) = 00000020\nlea 0x40(,%%eax, 2) = 00000042\nlea 0x40(,%%ebx, 4) = 00000048\nlea 0x40(,%%ecx, 8) = 00000060\nlea -10(%%ecx, %%ecx, 2) = 00000002\nlea -10(%%edx, %%ecx, 4) = 0000000e\nlea -10(%%esi, %%ecx, 8) = 00000026\nlea 0x4000(%%ecx, %%ecx, 2) = 0000400c\nlea 0x4000(%%edx, %%ecx, 4) = 00004018\nlea 0x4000(%%esi, %%ecx, 8) = 00004030\ncbw        A=8234a6f8 R=8234fff8\ncwde       A=8234a6f8 R=ffffa6f8\ncwd        A=8234a6f8 R=8234a6f8:8345ffff\ncdq        A=8234a6f8 R=8234a6f8:ffffffff\nbswapl    : A=12345678 R=78563412\npunpcklbw: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f4529677c23cdc65898ba69d748ab73\npunpcklbw: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f00767c25625ac2080854542727f8f8\npunpcklwd: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f2945677ccd23c658ba9869d7ab4873\npunpcklwd: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f76007c255a62c20854085427f827f8\npunpckldq: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f297ccd456723c658bad7ab98694873\npunpckldq: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f76255a007c62c2085427f8085427f8\npacksswb : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=7f7f80807f7f7f80807f807f7f7f807f\npacksswb : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=8080807f7f7f7f7f7f80807f7c7f7f7f\npcmpgtb  : a=456723c698694873 b=1f297ccd58bad7ab r=ffff000000ffffff\npcmpgtb  : a=007c62c2085427f8 b=0f76255a085427f8 r=00ffff0000000000\npcmpgtb  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=00ffffff00ffff00ffff000000ffffff\npcmpgtb  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=ff000000ffff000000ffff0000000000\npcmpgtw  : a=456723c698694873 b=1f297ccd58bad7ab r=ffff00000000ffff\npcmpgtw  : a=007c62c2085427f8 b=0f76255a085427f8 r=0000ffff00000000\npcmpgtw  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=0000ffff0000ffffffff00000000ffff\npcmpgtw  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=ffff0000ffff00000000ffff00000000\npcmpgtd  : a=456723c698694873 b=1f297ccd58bad7ab r=ffffffff00000000\npcmpgtd  : a=007c62c2085427f8 b=0f76255a085427f8 r=0000000000000000\npcmpgtd  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=0000000000000000ffffffff00000000\npcmpgtd  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=ffffffffffffffff0000000000000000\npackuswb : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ffff0000ffffff0000ff00ffffff00ff\npackuswb : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=000000ffffffffffff0000ff7cffffff\npunpckhbw: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41dcf2511e5cfbffa994e34ae15846ec\npunpckhbw: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c223331be9e9e8e8c4cdc9e743439a8d\npunpckhwd: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f2dc511efb5cffa9e3944ae14658ec\npunpckhwd: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233231be9e8e9e8c4c9cde7439a438d\npunpckhdq: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f21efbdc515cffa9e3e146944a58ec\npunpckhdq: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8231be9e8c4c9439acde7438d\npackssdw : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=7fff80007fff7fff800080007fff8000\npackssdw : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=800080007fff7fff7fff80007fff7fff\npcmpeqb  : a=456723c698694873 b=1f297ccd58bad7ab r=0000000000000000\npcmpeqb  : a=007c62c2085427f8 b=0f76255a085427f8 r=00000000ffffffff\npcmpeqb  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=00000000000000000000000000000000\npcmpeqb  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0000ffff0000ff0000000000ffffffff\npcmpeqw  : a=456723c698694873 b=1f297ccd58bad7ab r=0000000000000000\npcmpeqw  : a=007c62c2085427f8 b=0f76255a085427f8 r=00000000ffffffff\npcmpeqw  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=00000000000000000000000000000000\npcmpeqw  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0000ffff0000000000000000ffffffff\npcmpeqd  : a=456723c698694873 b=1f297ccd58bad7ab r=0000000000000000\npcmpeqd  : a=007c62c2085427f8 b=0f76255a085427f8 r=00000000ffffffff\npcmpeqd  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=00000000000000000000000000000000\npcmpeqd  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=000000000000000000000000ffffffff\npaddq    : a=456723c698694873 b=1f297ccd58bad7ab r=6490a093f124201e\npaddq    : a=007c62c2085427f8 b=0f76255a085427f8 r=0ff2881c10a84ff0\npaddq    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1e437bfb3e2e3a326490a093f124201e\npaddq    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54fd3d192b087270ff2881c10a84ff0\npmullw   : a=456723c698694873 b=1f297ccd58bad7ab r=967f8d8ed44af9d1\npmullw   : a=007c62c2085427f8 b=0f76255a085427f8 r=7d28c2345b908040\npmullw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=d5921005579ebc88967f8d8ed44af9d1\npmullw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=74612240865f89d27d28c2345b908040\npsubusb  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9b003e04000000a6263e000040000000\npsubusb  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=00000000091e000000063d6800000000\npsubusw  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9a5f3e0400000000263e00003faf0000\npsubusw  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=00000000091e000000003d6800000000\npminub   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41511efb944a58461f2923c658694873\npminub   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=231be9e8c4c9438d0076255a085427f8\npand     : a=456723c698694873 b=1f297ccd58bad7ab r=052120c418284023\npand     : a=007c62c2085427f8 b=0f76255a085427f8 r=00742042085427f8\npand     : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=40501cfb80424044052120c418284023\npand     : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0213e9e8c4c1438800742042085427f8\npaddusb  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ffff7affffffffff64909ffff0ffffff\npaddusb  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54effffffff86ff0ff287ff10a84eff\npaddusw  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ffff7bfaffffffff6490a093f123ffff\npaddusw  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54effffffff87270ff2881c10a84ff0\npmaxub   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=dcf25cffa9e3e1ec45677ccd98bad7ab\npmaxub   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8cde7439a0f7c62c2085427f8\npandn    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=01a2020029a1a1021a085c0940929788\npandn    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c0200000000800120f02051800000000\npmulhuw  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=38c00b4162684e3f0872117034d23d08\npmulhuw  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=1aa1d5b89e4611d600070e680045063d\npmulhw   : a=456723c698694873 b=1f297ccd58bad7ab r=08721170dc18f495\npmulhw   : a=007c62c2085427f8 b=0f76255a085427f8 r=00070e680045063d\npmulhw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=f6ce0b41243bf55308721170dc18f495\npmulhw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=f78601e80b9611d600070e680045063d\npsubsb   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9b5f3e04eb6777a6263ea7f9807f717f\npsubsb   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=61e80000091e00f3f1063d8000000000\npsubsw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9a5f3e04ea6777a6263ea6f9800070c8\npsubsw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=60e80000091efff3f1063d6800000000\npminsw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=dc511efb944ae1461f2923c69869d7ab\npminsw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8c4c9438d007c255a085427f8\npor      : a=456723c698694873 b=1f297ccd58bad7ab r=5f6f7fcfd8fbdffb\npor      : a=007c62c2085427f8 b=0f76255a085427f8 r=0f7e67da085427f8\npor      : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ddf35effbdebf9ee5f6f7fcfd8fbdffb\npor      : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e33be9e8cdef439f0f7e67da085427f8\npaddsb   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1d437afa802d3932647f7f93f0231f1e\npaddsb   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54ed2d091b07f800f7f7f1c107f4ef0\npaddsw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1e437bfa80003a3264907ffff123201e\npaddsw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54ed3d092b07fff0ff27fff10a84ff0\npmaxsw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f25cffa9e358ec45677ccd58ba4873\npmaxsw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=231be9e8cde7439a0f7662c2085427f8\npxor     : a=456723c698694873 b=1f297ccd58bad7ab r=5a4e5f0bc0d39fd8\npxor     : a=007c62c2085427f8 b=0f76255a085427f8 r=0f0a479800000000\npxor     : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9da342043da9b9aa5a4e5f0bc0d39fd8\npxor     : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e1280000092e00170f0a479800000000\npmuludq  : a=456723c698694873 b=1f297ccd58bad7ab r=34d36dcc65b9f9d1\npmuludq  : a=007c62c2085427f8 b=0f76255a085427f8 r=00455e29c0fd8040\npmuludq  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=6269151e89bfbc8834d36dcc65b9f9d1\npmuludq  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=9e46f0ab618189d200455e29c0fd8040\npmaddwd  : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=020fe597198f142619e3240dd0aece1b\npmaddwd  : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=f96e96a11d6d10310e703f5c0682dbd0\npsadbw   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=000000000000035b000000000000021c\npsadbw   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=00000000000000eb00000000000000ba\npsubb    : a=456723c698694873 b=1f297ccd58bad7ab r=263ea7f940af71c8\npsubb    : a=007c62c2085427f8 b=0f76255a085427f8 r=f1063d6800000000\npsubb    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9b5f3e04eb6777a6263ea7f940af71c8\npsubb    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=61e80000091e00f3f1063d6800000000\npsubw    : a=456723c698694873 b=1f297ccd58bad7ab r=263ea6f93faf70c8\npsubw    : a=007c62c2085427f8 b=0f76255a085427f8 r=f1063d6800000000\npsubw    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9a5f3e04ea6777a6263ea6f93faf70c8\npsubw    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=60e80000091efff3f1063d6800000000\npsubd    : a=456723c698694873 b=1f297ccd58bad7ab r=263da6f93fae70c8\npsubd    : a=007c62c2085427f8 b=0f76255a085427f8 r=f1063d6800000000\npsubd    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9a5f3e04ea6677a6263da6f93fae70c8\npsubd    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=60e80000091dfff3f1063d6800000000\npsubq    : a=456723c698694873 b=1f297ccd58bad7ab r=263da6f93fae70c8\npsubq    : a=007c62c2085427f8 b=0f76255a085427f8 r=f1063d6800000000\npsubq    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9a5f3e03ea6677a6263da6f93fae70c8\npsubq    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=60e80000091dfff3f1063d6800000000\npaddb    : a=456723c698694873 b=1f297ccd58bad7ab r=64909f93f0231f1e\npaddb    : a=007c62c2085427f8 b=0f76255a085427f8 r=0ff2871c10a84ef0\npaddb    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1d437afa3d2d393264909f93f0231f1e\npaddb    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54ed2d091b086270ff2871c10a84ef0\npaddw    : a=456723c698694873 b=1f297ccd58bad7ab r=6490a093f123201e\npaddw    : a=007c62c2085427f8 b=0f76255a085427f8 r=0ff2881c10a84ff0\npaddw    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1e437bfa3e2d3a326490a093f123201e\npaddw    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54ed3d092b087270ff2881c10a84ff0\npaddd    : a=456723c698694873 b=1f297ccd58bad7ab r=6490a093f124201e\npaddd    : a=007c62c2085427f8 b=0f76255a085427f8 r=0ff2881c10a84ff0\npaddd    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1e437bfa3e2e3a326490a093f124201e\npaddd    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e54fd3d092b087270ff2881c10a84ff0\npavgb    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=8fa23dfd9f979d99324850ca7892908f\npavgb    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=7327e9e8c9d843940879448e085427f8\npavgw    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=8f223dfd9f179d193248504a7892900f\npavgw    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=72a7e9e8c958439407f9440e085427f8\npinsrw   : r=0000000048730000\npinsrw   : r=0000000056780000\npinsrw   : r=72a7e9e84873439407f9440e085427f8\npinsrw   : r=72a7e9e85678439407f9440e085427f8\npextrw   : r=0000944a\npmovmskb : r=00000018\npmovmskb : r=00009918\npunpcklqdq: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f297ccd58bad7ab456723c698694873\npunpcklqdq: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f76255a085427f8007c62c2085427f8\npunpckhqdq: a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f21efba9e3e146dc515cff944a58ec\npunpckhqdq: a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8c4c9439a231be9e8cde7438d\nandps    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=40501cfb80424044052120c418284023\nandps    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0213e9e8c4c1438800742042085427f8\nandpd    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=40501cfb80424044052120c418284023\nandpd    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0213e9e8c4c1438800742042085427f8\nandnps   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=01a2020029a1a1021a085c0940929788\nandnps   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c0200000000800120f02051800000000\nandnpd   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=01a2020029a1a1021a085c0940929788\nandnpd   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c0200000000800120f02051800000000\norps     : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ddf35effbdebf9ee5f6f7fcfd8fbdffb\norps     : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e33be9e8cdef439f0f7e67da085427f8\norpd     : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=ddf35effbdebf9ee5f6f7fcfd8fbdffb\norpd     : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e33be9e8cdef439f0f7e67da085427f8\nxorps    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9da342043da9b9aa5a4e5f0bc0d39fd8\nxorps    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e1280000092e00170f0a479800000000\nxorpd    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=9da342043da9b9aa5a4e5f0bc0d39fd8\nxorpd    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=e1280000092e00170f0a479800000000\nunpcklps : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f297ccd456723c658bad7ab98694873\nunpcklps : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f76255a007c62c2085427f8085427f8\nunpcklpd : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=1f297ccd58bad7ab456723c698694873\nunpcklpd : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=0f76255a085427f8007c62c2085427f8\nunpckhps : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f21efbdc515cffa9e3e146944a58ec\nunpckhps : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8231be9e8c4c9439acde7438d\nunpckhpd : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=41f21efba9e3e146dc515cff944a58ec\nunpckhpd : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=c233e9e8c4c9439a231be9e8cde7438d\nshufps   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab ib=78 r=1f297ccd41f21efb944a58ec98694873\nshufpd   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab ib=02 r=41f21efba9e3e146456723c698694873\npshufd   : a=dc515cff944a58ec456723c698694873 ib=78 r=456723c6dc515cff944a58ec98694873\npshufd   : a=231be9e8cde7438d007c62c2085427f8 ib=78 r=007c62c2231be9e8cde7438d085427f8\npshuflw  : a=dc515cff944a58ec456723c698694873 ib=78 r=dc515cff944a58ec9869456723c64873\npshuflw  : a=231be9e8cde7438d007c62c2085427f8 ib=78 r=231be9e8cde7438d0854007c62c227f8\npshufhw  : a=dc515cff944a58ec456723c698694873 ib=78 r=944adc515cff58ec456723c698694873\npshufhw  : a=231be9e8cde7438d007c62c2085427f8 ib=78 r=cde7231be9e8438d007c62c2085427f8\npsrlw    : a=dc515cff944a58ec456723c698694873 ib=07 r=01b800b9012800b1008a004701300090\npsrlw    : a=456723c698694873 ib=07 r=008a004701300090\npsrlw    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=004601d3019b0087000000c50010004f\npsrlw    : a=007c62c2085427f8 ib=07 r=000000c50010004f\npsrlw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=01b800b9012800b1008a004701300090\npsrlw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=004601d3019b0087000000c50010004f\npsrlw    : a=456723c698694873 b=0000000000000007 r=008a004701300090\npsrlw    : a=007c62c2085427f8 b=0000000000000007 r=000000c50010004f\npsrlw    : a=dc515cff944a58ec456723c698694873 ib=10 r=00000000000000000000000000000000\npsrlw    : a=456723c698694873 ib=10 r=0000000000000000\npsrlw    : a=231be9e8cde7438d007c62c2085427f8 ib=10 r=00000000000000000000000000000000\npsrlw    : a=007c62c2085427f8 ib=10 r=0000000000000000\npsrlw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000010 r=00000000000000000000000000000000\npsrlw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000010 r=00000000000000000000000000000000\npsrlw    : a=456723c698694873 b=0000000000000010 r=0000000000000000\npsrlw    : a=007c62c2085427f8 b=0000000000000010 r=0000000000000000\npsraw    : a=dc515cff944a58ec456723c698694873 ib=07 r=ffb800b9ff2800b1008a0047ff300090\npsraw    : a=456723c698694873 ib=07 r=008a0047ff300090\npsraw    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=0046ffd3ff9b0087000000c50010004f\npsraw    : a=007c62c2085427f8 ib=07 r=000000c50010004f\npsraw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=ffb800b9ff2800b1008a0047ff300090\npsraw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=0046ffd3ff9b0087000000c50010004f\npsraw    : a=456723c698694873 b=0000000000000007 r=008a0047ff300090\npsraw    : a=007c62c2085427f8 b=0000000000000007 r=000000c50010004f\npsraw    : a=dc515cff944a58ec456723c698694873 ib=10 r=ffff0000ffff000000000000ffff0000\npsraw    : a=456723c698694873 ib=10 r=00000000ffff0000\npsraw    : a=231be9e8cde7438d007c62c2085427f8 ib=10 r=0000ffffffff00000000000000000000\npsraw    : a=007c62c2085427f8 ib=10 r=0000000000000000\npsraw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000010 r=ffff0000ffff000000000000ffff0000\npsraw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000010 r=0000ffffffff00000000000000000000\npsraw    : a=456723c698694873 b=0000000000000010 r=00000000ffff0000\npsraw    : a=007c62c2085427f8 b=0000000000000010 r=0000000000000000\npsllw    : a=dc515cff944a58ec456723c698694873 ib=07 r=28807f8025007600b380e30034803980\npsllw    : a=456723c698694873 ib=07 r=b380e30034803980\npsllw    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=8d80f400f380c6803e0061002a00fc00\npsllw    : a=007c62c2085427f8 ib=07 r=3e0061002a00fc00\npsllw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=28807f8025007600b380e30034803980\npsllw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=8d80f400f380c6803e0061002a00fc00\npsllw    : a=456723c698694873 b=0000000000000007 r=b380e30034803980\npsllw    : a=007c62c2085427f8 b=0000000000000007 r=3e0061002a00fc00\npsllw    : a=dc515cff944a58ec456723c698694873 ib=10 r=00000000000000000000000000000000\npsllw    : a=456723c698694873 ib=10 r=0000000000000000\npsllw    : a=231be9e8cde7438d007c62c2085427f8 ib=10 r=00000000000000000000000000000000\npsllw    : a=007c62c2085427f8 ib=10 r=0000000000000000\npsllw    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000010 r=00000000000000000000000000000000\npsllw    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000010 r=00000000000000000000000000000000\npsllw    : a=456723c698694873 b=0000000000000010 r=0000000000000000\npsllw    : a=007c62c2085427f8 b=0000000000000010 r=0000000000000000\npsrld    : a=dc515cff944a58ec456723c698694873 ib=07 r=01b8a2b9012894b1008ace470130d290\npsrld    : a=456723c698694873 ib=07 r=008ace470130d290\npsrld    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=004637d3019bce870000f8c50010a84f\npsrld    : a=007c62c2085427f8 ib=07 r=0000f8c50010a84f\npsrld    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=01b8a2b9012894b1008ace470130d290\npsrld    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=004637d3019bce870000f8c50010a84f\npsrld    : a=456723c698694873 b=0000000000000007 r=008ace470130d290\npsrld    : a=007c62c2085427f8 b=0000000000000007 r=0000f8c50010a84f\npsrld    : a=dc515cff944a58ec456723c698694873 ib=20 r=00000000000000000000000000000000\npsrld    : a=456723c698694873 ib=20 r=0000000000000000\npsrld    : a=231be9e8cde7438d007c62c2085427f8 ib=20 r=00000000000000000000000000000000\npsrld    : a=007c62c2085427f8 ib=20 r=0000000000000000\npsrld    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000020 r=00000000000000000000000000000000\npsrld    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000020 r=00000000000000000000000000000000\npsrld    : a=456723c698694873 b=0000000000000020 r=0000000000000000\npsrld    : a=007c62c2085427f8 b=0000000000000020 r=0000000000000000\npsrad    : a=dc515cff944a58ec456723c698694873 ib=07 r=ffb8a2b9ff2894b1008ace47ff30d290\npsrad    : a=456723c698694873 ib=07 r=008ace47ff30d290\npsrad    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=004637d3ff9bce870000f8c50010a84f\npsrad    : a=007c62c2085427f8 ib=07 r=0000f8c50010a84f\npsrad    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=ffb8a2b9ff2894b1008ace47ff30d290\npsrad    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=004637d3ff9bce870000f8c50010a84f\npsrad    : a=456723c698694873 b=0000000000000007 r=008ace47ff30d290\npsrad    : a=007c62c2085427f8 b=0000000000000007 r=0000f8c50010a84f\npsrad    : a=dc515cff944a58ec456723c698694873 ib=20 r=ffffffffffffffff00000000ffffffff\npsrad    : a=456723c698694873 ib=20 r=00000000ffffffff\npsrad    : a=231be9e8cde7438d007c62c2085427f8 ib=20 r=00000000ffffffff0000000000000000\npsrad    : a=007c62c2085427f8 ib=20 r=0000000000000000\npsrad    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000020 r=ffffffffffffffff00000000ffffffff\npsrad    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000020 r=00000000ffffffff0000000000000000\npsrad    : a=456723c698694873 b=0000000000000020 r=00000000ffffffff\npsrad    : a=007c62c2085427f8 b=0000000000000020 r=0000000000000000\npslld    : a=dc515cff944a58ec456723c698694873 ib=07 r=28ae7f80252c7600b391e30034a43980\npslld    : a=456723c698694873 ib=07 r=b391e30034a43980\npslld    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=8df4f400f3a1c6803e3161002a13fc00\npslld    : a=007c62c2085427f8 ib=07 r=3e3161002a13fc00\npslld    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=28ae7f80252c7600b391e30034a43980\npslld    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=8df4f400f3a1c6803e3161002a13fc00\npslld    : a=456723c698694873 b=0000000000000007 r=b391e30034a43980\npslld    : a=007c62c2085427f8 b=0000000000000007 r=3e3161002a13fc00\npslld    : a=dc515cff944a58ec456723c698694873 ib=20 r=00000000000000000000000000000000\npslld    : a=456723c698694873 ib=20 r=0000000000000000\npslld    : a=231be9e8cde7438d007c62c2085427f8 ib=20 r=00000000000000000000000000000000\npslld    : a=007c62c2085427f8 ib=20 r=0000000000000000\npslld    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000020 r=00000000000000000000000000000000\npslld    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000020 r=00000000000000000000000000000000\npslld    : a=456723c698694873 b=0000000000000020 r=0000000000000000\npslld    : a=007c62c2085427f8 b=0000000000000020 r=0000000000000000\npsrlq    : a=dc515cff944a58ec456723c698694873 ib=07 r=01b8a2b9ff2894b1008ace478d30d290\npsrlq    : a=456723c698694873 ib=07 r=008ace478d30d290\npsrlq    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=004637d3d19bce870000f8c58410a84f\npsrlq    : a=007c62c2085427f8 ib=07 r=0000f8c58410a84f\npsrlq    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=01b8a2b9ff2894b1008ace478d30d290\npsrlq    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=004637d3d19bce870000f8c58410a84f\npsrlq    : a=456723c698694873 b=0000000000000007 r=008ace478d30d290\npsrlq    : a=007c62c2085427f8 b=0000000000000007 r=0000f8c58410a84f\npsrlq    : a=dc515cff944a58ec456723c698694873 ib=20 r=00000000dc515cff00000000456723c6\npsrlq    : a=456723c698694873 ib=20 r=00000000456723c6\npsrlq    : a=231be9e8cde7438d007c62c2085427f8 ib=20 r=00000000231be9e800000000007c62c2\npsrlq    : a=007c62c2085427f8 ib=20 r=00000000007c62c2\npsrlq    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000020 r=00000000dc515cff00000000456723c6\npsrlq    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000020 r=00000000231be9e800000000007c62c2\npsrlq    : a=456723c698694873 b=0000000000000020 r=00000000456723c6\npsrlq    : a=007c62c2085427f8 b=0000000000000020 r=00000000007c62c2\npsllq    : a=dc515cff944a58ec456723c698694873 ib=07 r=28ae7fca252c7600b391e34c34a43980\npsllq    : a=456723c698694873 ib=07 r=b391e34c34a43980\npsllq    : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=8df4f466f3a1c6803e3161042a13fc00\npsllq    : a=007c62c2085427f8 ib=07 r=3e3161042a13fc00\npsllq    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000007 r=28ae7fca252c7600b391e34c34a43980\npsllq    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000007 r=8df4f466f3a1c6803e3161042a13fc00\npsllq    : a=456723c698694873 b=0000000000000007 r=b391e34c34a43980\npsllq    : a=007c62c2085427f8 b=0000000000000007 r=3e3161042a13fc00\npsllq    : a=dc515cff944a58ec456723c698694873 ib=20 r=944a58ec000000009869487300000000\npsllq    : a=456723c698694873 ib=20 r=9869487300000000\npsllq    : a=231be9e8cde7438d007c62c2085427f8 ib=20 r=cde7438d00000000085427f800000000\npsllq    : a=007c62c2085427f8 ib=20 r=085427f800000000\npsllq    : a=dc515cff944a58ec456723c698694873 b=00000000000000000000000000000020 r=944a58ec000000009869487300000000\npsllq    : a=231be9e8cde7438d007c62c2085427f8 b=00000000000000000000000000000020 r=cde7438d00000000085427f800000000\npsllq    : a=456723c698694873 b=0000000000000020 r=9869487300000000\npsllq    : a=007c62c2085427f8 b=0000000000000020 r=085427f800000000\npsrldq   : a=dc515cff944a58ec456723c698694873 ib=10 r=00000000000000000000000000000000\npsrldq   : a=231be9e8cde7438d007c62c2085427f8 ib=10 r=00000000000000000000000000000000\npsrldq   : a=dc515cff944a58ec456723c698694873 ib=07 r=00000000000000dc515cff944a58ec45\npsrldq   : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=00000000000000231be9e8cde7438d00\npslldq   : a=dc515cff944a58ec456723c698694873 ib=10 r=00000000000000000000000000000000\npslldq   : a=231be9e8cde7438d007c62c2085427f8 ib=10 r=00000000000000000000000000000000\npslldq   : a=dc515cff944a58ec456723c698694873 ib=07 r=ec456723c69869487300000000000000\npslldq   : a=231be9e8cde7438d007c62c2085427f8 ib=07 r=8d007c62c2085427f800000000000000\nmovmskpd : a=dc515cff944a58ec456723c698694873 r=00000002\nmovmskpd : a=231be9e8cde7438d007c62c2085427f8 r=00000000\nucomiss  : a=2.000000 b=-1.000000 cc=0000\ncomiss   : a=2.000000 b=-1.000000 cc=0000\nucomiss  : a=2.000000 b=2.000000 cc=0040\ncomiss   : a=2.000000 b=2.000000 cc=0040\nucomiss  : a=2.000000 b=3.000000 cc=0001\ncomiss   : a=2.000000 b=3.000000 cc=0001\nucomiss  : a=2.000000 b=-nan cc=0045\ncomiss   : a=2.000000 b=-nan cc=0045\nucomiss  : a=-nan b=-1.000000 cc=0045\ncomiss   : a=-nan b=-1.000000 cc=0045\naddps    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=424800004100000043b266664241999a\naddss    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a4241999a\nmulps    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c3b1585241800000449631ec42f6c7af\nmulss    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a42f6c7af\nsubps    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c27a666600000000c3af0000c22c0000\nsubss    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999ac22c0000\ndivss    : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a3d71fee1\ncmpeqss  : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpltss  : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpless  : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpunordss: a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpneqss : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpnltss : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpnless : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpordss : a=c0c9999a408000004059999a402ccccd b=426133334080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\naddpd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c04c6666666666664048333333333334\naddsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333334048333333333334\nmulpd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=4066b1eb851eb852405ed8f5c28f5c2a\nmulsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333405ed8f5c28f5c2a\nsubpd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=4049000000000000c045800000000000\nsubsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333c045800000000000\nminsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333400599999999999a\ndivsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333333fae3fdc26178701\nmaxsd    : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333334046d9999999999a\nsqrtsd   : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333401b0a6bf94cb839\ncmpeqpd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=00000000000000000000000000000000\ncmpeqsd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333330000000000000000\ncmpltpd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=0000000000000000ffffffffffffffff\ncmpltsd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333ffffffffffffffff\ncmplepd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=0000000000000000ffffffffffffffff\ncmplesd  : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpunordpd: a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=00000000000000000000000000000000\ncmpunordsd: a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333330000000000000000\ncmpneqpd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpneqsd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpnltpd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=ffffffffffffffff0000000000000000\ncmpnltsd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333330000000000000000\ncmpnlepd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=ffffffffffffffff0000000000000000\ncmpnlesd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b3333333333330000000000000000\ncmpordpd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpordsd : a=c00b333333333333400599999999999a b=c04ab333333333334046d9999999999a r=c00b333333333333ffffffffffffffff\naddps    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=ffc000004100000043b26666ffc00000\naddss    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affc00000\nmulps    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=ffc0000041800000449631ecffc00000\nmulss    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affc00000\nsubps    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=ffc0000000000000c3af0000ffc00000\nsubss    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affc00000\ndivss    : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affc00000\ncmpeqss  : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpltss  : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpless  : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\ncmpunordss: a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpneqss : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpnltss : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpnless : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999affffffff\ncmpordss : a=c0c9999a408000004059999affc00000 b=ffc000004080000043b0b3334236cccd r=c0c9999a408000004059999a00000000\naddpd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=fff8000000000000fff8000000000000\naddsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333fff8000000000000\nmulpd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=fff8000000000000fff8000000000000\nmulsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333fff8000000000000\nsubpd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=fff8000000000000fff8000000000000\nsubsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333fff8000000000000\nminsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333334046d9999999999a\ndivsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333fff8000000000000\nmaxsd    : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333334046d9999999999a\nsqrtsd   : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333401b0a6bf94cb839\ncmpeqpd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=00000000000000000000000000000000\ncmpeqsd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333330000000000000000\ncmpltpd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=00000000000000000000000000000000\ncmpltsd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333330000000000000000\ncmplepd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=00000000000000000000000000000000\ncmplesd  : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333330000000000000000\ncmpunordpd: a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpunordsd: a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpneqpd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpneqsd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpnltpd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpnltsd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpnlepd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=ffffffffffffffffffffffffffffffff\ncmpnlesd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b333333333333ffffffffffffffff\ncmpordpd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=00000000000000000000000000000000\ncmpordsd : a=c00b333333333333fff8000000000000 b=fff80000000000004046d9999999999a r=c00b3333333333330000000000000000\ncvtss2sd : a=c0c9999a408000004059999a402ccccd r=c0c9999a4080000040059999a0000000\ncvttss2si: a=c0c9999a408000004059999a402ccccd r=00000002\ncvttps2dq: a=c0c9999a408000004059999a402ccccd r=fffffffa000000040000000300000002\ncvtsd2ss : a=c00b3333333333334004cccccccccccd r=c00b3333333333334004cccc40266666\ncvttsd2si: a=c00b3333333333334004cccccccccccd r=00000002\ncvttpd2dq: a=c00b3333333333334004cccccccccccd r=0000000000000000fffffffd00000002\ncvtsi2ss : a=fffffffa r=0000000000000000fffffffdc0c00000\ncvtsi2sd : a=fffffffa r=0000000000000000c018000000000000\nmovlps   : a=456723c698694873 r=0000000000000000456723c698694873\nmovlps   : a=007c62c2085427f8 r=0000000000000000007c62c2085427f8\nmovlps   : a=dc515cff944a58ec456723c698694873 r=456723c698694873\nmovlps   : a=231be9e8cde7438d007c62c2085427f8 r=007c62c2085427f8\nmovhps   : a=dc515cff944a58ec r=dc515cff944a58ec007c62c2085427f8\nmovhps   : a=231be9e8cde7438d r=231be9e8cde7438d007c62c2085427f8\nmovhps   : a=dc515cff944a58ec456723c698694873 r=dc515cff944a58ec\nmovhps   : a=231be9e8cde7438d007c62c2085427f8 r=231be9e8cde7438d\nmovlpd   : a=456723c698694873 r=231be9e8cde7438d456723c698694873\nmovlpd   : a=007c62c2085427f8 r=231be9e8cde7438d007c62c2085427f8\nmovlpd   : a=dc515cff944a58ec456723c698694873 r=456723c698694873\nmovlpd   : a=231be9e8cde7438d007c62c2085427f8 r=007c62c2085427f8\nmovhpd   : a=dc515cff944a58ec r=dc515cff944a58ec007c62c2085427f8\nmovhpd   : a=231be9e8cde7438d r=231be9e8cde7438d007c62c2085427f8\nmovhpd   : a=dc515cff944a58ec456723c698694873 r=dc515cff944a58ec\nmovhpd   : a=231be9e8cde7438d007c62c2085427f8 r=231be9e8cde7438d\nmovntq   : a=456723c698694873 r=456723c698694873\nmovntq   : a=007c62c2085427f8 r=007c62c2085427f8\nmovntdq  : a=dc515cff944a58ec456723c698694873 r=dc515cff944a58ec456723c698694873\nmovntdq  : a=231be9e8cde7438d007c62c2085427f8 r=231be9e8cde7438d007c62c2085427f8\nmovups   : a=dc515cff944a58ec456723c698694873 r=dc515cff944a58ec456723c698694873\nmovups   : a=231be9e8cde7438d007c62c2085427f8 r=231be9e8cde7438d007c62c2085427f8\nmovupd   : a=dc515cff944a58ec456723c698694873 r=dc515cff944a58ec456723c698694873\nmovupd   : a=231be9e8cde7438d007c62c2085427f8 r=231be9e8cde7438d007c62c2085427f8\nminss    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=dc515cff944a58ec456723c698694873\nminss    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=231be9e8cde7438d007c62c2085427f8\nmaxss    : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=dc515cff944a58ec456723c658bad7ab\nmaxss    : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=231be9e8cde7438d007c62c2085427f8\nsqrtss   : a=dc515cff944a58ec456723c698694873 b=41f21efba9e3e1461f297ccd58bad7ab r=dc515cff944a58ec456723c64c1aa5bf\nsqrtss   : a=231be9e8cde7438d007c62c2085427f8 b=c233e9e8c4c9439a0f76255a085427f8 r=231be9e8cde7438d007c62c223e90c9e\n"
  },
  {
    "path": "tests/e2e/qemu/qemu-test-muldiv.h",
    "content": "\nvoid glue(glue(test_, OP), b)(long op0, long op1)\n{\n    long res, s1, s0, flags;\n    s0 = op0;\n    s1 = op1;\n    res = s0;\n    flags = 0;\n    asm (\"push %4\\n\\t\"\n         \"popf\\n\\t\"\n         stringify(OP)\"b %b2\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=a\" (res), \"=g\" (flags)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags));\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CC=%04lx\\n\",\n           stringify(OP) \"b\", s0, s1, res, flags & CC_MASK);\n}\n\nvoid glue(glue(test_, OP), w)(long op0h, long op0, long op1)\n{\n    long res, s1, flags, resh;\n    s1 = op1;\n    resh = op0h;\n    res = op0;\n    flags = 0;\n    asm (\"push %5\\n\\t\"\n         \"popf\\n\\t\"\n         stringify(OP) \"w %w3\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=a\" (res), \"=g\" (flags), \"=d\" (resh)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags), \"2\" (resh));\n    printf(\"%-10s AH=\" FMTLX \" AL=\" FMTLX \" B=\" FMTLX \" RH=\" FMTLX \" RL=\" FMTLX \" CC=%04lx\\n\",\n           stringify(OP) \"w\", op0h, op0, s1, resh, res, flags & CC_MASK);\n}\n\nvoid glue(glue(test_, OP), l)(long op0h, long op0, long op1)\n{\n    long res, s1, flags, resh;\n    s1 = op1;\n    resh = op0h;\n    res = op0;\n    flags = 0;\n    asm (\"push %5\\n\\t\"\n         \"popf\\n\\t\"\n         stringify(OP) \"l %k3\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=a\" (res), \"=g\" (flags), \"=d\" (resh)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags), \"2\" (resh));\n    printf(\"%-10s AH=\" FMTLX \" AL=\" FMTLX \" B=\" FMTLX \" RH=\" FMTLX \" RL=\" FMTLX \" CC=%04lx\\n\",\n           stringify(OP) \"l\", op0h, op0, s1, resh, res, flags & CC_MASK);\n}\n\n#if defined(__x86_64__)\nvoid glue(glue(test_, OP), q)(long op0h, long op0, long op1)\n{\n    long res, s1, flags, resh;\n    s1 = op1;\n    resh = op0h;\n    res = op0;\n    flags = 0;\n    asm (\"push %5\\n\\t\"\n         \"popf\\n\\t\"\n         stringify(OP) \"q %3\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=a\" (res), \"=g\" (flags), \"=d\" (resh)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags), \"2\" (resh));\n    printf(\"%-10s AH=\" FMTLX \" AL=\" FMTLX \" B=\" FMTLX \" RH=\" FMTLX \" RL=\" FMTLX \" CC=%04lx\\n\",\n           stringify(OP) \"q\", op0h, op0, s1, resh, res, flags & CC_MASK);\n}\n#endif\n\n#undef OP\n"
  },
  {
    "path": "tests/e2e/qemu/qemu-test-shift.h",
    "content": "\n#define exec_op glue(exec_, OP)\n#define exec_opq glue(glue(exec_, OP), q)\n#define exec_opl glue(glue(exec_, OP), l)\n#define exec_opw glue(glue(exec_, OP), w)\n#define exec_opb glue(glue(exec_, OP), b)\n\n#ifndef OP_SHIFTD\n\n#ifdef OP_NOBYTE\n#define EXECSHIFT(size, rsize, res, s1, s2, flags) \\\n    asm (\"push %4\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         stringify(OP) size \" %\" rsize \"2, %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=g\" (res), \"=g\" (flags)\\\n         : \"r\" (s1), \"0\" (res), \"1\" (flags));\n#else\n#define EXECSHIFT(size, rsize, res, s1, s2, flags) \\\n    asm (\"push %4\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         stringify(OP) size \" %%cl, %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=q\" (res), \"=g\" (flags)\\\n         : \"c\" (s1), \"0\" (res), \"1\" (flags));\n#endif\n\n#if defined(__x86_64__)\nvoid exec_opq(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"q\", \"\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"q\", s0, s1, res, iflags, flags & CC_MASK);\n}\n#endif\n\nvoid exec_opl(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"l\", \"k\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"l\", s0, s1, res, iflags, flags & CC_MASK);\n}\n\nvoid exec_opw(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"w\", \"w\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"w\", s0, s1, res, iflags, flags & CC_MASK);\n}\n\n#else\n#define EXECSHIFT(size, rsize, res, s1, s2, flags) \\\n    asm (\"push %4\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         stringify(OP) size \" %%cl, %\" rsize \"5, %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=g\" (res), \"=g\" (flags)\\\n         : \"c\" (s1), \"0\" (res), \"1\" (flags), \"r\" (s2));\n\n#if defined(__x86_64__)\nvoid exec_opq(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"q\", \"\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" C=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"q\", s0, s2, s1, res, iflags, flags & CC_MASK);\n}\n#endif\n\nvoid exec_opl(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"l\", \"k\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" C=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"l\", s0, s2, s1, res, iflags, flags & CC_MASK);\n}\n\n#if 0\nvoid exec_opw(long s2, long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"w\", \"w\", res, s1, s2, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" C=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"w\", s0, s2, s1, res, iflags, flags & CC_MASK);\n}\n#endif\n\n#endif\n\n#ifndef OP_NOBYTE\nvoid exec_opb(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECSHIFT(\"b\", \"b\", res, s1, 0, flags);\n    /* overflow is undefined if count != 1 */\n    if (s1 != 1)\n      flags &= ~CC_O;\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\",\n           stringify(OP) \"b\", s0, s1, res, iflags, flags & CC_MASK);\n}\n#endif\n\nvoid exec_op(long s2, long s0, long s1)\n{\n    s2 = i2l(s2);\n    s0 = i2l(s0);\n#if defined(__x86_64__)\n    exec_opq(s2, s0, s1, 0);\n#endif\n    exec_opl(s2, s0, s1, 0);\n#ifdef OP_SHIFTD\n    //exec_opw(s2, s0, s1, 0);\n#else\n    exec_opw(s2, s0, s1, 0);\n#endif\n#ifndef OP_NOBYTE\n    exec_opb(s0, s1, 0);\n#endif\n#ifdef OP_CC\n#if defined(__x86_64__)\n    exec_opq(s2, s0, s1, CC_C);\n#endif\n    exec_opl(s2, s0, s1, CC_C);\n    exec_opw(s2, s0, s1, CC_C);\n    exec_opb(s0, s1, CC_C);\n#endif\n}\n\nvoid glue(test_, OP)(void)\n{\n    int i, n;\n#if defined(__x86_64__)\n    n = 64;\n#else\n    n = 32;\n#endif\n    for(i = 0; i < n; i++)\n        exec_op(0x21ad3d34, 0x12345678, i);\n    for(i = 0; i < n; i++)\n        exec_op(0x813f3421, 0x82345679, i);\n}\n\nvoid *glue(_test_, OP) __init_call = glue(test_, OP);\n\n#undef OP\n#undef OP_CC\n#undef OP_SHIFTD\n#undef OP_NOBYTE\n#undef EXECSHIFT\n"
  },
  {
    "path": "tests/e2e/qemu/qemu-test.c",
    "content": "// Originally copied from the qemu repository\n/*\n *  x86 CPU test\n *\n *  Copyright (c) 2003 Fabrice Bellard\n *\n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program; if not, see <http://www.gnu.org/licenses/>.\n */\n#pragma GCC diagnostic ignored \"-Wsign-compare\"\n#define _GNU_SOURCE\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <inttypes.h>\n#include <math.h>\n#include <signal.h>\n#include <setjmp.h>\n#include <errno.h>\n#include <sys/ucontext.h>\n#include <sys/mman.h>\n\n#if !defined(__x86_64__)\n//#define TEST_VM86\n//#define TEST_SEGS\n#endif\n//#define LINUX_VM86_IOPL_FIX\n//#define TEST_P4_FLAGS\n#ifdef __SSE__\n#define TEST_SSE\n#define TEST_CMOV  1\n#define TEST_FCOMI 1\n#else\n#undef TEST_SSE\n#define TEST_CMOV  1\n#define TEST_FCOMI 1\n#endif\n\n#if defined(__x86_64__)\n#define FMT64X \"%016lx\"\n#define FMTLX \"%016lx\"\n#define X86_64_ONLY(x) x\n#else\n#define FMT64X \"%016\" PRIx64\n#define FMTLX \"%08lx\"\n#define X86_64_ONLY(x)\n#endif\n\n#ifdef TEST_VM86\n#include <asm/vm86.h>\n#endif\n\n#define xglue(x, y) x ## y\n#define glue(x, y) xglue(x, y)\n#define stringify(s)\ttostring(s)\n#define tostring(s)\t#s\n\n#define CC_C   \t0x0001\n#define CC_P \t0x0004\n#define CC_A\t0x0010\n#define CC_Z\t0x0040\n#define CC_S    0x0080\n#define CC_O    0x0800\n\n#ifdef __APPLE__\n    extern void *__start_initcall asm(\"section$start$__DATA$initcall\");\n    extern void *__stop_initcall asm(\"section$end$__DATA$initcall\");\n    #define __init_call\t__attribute__ ((unused,__section__(\"__DATA,initcall\")))\n#else\n    extern void *__start_initcall;\n    extern void *__stop_initcall;\n    #define __init_call\t__attribute__ ((unused,__section__ (\"initcall\")))\n#endif\n\n#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)\n\n#if defined(__x86_64__)\nstatic inline long i2l(long v)\n{\n    return v | ((v ^ 0xabcd) << 32);\n}\n#else\nstatic inline long i2l(long v)\n{\n    return v;\n}\n#endif\n\n#define OP add\n#include \"qemu-test.h\"\n\n#define OP sub\n#include \"qemu-test.h\"\n\n#define OP xor\n#include \"qemu-test.h\"\n\n#define OP and\n#include \"qemu-test.h\"\n\n#define OP or\n#include \"qemu-test.h\"\n\n#define OP cmp\n#include \"qemu-test.h\"\n\n#define OP adc\n#define OP_CC\n#include \"qemu-test.h\"\n\n#define OP sbb\n#define OP_CC\n#include \"qemu-test.h\"\n\n#define OP inc\n#define OP_CC\n#define OP1\n#include \"qemu-test.h\"\n\n#define OP dec\n#define OP_CC\n#define OP1\n#include \"qemu-test.h\"\n\n#define OP neg\n#define OP_CC\n#define OP1\n#include \"qemu-test.h\"\n\n#define OP not\n#define OP_CC\n#define OP1\n#include \"qemu-test.h\"\n\n#undef CC_MASK\n#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O)\n\n#define OP shl\n#include \"qemu-test-shift.h\"\n\n#define OP shr\n#include \"qemu-test-shift.h\"\n\n#define OP sar\n#include \"qemu-test-shift.h\"\n\n#define OP rol\n#include \"qemu-test-shift.h\"\n\n#define OP ror\n#include \"qemu-test-shift.h\"\n\n#define OP rcr\n#define OP_CC\n#include \"qemu-test-shift.h\"\n\n#define OP rcl\n#define OP_CC\n#include \"qemu-test-shift.h\"\n\n#define OP shld\n#define OP_SHIFTD\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n#define OP shrd\n#define OP_SHIFTD\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n/* XXX: should be more precise ? */\n#undef CC_MASK\n#define CC_MASK (CC_C)\n\n#define OP bt\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n#define OP bts\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n#define OP btr\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n#define OP btc\n#define OP_NOBYTE\n#include \"qemu-test-shift.h\"\n\n/* lea test (modrm support) */\n#define TEST_LEAQ(STR)\\\n{\\\n    asm(\"lea \" STR \", %0\"\\\n        : \"=r\" (res)\\\n        : \"a\" (eax), \"b\" (ebx), \"c\" (ecx), \"d\" (edx), \"S\" (esi), \"D\" (edi));\\\n    printf(\"lea %s = \" FMTLX \"\\n\", STR, res);\\\n}\n\n#define TEST_LEA(STR)\\\n{\\\n    asm(\"lea \" STR \", %0\"\\\n        : \"=r\" (res)\\\n        : \"a\" (eax), \"b\" (ebx), \"c\" (ecx), \"d\" (edx), \"S\" (esi), \"D\" (edi));\\\n    printf(\"lea %s = \" FMTLX \"\\n\", STR, res);\\\n}\n\n#define TEST_LEA16(STR)\\\n{\\\n    asm(\".code16 ; .byte 0x67 ; leal \" STR \", %0 ; .code32\"\\\n        : \"=r\" (res)\\\n        : \"a\" (eax), \"b\" (ebx), \"c\" (ecx), \"d\" (edx), \"S\" (esi), \"D\" (edi));\\\n    printf(\"lea %s = %08lx\\n\", STR, res);\\\n}\n\n\nvoid test_lea(void)\n{\n    long eax, ebx, ecx, edx, esi, edi, res;\n    eax = i2l(0x0001);\n    ebx = i2l(0x0002);\n    ecx = i2l(0x0004);\n    edx = i2l(0x0008);\n    esi = i2l(0x0010);\n    edi = i2l(0x0020);\n\n    TEST_LEA(\"0x4000\");\n\n    TEST_LEA(\"(%%eax)\");\n    TEST_LEA(\"(%%ebx)\");\n    TEST_LEA(\"(%%ecx)\");\n    TEST_LEA(\"(%%edx)\");\n    TEST_LEA(\"(%%esi)\");\n    TEST_LEA(\"(%%edi)\");\n\n    TEST_LEA(\"0x40(%%eax)\");\n    TEST_LEA(\"0x40(%%ebx)\");\n    TEST_LEA(\"0x40(%%ecx)\");\n    TEST_LEA(\"0x40(%%edx)\");\n    TEST_LEA(\"0x40(%%esi)\");\n    TEST_LEA(\"0x40(%%edi)\");\n\n    TEST_LEA(\"0x4000(%%eax)\");\n    TEST_LEA(\"0x4000(%%ebx)\");\n    TEST_LEA(\"0x4000(%%ecx)\");\n    TEST_LEA(\"0x4000(%%edx)\");\n    TEST_LEA(\"0x4000(%%esi)\");\n    TEST_LEA(\"0x4000(%%edi)\");\n\n    TEST_LEA(\"(%%eax, %%ecx)\");\n    TEST_LEA(\"(%%ebx, %%edx)\");\n    TEST_LEA(\"(%%ecx, %%ecx)\");\n    TEST_LEA(\"(%%edx, %%ecx)\");\n    TEST_LEA(\"(%%esi, %%ecx)\");\n    TEST_LEA(\"(%%edi, %%ecx)\");\n\n    TEST_LEA(\"0x40(%%eax, %%ecx)\");\n    TEST_LEA(\"0x4000(%%ebx, %%edx)\");\n\n    TEST_LEA(\"(%%ecx, %%ecx, 2)\");\n    TEST_LEA(\"(%%edx, %%ecx, 4)\");\n    TEST_LEA(\"(%%esi, %%ecx, 8)\");\n\n    TEST_LEA(\"(,%%eax, 2)\");\n    TEST_LEA(\"(,%%ebx, 4)\");\n    TEST_LEA(\"(,%%ecx, 8)\");\n\n    TEST_LEA(\"0x40(,%%eax, 2)\");\n    TEST_LEA(\"0x40(,%%ebx, 4)\");\n    TEST_LEA(\"0x40(,%%ecx, 8)\");\n\n\n    TEST_LEA(\"-10(%%ecx, %%ecx, 2)\");\n    TEST_LEA(\"-10(%%edx, %%ecx, 4)\");\n    TEST_LEA(\"-10(%%esi, %%ecx, 8)\");\n\n    TEST_LEA(\"0x4000(%%ecx, %%ecx, 2)\");\n    TEST_LEA(\"0x4000(%%edx, %%ecx, 4)\");\n    TEST_LEA(\"0x4000(%%esi, %%ecx, 8)\");\n\n#if defined(__x86_64__)\n    TEST_LEAQ(\"0x4000\");\n    TEST_LEAQ(\"0x4000(%%rip)\");\n\n    TEST_LEAQ(\"(%%rax)\");\n    TEST_LEAQ(\"(%%rbx)\");\n    TEST_LEAQ(\"(%%rcx)\");\n    TEST_LEAQ(\"(%%rdx)\");\n    TEST_LEAQ(\"(%%rsi)\");\n    TEST_LEAQ(\"(%%rdi)\");\n\n    TEST_LEAQ(\"0x40(%%rax)\");\n    TEST_LEAQ(\"0x40(%%rbx)\");\n    TEST_LEAQ(\"0x40(%%rcx)\");\n    TEST_LEAQ(\"0x40(%%rdx)\");\n    TEST_LEAQ(\"0x40(%%rsi)\");\n    TEST_LEAQ(\"0x40(%%rdi)\");\n\n    TEST_LEAQ(\"0x4000(%%rax)\");\n    TEST_LEAQ(\"0x4000(%%rbx)\");\n    TEST_LEAQ(\"0x4000(%%rcx)\");\n    TEST_LEAQ(\"0x4000(%%rdx)\");\n    TEST_LEAQ(\"0x4000(%%rsi)\");\n    TEST_LEAQ(\"0x4000(%%rdi)\");\n\n    TEST_LEAQ(\"(%%rax, %%rcx)\");\n    TEST_LEAQ(\"(%%rbx, %%rdx)\");\n    TEST_LEAQ(\"(%%rcx, %%rcx)\");\n    TEST_LEAQ(\"(%%rdx, %%rcx)\");\n    TEST_LEAQ(\"(%%rsi, %%rcx)\");\n    TEST_LEAQ(\"(%%rdi, %%rcx)\");\n\n    TEST_LEAQ(\"0x40(%%rax, %%rcx)\");\n    TEST_LEAQ(\"0x4000(%%rbx, %%rdx)\");\n\n    TEST_LEAQ(\"(%%rcx, %%rcx, 2)\");\n    TEST_LEAQ(\"(%%rdx, %%rcx, 4)\");\n    TEST_LEAQ(\"(%%rsi, %%rcx, 8)\");\n\n    TEST_LEAQ(\"(,%%rax, 2)\");\n    TEST_LEAQ(\"(,%%rbx, 4)\");\n    TEST_LEAQ(\"(,%%rcx, 8)\");\n\n    TEST_LEAQ(\"0x40(,%%rax, 2)\");\n    TEST_LEAQ(\"0x40(,%%rbx, 4)\");\n    TEST_LEAQ(\"0x40(,%%rcx, 8)\");\n\n\n    TEST_LEAQ(\"-10(%%rcx, %%rcx, 2)\");\n    TEST_LEAQ(\"-10(%%rdx, %%rcx, 4)\");\n    TEST_LEAQ(\"-10(%%rsi, %%rcx, 8)\");\n\n    TEST_LEAQ(\"0x4000(%%rcx, %%rcx, 2)\");\n    TEST_LEAQ(\"0x4000(%%rdx, %%rcx, 4)\");\n    TEST_LEAQ(\"0x4000(%%rsi, %%rcx, 8)\");\n#elif 0\n    /* limited 16 bit addressing test */\n    TEST_LEA16(\"0x4000\");\n    TEST_LEA16(\"(%%bx)\");\n    TEST_LEA16(\"(%%si)\");\n    TEST_LEA16(\"(%%di)\");\n    TEST_LEA16(\"0x40(%%bx)\");\n    TEST_LEA16(\"0x40(%%si)\");\n    TEST_LEA16(\"0x40(%%di)\");\n    TEST_LEA16(\"0x4000(%%bx)\");\n    TEST_LEA16(\"0x4000(%%si)\");\n    TEST_LEA16(\"(%%bx,%%si)\");\n    TEST_LEA16(\"(%%bx,%%di)\");\n    TEST_LEA16(\"0x40(%%bx,%%si)\");\n    TEST_LEA16(\"0x40(%%bx,%%di)\");\n    TEST_LEA16(\"0x4000(%%bx,%%si)\");\n    TEST_LEA16(\"0x4000(%%bx,%%di)\");\n#endif\n}\n\n#define TEST_JCC(JCC, v1, v2)\\\n{\\\n    int res;\\\n    asm(\"movl $1, %0\\n\\t\"\\\n        \"cmpl %2, %1\\n\\t\"\\\n        \"j\" JCC \" 1f\\n\\t\"\\\n        \"movl $0, %0\\n\\t\"\\\n        \"1:\\n\\t\"\\\n        : \"=r\" (res)\\\n        : \"r\" (v1), \"r\" (v2));\\\n    printf(\"%-10s %d\\n\", \"j\" JCC, res);\\\n\\\n    asm(\"movl $0, %0\\n\\t\"\\\n        \"cmpl %2, %1\\n\\t\"\\\n        \"set\" JCC \" %b0\\n\\t\"\\\n        : \"=q\" (res)\\\n        : \"r\" (v1), \"r\" (v2));\\\n    printf(\"%-10s %d\\n\", \"set\" JCC, res);\\\n if (TEST_CMOV) {\\\n    long val = i2l(1);\\\n    long res = i2l(0x12345678);\\\nX86_64_ONLY(\\\n    asm(\"cmpl %2, %1\\n\\t\"\\\n        \"cmov\" JCC \"q %3, %0\\n\\t\"\\\n        : \"=r\" (res)\\\n        : \"r\" (v1), \"r\" (v2), \"m\" (val), \"0\" (res));\\\n        printf(\"%-10s R=\" FMTLX \"\\n\", \"cmov\" JCC \"q\", res);)\\\n    asm(\"cmpl %2, %1\\n\\t\"\\\n        \"cmov\" JCC \"l %k3, %k0\\n\\t\"\\\n        : \"=r\" (res)\\\n        : \"r\" (v1), \"r\" (v2), \"m\" (val), \"0\" (res));\\\n        printf(\"%-10s R=\" FMTLX \"\\n\", \"cmov\" JCC \"l\", res);\\\n    asm(\"cmpl %2, %1\\n\\t\"\\\n        \"cmov\" JCC \"w %w3, %w0\\n\\t\"\\\n        : \"=r\" (res)\\\n        : \"r\" (v1), \"r\" (v2), \"r\" (1), \"0\" (res));\\\n        printf(\"%-10s R=\" FMTLX \"\\n\", \"cmov\" JCC \"w\", res);\\\n } \\\n}\n\n/* various jump tests */\nvoid test_jcc(void)\n{\n    TEST_JCC(\"ne\", 1, 1);\n    TEST_JCC(\"ne\", 1, 0);\n\n    TEST_JCC(\"e\", 1, 1);\n    TEST_JCC(\"e\", 1, 0);\n\n    TEST_JCC(\"l\", 1, 1);\n    TEST_JCC(\"l\", 1, 0);\n    TEST_JCC(\"l\", 1, -1);\n\n    TEST_JCC(\"le\", 1, 1);\n    TEST_JCC(\"le\", 1, 0);\n    TEST_JCC(\"le\", 1, -1);\n\n    TEST_JCC(\"ge\", 1, 1);\n    TEST_JCC(\"ge\", 1, 0);\n    TEST_JCC(\"ge\", -1, 1);\n\n    TEST_JCC(\"g\", 1, 1);\n    TEST_JCC(\"g\", 1, 0);\n    TEST_JCC(\"g\", 1, -1);\n\n    TEST_JCC(\"b\", 1, 1);\n    TEST_JCC(\"b\", 1, 0);\n    TEST_JCC(\"b\", 1, -1);\n\n    TEST_JCC(\"be\", 1, 1);\n    TEST_JCC(\"be\", 1, 0);\n    TEST_JCC(\"be\", 1, -1);\n\n    TEST_JCC(\"ae\", 1, 1);\n    TEST_JCC(\"ae\", 1, 0);\n    TEST_JCC(\"ae\", 1, -1);\n\n    TEST_JCC(\"a\", 1, 1);\n    TEST_JCC(\"a\", 1, 0);\n    TEST_JCC(\"a\", 1, -1);\n\n\n    TEST_JCC(\"p\", 1, 1);\n    TEST_JCC(\"p\", 1, 0);\n\n    TEST_JCC(\"np\", 1, 1);\n    TEST_JCC(\"np\", 1, 0);\n\n    TEST_JCC(\"o\", 0x7fffffff, 0);\n    TEST_JCC(\"o\", 0x7fffffff, -1);\n\n    TEST_JCC(\"no\", 0x7fffffff, 0);\n    TEST_JCC(\"no\", 0x7fffffff, -1);\n\n    TEST_JCC(\"s\", 0, 1);\n    TEST_JCC(\"s\", 0, -1);\n    TEST_JCC(\"s\", 0, 0);\n\n    TEST_JCC(\"ns\", 0, 1);\n    TEST_JCC(\"ns\", 0, -1);\n    TEST_JCC(\"ns\", 0, 0);\n}\n\n#define TEST_LOOP(insn) \\\n{\\\n    for(i = 0; i < sizeof(ecx_vals) / sizeof(long); i++) {\\\n        ecx = ecx_vals[i];\\\n        for(zf = 0; zf < 2; zf++) {\\\n    asm(\"test %2, %2\\n\\t\"\\\n        \"movl $1, %0\\n\\t\"\\\n          insn \" 1f\\n\\t\" \\\n        \"movl $0, %0\\n\\t\"\\\n        \"1:\\n\\t\"\\\n        : \"=a\" (res)\\\n        : \"c\" (ecx), \"b\" (!zf)); \\\n    printf(\"%-10s ECX=\" FMTLX \" ZF=%ld r=%d\\n\", insn, ecx, zf, res);      \\\n        }\\\n   }\\\n}\n\nvoid test_loop(void)\n{\n    long ecx, zf;\n    const long ecx_vals[] = {\n        0,\n        1,\n        0x10000,\n        0x10001,\n#if defined(__x86_64__)\n        0x100000000L,\n        0x100000001L,\n#endif\n    };\n    int i, res;\n\n#if !defined(__x86_64__)\n    TEST_LOOP(\"jcxz\");\n#endif\n\n    TEST_LOOP(\"jecxz\");\n    TEST_LOOP(\"loop\");\n    TEST_LOOP(\"loopz\");\n    TEST_LOOP(\"loopnz\");\n}\n\n#undef CC_MASK\n#ifdef TEST_P4_FLAGS\n#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)\n#else\n#define CC_MASK (CC_O | CC_C)\n#endif\n\n#define OP mul\n#include \"qemu-test-muldiv.h\"\n\n#define OP imul\n#include \"qemu-test-muldiv.h\"\n\nvoid test_imulw2(long op0, long op1)\n{\n    long res, s1, s0, flags;\n    s0 = op0;\n    s1 = op1;\n    res = s0;\n    flags = 0;\n    asm volatile (\"push %4\\n\\t\"\n         \"popf\\n\\t\"\n         \"imulw %w2, %w0\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=q\" (res), \"=g\" (flags)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags));\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CC=%04lx\\n\",\n           \"imulw\", s0, s1, res, flags & CC_MASK);\n}\n\nvoid test_imull2(long op0, long op1)\n{\n    long res, s1, s0, flags;\n    s0 = op0;\n    s1 = op1;\n    res = s0;\n    flags = 0;\n    asm volatile (\"push %4\\n\\t\"\n         \"popf\\n\\t\"\n         \"imull %k2, %k0\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=q\" (res), \"=g\" (flags)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags));\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CC=%04lx\\n\",\n           \"imull\", s0, s1, res, flags & CC_MASK);\n}\n\n#if defined(__x86_64__)\nvoid test_imulq2(long op0, long op1)\n{\n    long res, s1, s0, flags;\n    s0 = op0;\n    s1 = op1;\n    res = s0;\n    flags = 0;\n    asm volatile (\"push %4\\n\\t\"\n         \"popf\\n\\t\"\n         \"imulq %2, %0\\n\\t\"\n         \"pushf\\n\\t\"\n         \"pop %1\\n\\t\"\n         : \"=q\" (res), \"=g\" (flags)\n         : \"q\" (s1), \"0\" (res), \"1\" (flags));\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CC=%04lx\\n\",\n           \"imulq\", s0, s1, res, flags & CC_MASK);\n}\n#endif\n\n#define TEST_IMUL_IM(size, rsize, op0, op1)\\\n{\\\n    long res, flags, s1;\\\n    flags = 0;\\\n    res = 0;\\\n    s1 = op1;\\\n    asm volatile (\"push %3\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         \"imul\" size \" $\" #op0 \", %\" rsize \"2, %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=r\" (res), \"=g\" (flags)\\\n         : \"r\" (s1), \"1\" (flags), \"0\" (res));\\\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CC=%04lx\\n\",\\\n           \"imul\" size \" im\", (long)op0, (long)op1, res, flags & CC_MASK);\\\n}\n\n\n#undef CC_MASK\n#define CC_MASK (0)\n\n#define OP div\n#include \"qemu-test-muldiv.h\"\n\n#define OP idiv\n#include \"qemu-test-muldiv.h\"\n\nvoid test_mul(void)\n{\n    test_imulb(0x1234561d, 4);\n    test_imulb(3, -4);\n    test_imulb(0x80, 0x80);\n    test_imulb(0x10, 0x10);\n\n    test_imulw(0, 0x1234001d, 45);\n    test_imulw(0, 23, -45);\n    test_imulw(0, 0x8000, 0x8000);\n    test_imulw(0, 0x100, 0x100);\n\n    test_imull(0, 0x1234001d, 45);\n    test_imull(0, 23, -45);\n    test_imull(0, 0x80000000, 0x80000000);\n    test_imull(0, 0x10000, 0x10000);\n\n    test_mulb(0x1234561d, 4);\n    test_mulb(3, -4);\n    test_mulb(0x80, 0x80);\n    test_mulb(0x10, 0x10);\n\n    test_mulw(0, 0x1234001d, 45);\n    test_mulw(0, 23, -45);\n    test_mulw(0, 0x8000, 0x8000);\n    test_mulw(0, 0x100, 0x100);\n\n    test_mull(0, 0x1234001d, 45);\n    test_mull(0, 23, -45);\n    test_mull(0, 0x80000000, 0x80000000);\n    test_mull(0, 0x10000, 0x10000);\n\n    test_imulw2(0x1234001d, 45);\n    test_imulw2(23, -45);\n    test_imulw2(0x8000, 0x8000);\n    test_imulw2(0x100, 0x100);\n\n    test_imull2(0x1234001d, 45);\n    test_imull2(23, -45);\n    test_imull2(0x80000000, 0x80000000);\n    test_imull2(0x10000, 0x10000);\n\n    TEST_IMUL_IM(\"w\", \"w\", 45, 0x1234);\n    TEST_IMUL_IM(\"w\", \"w\", -45, 23);\n    TEST_IMUL_IM(\"w\", \"w\", 0x8000, 0x80000000);\n    TEST_IMUL_IM(\"w\", \"w\", 0x7fff, 0x1000);\n\n    TEST_IMUL_IM(\"l\", \"k\", 45, 0x1234);\n    TEST_IMUL_IM(\"l\", \"k\", -45, 23);\n    TEST_IMUL_IM(\"l\", \"k\", 0x8000, 0x80000000);\n    TEST_IMUL_IM(\"l\", \"k\", 0x7fff, 0x1000);\n\n    test_idivb(0x12341678, 0x127e);\n    test_idivb(0x43210123, -5);\n    test_idivb(0x12340004, -1);\n\n    test_idivw(0, 0x12345678, 12347);\n    test_idivw(0, -23223, -45);\n    test_idivw(0, 0x12348000, -1);\n    test_idivw(0x12343, 0x12345678, 0x81238567);\n\n    test_idivl(0, 0x12345678, 12347);\n    test_idivl(0, -233223, -45);\n    test_idivl(0, 0x80000000, -1);\n    test_idivl(0x12343, 0x12345678, 0x81234567);\n\n    test_divb(0x12341678, 0x127e);\n    test_divb(0x43210123, -5);\n    test_divb(0x12340004, -1);\n\n    test_divw(0, 0x12345678, 12347);\n    test_divw(0, -23223, -45);\n    test_divw(0, 0x12348000, -1);\n    test_divw(0x12343, 0x12345678, 0x81238567);\n\n    test_divl(0, 0x12345678, 12347);\n    test_divl(0, -233223, -45);\n    test_divl(0, 0x80000000, -1);\n    test_divl(0x12343, 0x12345678, 0x81234567);\n\n#if defined(__x86_64__)\n    test_imulq(0, 0x1234001d1234001d, 45);\n    test_imulq(0, 23, -45);\n    test_imulq(0, 0x8000000000000000, 0x8000000000000000);\n    test_imulq(0, 0x100000000, 0x100000000);\n\n    test_mulq(0, 0x1234001d1234001d, 45);\n    test_mulq(0, 23, -45);\n    test_mulq(0, 0x8000000000000000, 0x8000000000000000);\n    test_mulq(0, 0x100000000, 0x100000000);\n\n    test_imulq2(0x1234001d1234001d, 45);\n    test_imulq2(23, -45);\n    test_imulq2(0x8000000000000000, 0x8000000000000000);\n    test_imulq2(0x100000000, 0x100000000);\n\n    TEST_IMUL_IM(\"q\", \"\", 45, 0x12341234);\n    TEST_IMUL_IM(\"q\", \"\", -45, 23);\n    TEST_IMUL_IM(\"q\", \"\", 0x8000, 0x8000000000000000);\n    TEST_IMUL_IM(\"q\", \"\", 0x7fff, 0x10000000);\n\n    test_idivq(0, 0x12345678abcdef, 12347);\n    test_idivq(0, -233223, -45);\n    test_idivq(0, 0x8000000000000000, -1);\n    test_idivq(0x12343, 0x12345678, 0x81234567);\n\n    test_divq(0, 0x12345678abcdef, 12347);\n    test_divq(0, -233223, -45);\n    test_divq(0, 0x8000000000000000, -1);\n    test_divq(0x12343, 0x12345678, 0x81234567);\n#endif\n}\n\n#define TEST_BSX(op, size, op0)\\\n{\\\n    long res, val, resz;\\\n    val = op0;\\\n    asm(\"xor %1, %1\\n\"\\\n        \"mov $0x12345678, %0\\n\"\\\n        #op \" %\" size \"2, %\" size \"0 ; setz %b1\" \\\n        : \"=&r\" (res), \"=&q\" (resz)\\\n        : \"r\" (val));\\\n    printf(\"%-10s A=\" FMTLX \" R=\" FMTLX \" %ld\\n\", #op, val, res, resz);\\\n}\n\nvoid test_bsx(void)\n{\n    TEST_BSX(bsrw, \"w\", 0);\n    TEST_BSX(bsrw, \"w\", 0x12340128);\n    TEST_BSX(bsfw, \"w\", 0);\n    TEST_BSX(bsfw, \"w\", 0x12340128);\n    TEST_BSX(bsrl, \"k\", 0);\n    TEST_BSX(bsrl, \"k\", 0x00340128);\n    TEST_BSX(bsfl, \"k\", 0);\n    TEST_BSX(bsfl, \"k\", 0x00340128);\n#if defined(__x86_64__)\n    TEST_BSX(bsrq, \"\", 0);\n    TEST_BSX(bsrq, \"\", 0x003401281234);\n    TEST_BSX(bsfq, \"\", 0);\n    TEST_BSX(bsfq, \"\", 0x003401281234);\n#endif\n}\n\n/**********************************************/\n\nunion float64u {\n    double d;\n    uint64_t l;\n};\n\nunion float64u q_nan = { .l = 0xFFF8000000000000LL };\nunion float64u s_nan = { .l = 0xFFF0000000000000LL };\n\nvoid test_fops(double a, double b)\n{\n    printf(\"a=%f b=%f a+b=%f\\n\", a, b, a + b);\n    printf(\"a=%f b=%f a-b=%f\\n\", a, b, a - b);\n    printf(\"a=%f b=%f a*b=%f\\n\", a, b, a * b);\n    printf(\"a=%f b=%f a/b=%f\\n\", a, b, a / b);\n    printf(\"a=%f b=%f fmod(a, b)=%f\\n\", a, b, fmod(a, b));\n    printf(\"a=%f sqrt(a)=%f\\n\", a, sqrt(a));\n    printf(\"a=%f sin(a)=%f\\n\", a, sin(a));\n    printf(\"a=%f cos(a)=%f\\n\", a, cos(a));\n    printf(\"a=%f tan(a)=%f\\n\", a, tan(a));\n    printf(\"a=%f log(a)=%f\\n\", a, log(a));\n    printf(\"a=%f exp(a)=%f\\n\", a, exp(a));\n    printf(\"a=%f b=%f atan2(a, b)=%f\\n\", a, b, atan2(a, b));\n    /* just to test some op combining */\n    printf(\"a=%f asin(sin(a))=%f\\n\", a, asin(sin(a)));\n    printf(\"a=%f acos(cos(a))=%f\\n\", a, acos(cos(a)));\n    printf(\"a=%f atan(tan(a))=%f\\n\", a, atan(tan(a)));\n\n}\n\nvoid fpu_clear_exceptions(void)\n{\n#if 0\n    struct QEMU_PACKED {\n        uint16_t fpuc;\n        uint16_t dummy1;\n        uint16_t fpus;\n        uint16_t dummy2;\n        uint16_t fptag;\n        uint16_t dummy3;\n        uint32_t ignored[4];\n        long double fpregs[8];\n    } float_env32;\n\n    asm volatile (\"fnstenv %0\\n\" : \"=m\" (float_env32));\n    float_env32.fpus &= ~0x7f;\n    asm volatile (\"fldenv %0\\n\" : : \"m\" (float_env32));\n#endif\n}\n\n/* XXX: display exception bits when supported */\n#define FPUS_EMASK 0x0000\n//#define FPUS_EMASK 0x007f\n\nvoid test_fcmp(double a, double b)\n{\n    long eflags, fpus;\n\n    fpu_clear_exceptions();\n    asm(\"fcom %2\\n\"\n        \"fstsw %%ax\\n\"\n        : \"=a\" (fpus)\n        : \"t\" (a), \"u\" (b));\n    printf(\"fcom(%f %f)=%04lx\\n\",\n           a, b, fpus & (0x4500 | FPUS_EMASK));\n    fpu_clear_exceptions();\n    asm(\"fucom %2\\n\"\n        \"fstsw %%ax\\n\"\n        : \"=a\" (fpus)\n        : \"t\" (a), \"u\" (b));\n    printf(\"fucom(%f %f)=%04lx\\n\",\n           a, b, fpus & (0x4500 | FPUS_EMASK));\n    if (TEST_FCOMI) {\n        /* test f(u)comi instruction */\n        fpu_clear_exceptions();\n        asm(\"fcomi %3, %2\\n\"\n            \"fstsw %%ax\\n\"\n            \"pushf\\n\"\n            \"pop %0\\n\"\n            : \"=r\" (eflags), \"=a\" (fpus)\n            : \"t\" (a), \"u\" (b));\n        printf(\"fcomi(%f %f)=%04lx %02lx\\n\",\n               a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));\n        fpu_clear_exceptions();\n        asm(\"fucomi %3, %2\\n\"\n            \"fstsw %%ax\\n\"\n            \"pushf\\n\"\n            \"pop %0\\n\"\n            : \"=r\" (eflags), \"=a\" (fpus)\n            : \"t\" (a), \"u\" (b));\n        printf(\"fucomi(%f %f)=%04lx %02lx\\n\",\n               a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));\n    }\n    fpu_clear_exceptions();\n    asm volatile(\"fxam\\n\"\n                 \"fstsw %%ax\\n\"\n                 : \"=a\" (fpus)\n                 : \"t\" (a));\n    printf(\"fxam(%f)=%04lx\\n\", a, fpus & 0x4700);\n    fpu_clear_exceptions();\n}\n\nvoid test_fcvt(double a)\n{\n    float fa;\n    long double la;\n    int16_t fpuc;\n    int i;\n    int64_t lla;\n    int ia;\n    int16_t wa;\n    double ra;\n\n    fa = a;\n    la = a;\n    printf(\"(float)%f = %f\\n\", a, fa);\n    printf(\"(long double)%f = %Lf\\n\", a, la);\n    printf(\"a=\" FMT64X \"\\n\", *(uint64_t *)&a);\n    printf(\"la=\" FMT64X \" %04x\\n\", *(uint64_t *)&la,\n           *(unsigned short *)((char *)(&la) + 8));\n\n    /* test all roundings */\n    asm volatile (\"fstcw %0\" : \"=m\" (fpuc));\n    for(i=0;i<4;i++) {\n        uint16_t val16;\n        val16 = (fpuc & ~0x0c00) | (i << 10);\n        asm volatile (\"fldcw %0\" : : \"m\" (val16));\n        asm volatile (\"fists %0\" : \"=m\" (wa) : \"t\" (a));\n        asm volatile (\"fistl %0\" : \"=m\" (ia) : \"t\" (a));\n        asm volatile (\"fistpll %0\" : \"=m\" (lla) : \"t\" (a) : \"st\");\n        asm volatile (\"frndint ; fstl %0\" : \"=m\" (ra) : \"t\" (a));\n        asm volatile (\"fldcw %0\" : : \"m\" (fpuc));\n        printf(\"(short)a = %d\\n\", wa);\n        printf(\"(int)a = %d\\n\", ia);\n        printf(\"(int64_t)a = \" FMT64X \"\\n\", lla);\n        printf(\"rint(a) = %f\\n\", ra);\n    }\n}\n\n#define TEST(N) \\\n    asm(\"fld\" #N : \"=t\" (a)); \\\n    printf(\"fld\" #N \"= %f\\n\", a);\n\nvoid test_fconst(void)\n{\n    double a;\n    TEST(1);\n    TEST(l2t);\n    TEST(l2e);\n    TEST(pi);\n    TEST(lg2);\n    TEST(ln2);\n    TEST(z);\n}\n\nvoid test_fbcd(double a)\n{\n    unsigned short bcd[5];\n    double b;\n\n    asm(\"fbstp %0\" : \"=m\" (bcd[0]) : \"t\" (a) : \"st\");\n    asm(\"fbld %1\" : \"=t\" (b) : \"m\" (bcd[0]));\n    printf(\"a=%f bcd=%04x%04x%04x%04x%04x b=%f\\n\",\n           a, bcd[4], bcd[3], bcd[2], bcd[1], bcd[0], b);\n}\n\n#define TEST_ENV(env, save, restore)\\\n{\\\n    memset((env), 0xaa, sizeof(*(env)));\\\n    for(i=0;i<5;i++)\\\n        asm volatile (\"fldl %0\" : : \"m\" (dtab[i]));\\\n    asm volatile (save \" %0\\n\" : : \"m\" (*(env)));\\\n    asm volatile (restore \" %0\\n\": : \"m\" (*(env)));\\\n    for(i=0;i<5;i++)\\\n        asm volatile (\"fstpl %0\" : \"=m\" (rtab[i]));\\\n    for(i=0;i<5;i++)\\\n        printf(\"res[%d]=%f\\n\", i, rtab[i]);\\\n    printf(\"fpuc=%04x fpus=%04x fptag=%04x\\n\",\\\n           (env)->fpuc,\\\n           (env)->fpus & 0xff00,\\\n           (env)->fptag);\\\n}\n\nvoid test_fenv(void)\n{\n    struct __attribute__((__packed__)) {\n        uint16_t fpuc;\n        uint16_t dummy1;\n        uint16_t fpus;\n        uint16_t dummy2;\n        uint16_t fptag;\n        uint16_t dummy3;\n        uint32_t ignored[4];\n        long double fpregs[8];\n    } float_env32;\n    struct __attribute__((__packed__)) {\n        uint16_t fpuc;\n        uint16_t fpus;\n        uint16_t fptag;\n        uint16_t ignored[4];\n        long double fpregs[8];\n    } float_env16;\n    double dtab[8];\n    double rtab[8];\n    int i;\n\n    for(i=0;i<8;i++)\n        dtab[i] = i + 1;\n\n    TEST_ENV(&float_env16, \"data16 fnstenv\", \"data16 fldenv\");\n    TEST_ENV(&float_env16, \"data16 fnsave\", \"data16 frstor\");\n    TEST_ENV(&float_env32, \"fnstenv\", \"fldenv\");\n    TEST_ENV(&float_env32, \"fnsave\", \"frstor\");\n\n    /* test for ffree */\n    for(i=0;i<5;i++)\n        asm volatile (\"fldl %0\" : : \"m\" (dtab[i]));\n    asm volatile(\"ffree %st(2)\");\n    asm volatile (\"fnstenv %0\\n\" : : \"m\" (float_env32));\n    asm volatile (\"fninit\");\n    printf(\"fptag=%04x\\n\", float_env32.fptag);\n}\n\n\n#define TEST_FCMOV(a, b, eflags, CC)\\\n{\\\n    double res;\\\n    asm(\"push %3\\n\"\\\n        \"popf\\n\"\\\n        \"fcmov\" CC \" %2, %0\\n\"\\\n        : \"=t\" (res)\\\n        : \"0\" (a), \"u\" (b), \"g\" (eflags));\\\n    printf(\"fcmov%s eflags=0x%04lx-> %f\\n\", \\\n           CC, (long)eflags, res);\\\n}\n\nvoid test_fcmov(void)\n{\n    double a, b;\n    long eflags, i;\n\n    a = 1.0;\n    b = 2.0;\n    for(i = 0; i < 4; i++) {\n        eflags = 0;\n        if (i & 1)\n            eflags |= CC_C;\n        if (i & 2)\n            eflags |= CC_Z;\n        TEST_FCMOV(a, b, eflags, \"b\");\n        TEST_FCMOV(a, b, eflags, \"e\");\n        TEST_FCMOV(a, b, eflags, \"be\");\n        TEST_FCMOV(a, b, eflags, \"nb\");\n        TEST_FCMOV(a, b, eflags, \"ne\");\n        TEST_FCMOV(a, b, eflags, \"nbe\");\n    }\n    TEST_FCMOV(a, b, 0, \"u\");\n    TEST_FCMOV(a, b, CC_P, \"u\");\n    TEST_FCMOV(a, b, 0, \"nu\");\n    TEST_FCMOV(a, b, CC_P, \"nu\");\n}\n\nvoid test_floats(void)\n{\n    test_fops(2, 3);\n    test_fops(1.4, -5);\n    test_fcmp(2, -1);\n    test_fcmp(2, 2);\n    test_fcmp(2, 3);\n    test_fcmp(2, q_nan.d);\n    test_fcmp(q_nan.d, -1);\n    test_fcmp(-1.0/0.0, -1);\n    test_fcmp(1.0/0.0, -1);\n    test_fcvt(0.5);\n    test_fcvt(-0.5);\n    test_fcvt(1.0/7.0);\n    test_fcvt(-1.0/9.0);\n    test_fcvt(32768);\n    test_fcvt(-1e20);\n    test_fcvt(-1.0/0.0);\n    test_fcvt(1.0/0.0);\n    test_fcvt(q_nan.d);\n    test_fconst();\n    //test_fbcd(1234567890123456.0);\n    //test_fbcd(-123451234567890.0);\n    //test_fenv();\n    if (TEST_CMOV) {\n        test_fcmov();\n    }\n}\n\n/**********************************************/\n#if !defined(__x86_64__)\n\n#define TEST_BCD(op, op0, cc_in, cc_mask)\\\n{\\\n    int res, flags;\\\n    res = op0;\\\n    flags = cc_in;\\\n    asm (\"push %3\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         #op \"\\n\\t\"\\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n        : \"=a\" (res), \"=g\" (flags)\\\n        : \"0\" (res), \"1\" (flags));\\\n    printf(\"%-10s A=%08x R=%08x CCIN=%04x CC=%04x\\n\",\\\n           #op, op0, res, cc_in, flags & cc_mask);\\\n}\n\nvoid test_bcd(void)\n{\n    TEST_BCD(daa, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(daa, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n\n    TEST_BCD(das, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n    TEST_BCD(das, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));\n\n    TEST_BCD(aaa, 0x12340205, CC_A, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x12340306, CC_A, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x1234040a, CC_A, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x123405fa, CC_A, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x12340205, 0, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x12340306, 0, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x1234040a, 0, (CC_C | CC_A));\n    TEST_BCD(aaa, 0x123405fa, 0, (CC_C | CC_A));\n\n    TEST_BCD(aas, 0x12340205, CC_A, (CC_C | CC_A));\n    TEST_BCD(aas, 0x12340306, CC_A, (CC_C | CC_A));\n    TEST_BCD(aas, 0x1234040a, CC_A, (CC_C | CC_A));\n    TEST_BCD(aas, 0x123405fa, CC_A, (CC_C | CC_A));\n    TEST_BCD(aas, 0x12340205, 0, (CC_C | CC_A));\n    TEST_BCD(aas, 0x12340306, 0, (CC_C | CC_A));\n    TEST_BCD(aas, 0x1234040a, 0, (CC_C | CC_A));\n    TEST_BCD(aas, 0x123405fa, 0, (CC_C | CC_A));\n\n    TEST_BCD(aam, 0x12340547, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\n    TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\n}\n#endif\n\n#define TEST_XCHG(op, size, opconst)\\\n{\\\n    long op0, op1;\\\n    op0 = i2l(0x12345678);\\\n    op1 = i2l(0xfbca7654);\\\n    asm(#op \" %\" size \"0, %\" size \"1\" \\\n        : \"=q\" (op0), opconst (op1) \\\n        : \"0\" (op0));\\\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \"\\n\",\\\n           #op, op0, op1);\\\n}\n\n#define TEST_CMPXCHG(op, size, opconst, eax)\\\n{\\\n    long op0, op1, op2;\\\n    op0 = i2l(0x12345678);\\\n    op1 = i2l(0xfbca7654);\\\n    op2 = i2l(eax);\\\n    asm(#op \" %\" size \"0, %\" size \"1\" \\\n        : \"=q\" (op0), opconst (op1) \\\n        : \"0\" (op0), \"a\" (op2));\\\n    printf(\"%-10s EAX=\" FMTLX \" A=\" FMTLX \" C=\" FMTLX \"\\n\",\\\n           #op, op2, op0, op1);\\\n}\n\nvoid test_xchg(void)\n{\n#if defined(__x86_64__)\n    TEST_XCHG(xchgq, \"\", \"+q\");\n#endif\n    TEST_XCHG(xchgl, \"k\", \"+q\");\n    TEST_XCHG(xchgw, \"w\", \"+q\");\n    TEST_XCHG(xchgb, \"b\", \"+q\");\n\n#if defined(__x86_64__)\n    TEST_XCHG(xchgq, \"\", \"=m\");\n#endif\n    TEST_XCHG(xchgl, \"k\", \"+m\");\n    TEST_XCHG(xchgw, \"w\", \"+m\");\n    TEST_XCHG(xchgb, \"b\", \"+m\");\n\n#if defined(__x86_64__)\n    TEST_XCHG(xaddq, \"\", \"+q\");\n#endif\n    TEST_XCHG(xaddl, \"k\", \"+q\");\n    TEST_XCHG(xaddw, \"w\", \"+q\");\n    TEST_XCHG(xaddb, \"b\", \"+q\");\n\n    {\n        int res;\n        res = 0x12345678;\n        asm(\"xaddl %1, %0\" : \"=r\" (res) : \"0\" (res));\n        printf(\"xaddl same res=%08x\\n\", res);\n    }\n\n#if defined(__x86_64__)\n    TEST_XCHG(xaddq, \"\", \"+m\");\n#endif\n    TEST_XCHG(xaddl, \"k\", \"+m\");\n    TEST_XCHG(xaddw, \"w\", \"+m\");\n    TEST_XCHG(xaddb, \"b\", \"+m\");\n\n#if defined(__x86_64__)\n    TEST_CMPXCHG(cmpxchgq, \"\", \"+q\", 0xfbca7654);\n#endif\n    //TEST_CMPXCHG(cmpxchgl, \"k\", \"+q\", 0xfbca7654);\n    //TEST_CMPXCHG(cmpxchgw, \"w\", \"+q\", 0xfbca7654);\n    //TEST_CMPXCHG(cmpxchgb, \"b\", \"+q\", 0xfbca7654);\n\n#if defined(__x86_64__)\n    TEST_CMPXCHG(cmpxchgq, \"\", \"+q\", 0xfffefdfc);\n#endif\n    //TEST_CMPXCHG(cmpxchgl, \"k\", \"+q\", 0xfffefdfc);\n    //TEST_CMPXCHG(cmpxchgw, \"w\", \"+q\", 0xfffefdfc);\n    //TEST_CMPXCHG(cmpxchgb, \"b\", \"+q\", 0xfffefdfc);\n\n#if defined(__x86_64__)\n    TEST_CMPXCHG(cmpxchgq, \"\", \"+m\", 0xfbca7654);\n#endif\n    TEST_CMPXCHG(cmpxchgl, \"k\", \"+m\", 0xfbca7654);\n    TEST_CMPXCHG(cmpxchgw, \"w\", \"+m\", 0xfbca7654);\n    TEST_CMPXCHG(cmpxchgb, \"b\", \"+m\", 0xfbca7654);\n\n#if defined(__x86_64__)\n    TEST_CMPXCHG(cmpxchgq, \"\", \"+m\", 0xfffefdfc);\n#endif\n    TEST_CMPXCHG(cmpxchgl, \"k\", \"+m\", 0xfffefdfc);\n    TEST_CMPXCHG(cmpxchgw, \"w\", \"+m\", 0xfffefdfc);\n    TEST_CMPXCHG(cmpxchgb, \"b\", \"+m\", 0xfffefdfc);\n\n    {\n        uint64_t op0, op1, op2;\n        long eax, edx;\n        long i, eflags;\n\n        for(i = 0; i < 2; i++) {\n            op0 = 0x123456789abcdLL;\n            eax = i2l(op0 & 0xffffffff);\n            edx = i2l(op0 >> 32);\n            if (i == 0)\n                op1 = 0xfbca765423456LL;\n            else\n                op1 = op0;\n            op2 = 0x6532432432434LL;\n            asm(\"lock cmpxchg8b %2\\n\"\n                \"pushf\\n\"\n                \"pop %3\\n\"\n                : \"=a\" (eax), \"=d\" (edx), \"=m\" (op1), \"=g\" (eflags)\n                : \"0\" (eax), \"1\" (edx), \"m\" (op1), \"b\" ((int)op2), \"c\" ((int)(op2 >> 32)));\n            printf(\"cmpxchg8b: eax=\" FMTLX \" edx=\" FMTLX \" op1=\" FMT64X \" CC=%02lx\\n\",\n                   eax, edx, op1, eflags & CC_Z);\n        }\n    }\n}\n\n#ifdef TEST_SEGS\n/**********************************************/\n/* segmentation tests */\n\n#include <sys/syscall.h>\n#include <unistd.h>\n#include <asm/ldt.h>\n#include <linux/version.h>\n\nstatic inline int modify_ldt(int func, void * ptr, unsigned long bytecount)\n{\n    return syscall(__NR_modify_ldt, func, ptr, bytecount);\n}\n\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66)\n#define modify_ldt_ldt_s user_desc\n#endif\n\n#define MK_SEL(n) (((n) << 3) | 7)\n\nuint8_t seg_data1[4096];\nuint8_t seg_data2[4096];\n\n#define TEST_LR(op, size, seg, mask)\\\n{\\\n    int res, res2;\\\n    uint16_t mseg = seg;\\\n    res = 0x12345678;\\\n    asm (op \" %\" size \"2, %\" size \"0\\n\" \\\n         \"movl $0, %1\\n\"\\\n         \"jnz 1f\\n\"\\\n         \"movl $1, %1\\n\"\\\n         \"1:\\n\"\\\n         : \"=r\" (res), \"=r\" (res2) : \"m\" (mseg), \"0\" (res));\\\n    printf(op \": Z=%d %08x\\n\", res2, res & ~(mask));\\\n}\n\n#define TEST_ARPL(op, size, op1, op2)\\\n{\\\n    long a, b, c;                               \\\n    a = (op1);                                  \\\n    b = (op2);                                  \\\n    asm volatile(op \" %\" size \"3, %\" size \"0\\n\"\\\n                 \"movl $0,%1\\n\"\\\n                 \"jnz 1f\\n\"\\\n                 \"movl $1,%1\\n\"\\\n                 \"1:\\n\"\\\n                 : \"=r\" (a), \"=r\" (c) : \"0\" (a), \"r\" (b));    \\\n    printf(op size \" A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" z=%ld\\n\",\\\n           (long)(op1), (long)(op2), a, c);\\\n}\n\n/* NOTE: we use Linux modify_ldt syscall */\nvoid test_segs(void)\n{\n    struct modify_ldt_ldt_s ldt;\n    long long ldt_table[3];\n    int res, res2;\n    char tmp;\n    struct {\n        uint32_t offset;\n        uint16_t seg;\n    } __attribute__((__packed__)) segoff;\n\n    ldt.entry_number = 1;\n    ldt.base_addr = (unsigned long)&seg_data1;\n    ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;\n    ldt.seg_32bit = 1;\n    ldt.contents = MODIFY_LDT_CONTENTS_DATA;\n    ldt.read_exec_only = 0;\n    ldt.limit_in_pages = 1;\n    ldt.seg_not_present = 0;\n    ldt.useable = 1;\n    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */\n\n    ldt.entry_number = 2;\n    ldt.base_addr = (unsigned long)&seg_data2;\n    ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12;\n    ldt.seg_32bit = 1;\n    ldt.contents = MODIFY_LDT_CONTENTS_DATA;\n    ldt.read_exec_only = 0;\n    ldt.limit_in_pages = 1;\n    ldt.seg_not_present = 0;\n    ldt.useable = 1;\n    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */\n\n    modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */\n#if 0\n    {\n        int i;\n        for(i=0;i<3;i++)\n            printf(\"%d: %016Lx\\n\", i, ldt_table[i]);\n    }\n#endif\n    /* do some tests with fs or gs */\n    asm volatile (\"movl %0, %%fs\" : : \"r\" (MK_SEL(1)));\n\n    seg_data1[1] = 0xaa;\n    seg_data2[1] = 0x55;\n\n    asm volatile (\"fs movzbl 0x1, %0\" : \"=r\" (res));\n    printf(\"FS[1] = %02x\\n\", res);\n\n    asm volatile (\"pushl %%gs\\n\"\n                  \"movl %1, %%gs\\n\"\n                  \"gs movzbl 0x1, %0\\n\"\n                  \"popl %%gs\\n\"\n                  : \"=r\" (res)\n                  : \"r\" (MK_SEL(2)));\n    printf(\"GS[1] = %02x\\n\", res);\n\n    /* tests with ds/ss (implicit segment case) */\n    tmp = 0xa5;\n    asm volatile (\"pushl %%ebp\\n\\t\"\n                  \"pushl %%ds\\n\\t\"\n                  \"movl %2, %%ds\\n\\t\"\n                  \"movl %3, %%ebp\\n\\t\"\n                  \"movzbl 0x1, %0\\n\\t\"\n                  \"movzbl (%%ebp), %1\\n\\t\"\n                  \"popl %%ds\\n\\t\"\n                  \"popl %%ebp\\n\\t\"\n                  : \"=r\" (res), \"=r\" (res2)\n                  : \"r\" (MK_SEL(1)), \"r\" (&tmp));\n    printf(\"DS[1] = %02x\\n\", res);\n    printf(\"SS[tmp] = %02x\\n\", res2);\n\n    segoff.seg = MK_SEL(2);\n    segoff.offset = 0xabcdef12;\n    asm volatile(\"lfs %2, %0\\n\\t\"\n                 \"movl %%fs, %1\\n\\t\"\n                 : \"=r\" (res), \"=g\" (res2)\n                 : \"m\" (segoff));\n    printf(\"FS:reg = %04x:%08x\\n\", res2, res);\n\n    TEST_LR(\"larw\", \"w\", MK_SEL(2), 0x0100);\n    TEST_LR(\"larl\", \"\", MK_SEL(2), 0x0100);\n    TEST_LR(\"lslw\", \"w\", MK_SEL(2), 0);\n    TEST_LR(\"lsll\", \"\", MK_SEL(2), 0);\n\n    TEST_LR(\"larw\", \"w\", 0xfff8, 0);\n    TEST_LR(\"larl\", \"\", 0xfff8, 0);\n    TEST_LR(\"lslw\", \"w\", 0xfff8, 0);\n    TEST_LR(\"lsll\", \"\", 0xfff8, 0);\n\n    TEST_ARPL(\"arpl\", \"w\", 0x12345678 | 3, 0x762123c | 1);\n    TEST_ARPL(\"arpl\", \"w\", 0x12345678 | 1, 0x762123c | 3);\n    TEST_ARPL(\"arpl\", \"w\", 0x12345678 | 1, 0x762123c | 1);\n}\n\n/* 16 bit code test */\nextern char code16_start, code16_end;\nextern char code16_func1;\nextern char code16_func2;\nextern char code16_func3;\n\nvoid test_code16(void)\n{\n    struct modify_ldt_ldt_s ldt;\n    int res, res2;\n\n    /* build a code segment */\n    ldt.entry_number = 1;\n    ldt.base_addr = (unsigned long)&code16_start;\n    ldt.limit = &code16_end - &code16_start;\n    ldt.seg_32bit = 0;\n    ldt.contents = MODIFY_LDT_CONTENTS_CODE;\n    ldt.read_exec_only = 0;\n    ldt.limit_in_pages = 0;\n    ldt.seg_not_present = 0;\n    ldt.useable = 1;\n    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */\n\n    /* call the first function */\n    asm volatile (\"lcall %1, %2\"\n                  : \"=a\" (res)\n                  : \"i\" (MK_SEL(1)), \"i\" (&code16_func1): \"memory\", \"cc\");\n    printf(\"func1() = 0x%08x\\n\", res);\n    asm volatile (\"lcall %2, %3\"\n                  : \"=a\" (res), \"=c\" (res2)\n                  : \"i\" (MK_SEL(1)), \"i\" (&code16_func2): \"memory\", \"cc\");\n    printf(\"func2() = 0x%08x spdec=%d\\n\", res, res2);\n    asm volatile (\"lcall %1, %2\"\n                  : \"=a\" (res)\n                  : \"i\" (MK_SEL(1)), \"i\" (&code16_func3): \"memory\", \"cc\");\n    printf(\"func3() = 0x%08x\\n\", res);\n}\n#endif\n\n#if defined(__x86_64__)\nasm(\".globl func_lret\\n\"\n    \"func_lret:\\n\"\n    \"movl $0x87654641, %eax\\n\"\n    \"lretq\\n\");\n#else\nasm(\".globl func_lret\\n\"\n    \"func_lret:\\n\"\n    \"movl $0x87654321, %eax\\n\"\n    \"lret\\n\"\n\n    \".globl func_iret\\n\"\n    \"func_iret:\\n\"\n    \"movl $0xabcd4321, %eax\\n\"\n    \"iret\\n\");\n#endif\n\nextern char func_lret;\nextern char func_iret;\n\nvoid test_misc(void)\n{\n    char table[256];\n    long res, i;\n\n    for(i=0;i<256;i++) table[i] = 256 - i;\n    res = 0x12345678;\n    asm (\"xlat\" : \"=a\" (res) : \"b\" (table), \"0\" (res));\n    printf(\"xlat: EAX=\" FMTLX \"\\n\", res);\n\n#if defined(__x86_64__)\n#if 0\n    {\n        /* XXX: see if Intel Core2 and AMD64 behavior really\n           differ. Here we implemented the Intel way which is not\n           compatible yet with QEMU. */\n        static struct QEMU_PACKED {\n            uint64_t offset;\n            uint16_t seg;\n        } desc;\n        long cs_sel;\n\n        asm volatile (\"mov %%cs, %0\" : \"=r\" (cs_sel));\n\n        asm volatile (\"push %1\\n\"\n                      \"call func_lret\\n\"\n                      : \"=a\" (res)\n                      : \"r\" (cs_sel) : \"memory\", \"cc\");\n        printf(\"func_lret=\" FMTLX \"\\n\", res);\n\n        desc.offset = (long)&func_lret;\n        desc.seg = cs_sel;\n\n        asm volatile (\"xor %%rax, %%rax\\n\"\n                      \"rex64 lcall *(%%rcx)\\n\"\n                      : \"=a\" (res)\n                      : \"c\" (&desc)\n                      : \"memory\", \"cc\");\n        printf(\"func_lret2=\" FMTLX \"\\n\", res);\n\n        asm volatile (\"push %2\\n\"\n                      \"mov $ 1f, %%rax\\n\"\n                      \"push %%rax\\n\"\n                      \"rex64 ljmp *(%%rcx)\\n\"\n                      \"1:\\n\"\n                      : \"=a\" (res)\n                      : \"c\" (&desc), \"b\" (cs_sel)\n                      : \"memory\", \"cc\");\n        printf(\"func_lret3=\" FMTLX \"\\n\", res);\n    }\n#endif\n#else\n    asm volatile (\"push %%cs ; call *%1\"\n                  : \"=a\" (res)\n                  : \"m\" (func_lret): \"memory\", \"cc\");\n    printf(\"func_lret=\" FMTLX \"\\n\", res);\n\n    asm volatile (\"pushf ; push %%cs ; call *%1\"\n                  : \"=a\" (res)\n                  : \"m\" (func_iret): \"memory\", \"cc\");\n    printf(\"func_iret=\" FMTLX \"\\n\", res);\n#endif\n\n#if defined(__x86_64__)\n    /* specific popl test */\n    asm volatile (\"push $12345432 ; push $0x9abcdef ; pop (%%rsp) ; pop %0\"\n                  : \"=g\" (res));\n    printf(\"popl esp=\" FMTLX \"\\n\", res);\n#else\n    /* specific popl test */\n    asm volatile (\"pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0\"\n                  : \"=g\" (res));\n    printf(\"popl esp=\" FMTLX \"\\n\", res);\n\n    /* specific popw test */\n    asm volatile (\"pushl $12345432 ; pushl $0x9abcdef ; popw (%%esp) ; addl $2, %%esp ; popl %0\"\n                  : \"=g\" (res));\n    printf(\"popw esp=\" FMTLX \"\\n\", res);\n#endif\n}\n\nuint8_t str_buffer[4096];\n\n#define TEST_STRING1(OP, size, DF, REP)\\\n{\\\n    long esi, edi, eax, ecx, eflags;\\\n\\\n    esi = (long)(str_buffer + sizeof(str_buffer) / 2);\\\n    edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\\\n    eax = i2l(0x12345678);\\\n    ecx = 17;\\\n\\\n    asm volatile (\"push $0\\n\\t\"\\\n                  \"popf\\n\\t\"\\\n                  DF \"\\n\\t\"\\\n                  REP #OP size \"\\n\\t\"\\\n                  \"cld\\n\\t\"\\\n                  \"pushf\\n\\t\"\\\n                  \"pop %4\\n\\t\"\\\n                  : \"=S\" (esi), \"=D\" (edi), \"=a\" (eax), \"=c\" (ecx), \"=g\" (eflags)\\\n                  : \"0\" (esi), \"1\" (edi), \"2\" (eax), \"3\" (ecx));\\\n    printf(\"%-10s ESI=\" FMTLX \" EDI=\" FMTLX \" EAX=\" FMTLX \" ECX=\" FMTLX \" EFL=%04x\\n\",\\\n           REP #OP size, esi - (long) str_buffer, edi - (long) str_buffer, eax, ecx,\\\n           (int)(eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)));\\\n}\n\n#define TEST_STRING(OP, REP)\\\n    TEST_STRING1(OP, \"b\", \"\", REP);\\\n    TEST_STRING1(OP, \"w\", \"\", REP);\\\n    TEST_STRING1(OP, \"l\", \"\", REP);\\\n    X86_64_ONLY(TEST_STRING1(OP, \"q\", \"\", REP));\\\n    TEST_STRING1(OP, \"b\", \"std\", REP);\\\n    TEST_STRING1(OP, \"w\", \"std\", REP);\\\n    TEST_STRING1(OP, \"l\", \"std\", REP);\\\n    X86_64_ONLY(TEST_STRING1(OP, \"q\", \"std\", REP))\n\nvoid test_string(void)\n{\n    int i;\n    for(i = 0;i < sizeof(str_buffer); i++)\n        str_buffer[i] = i + 0x56;\n   TEST_STRING(stos, \"\");\n   TEST_STRING(stos, \"rep \");\n   TEST_STRING(lods, \"\"); /* to verify stos */\n   TEST_STRING(lods, \"rep \");\n   TEST_STRING(movs, \"\");\n   TEST_STRING(movs, \"rep \");\n   TEST_STRING(lods, \"\"); /* to verify stos */\n\n   /* XXX: better tests */\n   TEST_STRING(scas, \"\");\n   TEST_STRING(scas, \"repz \");\n   TEST_STRING(scas, \"repnz \");\n   TEST_STRING(cmps, \"\");\n   TEST_STRING(cmps, \"repz \");\n   TEST_STRING(cmps, \"repnz \");\n}\n\n#ifdef TEST_VM86\n/* VM86 test */\n\nstatic inline void set_bit(uint8_t *a, unsigned int bit)\n{\n    a[bit / 8] |= (1 << (bit % 8));\n}\n\nstatic inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)\n{\n    return (uint8_t *)((seg << 4) + (reg & 0xffff));\n}\n\nstatic inline void pushw(struct vm86_regs *r, int val)\n{\n    r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);\n    *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;\n}\n\nstatic inline int vm86(int func, struct vm86plus_struct *v86)\n{\n    return syscall(__NR_vm86, func, v86);\n}\n\nextern char vm86_code_start;\nextern char vm86_code_end;\n\n#define VM86_CODE_CS 0x100\n#define VM86_CODE_IP 0x100\n\nvoid test_vm86(void)\n{\n    struct vm86plus_struct ctx;\n    struct vm86_regs *r;\n    uint8_t *vm86_mem;\n    int seg, ret;\n\n    vm86_mem = mmap((void *)0x00000000, 0x110000,\n                    PROT_WRITE | PROT_READ | PROT_EXEC,\n                    MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);\n    if (vm86_mem == MAP_FAILED) {\n        printf(\"ERROR: could not map vm86 memory\");\n        return;\n    }\n    memset(&ctx, 0, sizeof(ctx));\n\n    /* init basic registers */\n    r = &ctx.regs;\n    r->eip = VM86_CODE_IP;\n    r->esp = 0xfffe;\n    seg = VM86_CODE_CS;\n    r->cs = seg;\n    r->ss = seg;\n    r->ds = seg;\n    r->es = seg;\n    r->fs = seg;\n    r->gs = seg;\n    r->eflags = VIF_MASK;\n\n    /* move code to proper address. We use the same layout as a .com\n       dos program. */\n    memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,\n           &vm86_code_start, &vm86_code_end - &vm86_code_start);\n\n    /* mark int 0x21 as being emulated */\n    set_bit((uint8_t *)&ctx.int_revectored, 0x21);\n\n    for(;;) {\n        ret = vm86(VM86_ENTER, &ctx);\n        switch(VM86_TYPE(ret)) {\n        case VM86_INTx:\n            {\n                int int_num, ah, v;\n\n                int_num = VM86_ARG(ret);\n                if (int_num != 0x21)\n                    goto unknown_int;\n                ah = (r->eax >> 8) & 0xff;\n                switch(ah) {\n                case 0x00: /* exit */\n                    goto the_end;\n                case 0x02: /* write char */\n                    {\n                        uint8_t c = r->edx;\n                        putchar(c);\n                    }\n                    break;\n                case 0x09: /* write string */\n                    {\n                        uint8_t c, *ptr;\n                        ptr = seg_to_linear(r->ds, r->edx);\n                        for(;;) {\n                            c = *ptr++;\n                            if (c == '$')\n                                break;\n                            putchar(c);\n                        }\n                        r->eax = (r->eax & ~0xff) | '$';\n                    }\n                    break;\n                case 0xff: /* extension: write eflags number in edx */\n                    v = (int)r->edx;\n#ifndef LINUX_VM86_IOPL_FIX\n                    v &= ~0x3000;\n#endif\n                    printf(\"%08x\\n\", v);\n                    break;\n                default:\n                unknown_int:\n                    printf(\"unsupported int 0x%02x\\n\", int_num);\n                    goto the_end;\n                }\n            }\n            break;\n        case VM86_SIGNAL:\n            /* a signal came, we just ignore that */\n            break;\n        case VM86_STI:\n            break;\n        default:\n            printf(\"ERROR: unhandled vm86 return code (0x%x)\\n\", ret);\n            goto the_end;\n        }\n    }\n the_end:\n    printf(\"VM86 end\\n\");\n    munmap(vm86_mem, 0x110000);\n}\n#endif\n\n#ifndef __APPLE__\n/* exception tests */\n#if defined(__i386__) && !defined(REG_EAX)\n#define REG_EAX EAX\n#define REG_EBX EBX\n#define REG_ECX ECX\n#define REG_EDX EDX\n#define REG_ESI ESI\n#define REG_EDI EDI\n#define REG_EBP EBP\n#define REG_ESP ESP\n#define REG_EIP EIP\n#define REG_EFL EFL\n#define REG_TRAPNO TRAPNO\n#define REG_ERR ERR\n#endif\n\n#if defined(__x86_64__)\n#define REG_EIP REG_RIP\n#endif\n\njmp_buf jmp_env;\nint v1;\nint tab[2];\n\nvoid sig_handler(int sig, siginfo_t *info, void *puc)\n{\n    ucontext_t *uc = puc;\n\n    printf(\"si_signo=%d si_errno=%d si_code=%d\",\n           info->si_signo, info->si_errno, info->si_code);\n    printf(\" si_addr=0x%08lx\",\n           (unsigned long)info->si_addr);\n    printf(\"\\n\");\n\n    printf(\"trapno=\" FMTLX \" err=\" FMTLX,\n           (long)uc->uc_mcontext.gregs[REG_TRAPNO],\n           (long)uc->uc_mcontext.gregs[REG_ERR]);\n    printf(\" EIP=\" FMTLX, (long)uc->uc_mcontext.gregs[REG_EIP]);\n    printf(\"\\n\");\n    longjmp(jmp_env, 1);\n}\n\nvoid test_exceptions(void)\n{\n    struct sigaction act;\n    volatile int val;\n\n    act.sa_sigaction = sig_handler;\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_SIGINFO | SA_NODEFER;\n    sigaction(SIGFPE, &act, NULL);\n    sigaction(SIGILL, &act, NULL);\n    sigaction(SIGSEGV, &act, NULL);\n    sigaction(SIGBUS, &act, NULL);\n    sigaction(SIGTRAP, &act, NULL);\n\n    /* test division by zero reporting */\n    printf(\"DIVZ exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* now divide by zero */\n        v1 = 0;\n        v1 = 2 / v1;\n    }\n\n#if !defined(__x86_64__)\n    printf(\"BOUND exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* bound exception */\n        tab[0] = 1;\n        tab[1] = 10;\n        asm volatile (\"bound %0, %1\" : : \"r\" (11), \"m\" (tab[0]));\n    }\n#endif\n\n#ifdef TEST_SEGS\n    printf(\"segment exceptions:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* load an invalid segment */\n        asm volatile (\"movl %0, %%fs\" : : \"r\" ((0x1234 << 3) | 1));\n    }\n    if (setjmp(jmp_env) == 0) {\n        /* null data segment is valid */\n        asm volatile (\"movl %0, %%fs\" : : \"r\" (3));\n        /* null stack segment */\n        asm volatile (\"movl %0, %%ss\" : : \"r\" (3));\n    }\n\n    {\n        struct modify_ldt_ldt_s ldt;\n        ldt.entry_number = 1;\n        ldt.base_addr = (unsigned long)&seg_data1;\n        ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;\n        ldt.seg_32bit = 1;\n        ldt.contents = MODIFY_LDT_CONTENTS_DATA;\n        ldt.read_exec_only = 0;\n        ldt.limit_in_pages = 1;\n        ldt.seg_not_present = 1;\n        ldt.useable = 1;\n        modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */\n\n        if (setjmp(jmp_env) == 0) {\n            /* segment not present */\n            asm volatile (\"movl %0, %%fs\" : : \"r\" (MK_SEL(1)));\n        }\n    }\n#endif\n\n    /* test SEGV reporting */\n    printf(\"PF exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        val = 1;\n        /* we add a nop to test a weird PC retrieval case */\n        asm volatile (\"nop\");\n        /* now store in an invalid address */\n        *(char *)0x1234 = 1;\n    }\n\n    /* test SEGV reporting */\n    printf(\"PF exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        val = 1;\n        /* read from an invalid address */\n        v1 = *(char *)0x1234;\n    }\n\n    /* test illegal instruction reporting */\n    printf(\"UD2 exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* now execute an invalid instruction */\n        asm volatile(\"ud2\");\n    }\n    printf(\"lock nop exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* now execute an invalid instruction */\n        asm volatile(\".byte 0xf0, 0x90\");\n    }\n\n    printf(\"INT exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"int $0xfd\");\n    }\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"int $0x01\");\n    }\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\".byte 0xcd, 0x03\");\n    }\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"int $0x04\");\n    }\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"int $0x05\");\n    }\n\n    printf(\"INT3 exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"int3\");\n    }\n\n    printf(\"CLI exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"cli\");\n    }\n\n    printf(\"STI exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"cli\");\n    }\n\n#if !defined(__x86_64__)\n    printf(\"INTO exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        /* overflow exception */\n        asm volatile (\"addl $1, %0 ; into\" : : \"r\" (0x7fffffff));\n    }\n#endif\n\n    printf(\"OUTB exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"outb %%al, %%dx\" : : \"d\" (0x4321), \"a\" (0));\n    }\n\n    printf(\"INB exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"inb %%dx, %%al\" : \"=a\" (val) : \"d\" (0x4321));\n    }\n\n    printf(\"REP OUTSB exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"rep outsb\" : : \"d\" (0x4321), \"S\" (tab), \"c\" (1));\n    }\n\n    printf(\"REP INSB exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"rep insb\" : : \"d\" (0x4321), \"D\" (tab), \"c\" (1));\n    }\n\n    printf(\"HLT exception:\\n\");\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"hlt\");\n    }\n\n    printf(\"single step exception:\\n\");\n    val = 0;\n    if (setjmp(jmp_env) == 0) {\n        asm volatile (\"pushf\\n\"\n                      \"orl $0x00100, (%%esp)\\n\"\n                      \"popf\\n\"\n                      \"movl $0xabcd, %0\\n\"\n                      \"movl $0x0, %0\\n\" : \"=m\" (val) : : \"cc\", \"memory\");\n    }\n    printf(\"val=0x%x\\n\", val);\n}\n\n#if !defined(__x86_64__)\n/* specific precise single step test */\nvoid sig_trap_handler(int sig, siginfo_t *info, void *puc)\n{\n    ucontext_t *uc = puc;\n    printf(\"EIP=\" FMTLX \"\\n\", (long)uc->uc_mcontext.gregs[REG_EIP]);\n}\n\nconst uint8_t sstep_buf1[4] = { 1, 2, 3, 4};\nuint8_t sstep_buf2[4];\n\nvoid test_single_step(void)\n{\n    struct sigaction act;\n    volatile int val;\n    int i;\n\n    val = 0;\n    act.sa_sigaction = sig_trap_handler;\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_SIGINFO;\n    sigaction(SIGTRAP, &act, NULL);\n    asm volatile (\"pushf\\n\"\n                  \"orl $0x00100, (%%esp)\\n\"\n                  \"popf\\n\"\n                  \"movl $0xabcd, %0\\n\"\n\n                  /* jmp test */\n                  \"movl $3, %%ecx\\n\"\n                  \"1:\\n\"\n                  \"addl $1, %0\\n\"\n                  \"decl %%ecx\\n\"\n                  \"jnz 1b\\n\"\n\n                  /* movsb: the single step should stop at each movsb iteration */\n                  \"movl $sstep_buf1, %%esi\\n\"\n                  \"movl $sstep_buf2, %%edi\\n\"\n                  \"movl $0, %%ecx\\n\"\n                  \"rep movsb\\n\"\n                  \"movl $3, %%ecx\\n\"\n                  \"rep movsb\\n\"\n                  \"movl $1, %%ecx\\n\"\n                  \"rep movsb\\n\"\n\n                  /* cmpsb: the single step should stop at each cmpsb iteration */\n                  \"movl $sstep_buf1, %%esi\\n\"\n                  \"movl $sstep_buf2, %%edi\\n\"\n                  \"movl $0, %%ecx\\n\"\n                  \"rep cmpsb\\n\"\n                  \"movl $4, %%ecx\\n\"\n                  \"rep cmpsb\\n\"\n\n                  /* getpid() syscall: single step should skip one\n                     instruction */\n                  \"movl $20, %%eax\\n\"\n                  \"int $0x80\\n\"\n                  \"movl $0, %%eax\\n\"\n\n                  /* when modifying SS, trace is not done on the next\n                     instruction */\n                  \"movl %%ss, %%ecx\\n\"\n                  \"movl %%ecx, %%ss\\n\"\n                  \"addl $1, %0\\n\"\n                  \"movl $1, %%eax\\n\"\n                  \"movl %%ecx, %%ss\\n\"\n                  \"jmp 1f\\n\"\n                  \"addl $1, %0\\n\"\n                  \"1:\\n\"\n                  \"movl $1, %%eax\\n\"\n                  \"pushl %%ecx\\n\"\n                  \"popl %%ss\\n\"\n                  \"addl $1, %0\\n\"\n                  \"movl $1, %%eax\\n\"\n\n                  \"pushf\\n\"\n                  \"andl $~0x00100, (%%esp)\\n\"\n                  \"popf\\n\"\n                  : \"=m\" (val)\n                  :\n                  : \"cc\", \"memory\", \"eax\", \"ecx\", \"esi\", \"edi\");\n    printf(\"val=%d\\n\", val);\n    for(i = 0; i < 4; i++)\n        printf(\"sstep_buf2[%d] = %d\\n\", i, sstep_buf2[i]);\n}\n\n/* self modifying code test */\nuint8_t code[] = {\n    0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */\n    0xc3, /* ret */\n};\n\nasm(\".section \\\".data\\\"\\n\"\n    \"smc_code2:\\n\"\n    \"movl 4(%esp), %eax\\n\"\n    \"movl %eax, smc_patch_addr2 + 1\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"nop\\n\"\n    \"smc_patch_addr2:\\n\"\n    \"movl $1, %eax\\n\"\n    \"ret\\n\"\n    \".previous\\n\"\n    );\n\ntypedef int FuncType(void);\nextern int smc_code2(int);\nvoid test_self_modifying_code(void)\n{\n    int i;\n    printf(\"self modifying code:\\n\");\n    printf(\"func1 = 0x%x\\n\", ((FuncType *)code)());\n    for(i = 2; i <= 4; i++) {\n        code[1] = i;\n        printf(\"func%d = 0x%x\\n\", i, ((FuncType *)code)());\n    }\n\n    /* more difficult test : the modified code is just after the\n       modifying instruction. It is forbidden in Intel specs, but it\n       is used by old DOS programs */\n    for(i = 2; i <= 4; i++) {\n        printf(\"smc_code2(%d) = %d\\n\", i, smc_code2(i));\n    }\n}\n#endif\n\nlong enter_stack[4096];\n\n#if defined(__x86_64__)\n#define RSP \"%%rsp\"\n#define RBP \"%%rbp\"\n#else\n#define RSP \"%%esp\"\n#define RBP \"%%ebp\"\n#endif\n\n#if !defined(__x86_64__)\n/* causes an infinite loop, disable it for now.  */\n#define TEST_ENTER(size, stack_type, level)\n#else\n#define TEST_ENTER(size, stack_type, level)\\\n{\\\n    long esp_save, esp_val, ebp_val, ebp_save, i;\\\n    stack_type *ptr, *stack_end, *stack_ptr;\\\n    memset(enter_stack, 0, sizeof(enter_stack));\\\n    stack_end = stack_ptr = (stack_type *)(enter_stack + 4096);\\\n    ebp_val = (long)stack_ptr;\\\n    for(i=1;i<=32;i++)\\\n       *--stack_ptr = i;\\\n    esp_val = (long)stack_ptr;\\\n    asm(\"mov \" RSP \", %[esp_save]\\n\"\\\n        \"mov \" RBP \", %[ebp_save]\\n\"\\\n        \"mov %[esp_val], \" RSP \"\\n\"\\\n        \"mov %[ebp_val], \" RBP \"\\n\"\\\n        \"enter\" size \" $8, $\" #level \"\\n\"\\\n        \"mov \" RSP \", %[esp_val]\\n\"\\\n        \"mov \" RBP \", %[ebp_val]\\n\"\\\n        \"mov %[esp_save], \" RSP \"\\n\"\\\n        \"mov %[ebp_save], \" RBP \"\\n\"\\\n        : [esp_save] \"=r\" (esp_save),\\\n        [ebp_save] \"=r\" (ebp_save),\\\n        [esp_val] \"=r\" (esp_val),\\\n        [ebp_val] \"=r\" (ebp_val)\\\n        :  \"[esp_val]\" (esp_val),\\\n        \"[ebp_val]\" (ebp_val));\\\n    printf(\"level=%d:\\n\", level);\\\n    printf(\"esp_val=\" FMTLX \"\\n\", esp_val - (long)stack_end);\\\n    printf(\"ebp_val=\" FMTLX \"\\n\", ebp_val - (long)stack_end);\\\n    for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\\\n        printf(FMTLX \"\\n\", (long)ptr[0]);\\\n}\n#endif\n\nstatic void test_enter(void)\n{\n#if defined(__x86_64__)\n    TEST_ENTER(\"q\", uint64_t, 0);\n    TEST_ENTER(\"q\", uint64_t, 1);\n    TEST_ENTER(\"q\", uint64_t, 2);\n    TEST_ENTER(\"q\", uint64_t, 31);\n#else\n    TEST_ENTER(\"l\", uint32_t, 0);\n    TEST_ENTER(\"l\", uint32_t, 1);\n    TEST_ENTER(\"l\", uint32_t, 2);\n    TEST_ENTER(\"l\", uint32_t, 31);\n#endif\n\n    TEST_ENTER(\"w\", uint16_t, 0);\n    TEST_ENTER(\"w\", uint16_t, 1);\n    TEST_ENTER(\"w\", uint16_t, 2);\n    TEST_ENTER(\"w\", uint16_t, 31);\n}\n#endif\n#ifdef TEST_SSE\n\ntypedef int __m64 __attribute__ ((vector_size (8)));\ntypedef float __m128 __attribute__ ((vector_size (16)));\n\ntypedef union {\n    double d[2];\n    float s[4];\n    uint32_t l[4];\n    uint64_t q[2];\n    __m128 dq;\n} XMMReg;\n\nstatic uint64_t __attribute__((aligned(16))) test_values[4][2] = {\n    { 0x456723c698694873, 0xdc515cff944a58ec },\n    { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 },\n    { 0x007c62c2085427f8, 0x231be9e8cde7438d },\n    { 0x0f76255a085427f8, 0xc233e9e8c4c9439a },\n};\n\n#define MOV_OP(op, hi, rm)\\\n{\\\n    r.q[0] = r.q[1] = 0;\\\n    if (rm) {\\\n        uint64_t mem;\\\n        asm volatile (#op \" %1, %0\" : \"=m\" (mem) : \"x\" (a.dq));\\\n        printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\\n\", #op, a.q[1], a.q[0], mem);\\\n    } else {\\\n        uint64_t mem = a.q[hi];\\\n        asm volatile (#op \" %1, %0\" : \"=x\" (r.dq) : \"m\" (mem));\\\n        printf(\"%-9s: a=\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\", #op, mem, r.q[1], r.q[0]);\\\n    }\\\n}\n#define MOV_OP_REGMEM(op, hi, rm)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    MOV_OP(op, hi, rm);\\\n    }\\\n}\n#define MOVL_OP2(op)\\\n{\\\n    MOV_OP_REGMEM(op, 0, 0);\\\n    MOV_OP_REGMEM(op, 0, 1);\\\n}\n#define MOVH_OP2(op)\\\n{\\\n    MOV_OP_REGMEM(op, 1, 0);\\\n    MOV_OP_REGMEM(op, 1, 1);\\\n}\n#define MOVNT_OP(op, quad)\\\n{\\\n    r.q[0] = r.q[1] = 0;\\\n    if (quad) {\\\n        asm volatile (#op \" %1, %0\" : \"=m\" (r.dq) : \"x\" (a.dq));\\\n        printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\", #op, a.q[1], a.q[0], r.q[1], r.q[0]);\\\n    } else {\\\n        asm volatile (#op \" %1, %0\" : \"=m\" (r.q[0]) : \"y\" (a.q[0]));\\\n        printf(\"%-9s: a=\" FMT64X \" r=\" FMT64X \"\\n\", #op, a.q[0], r.q[0]);\\\n    }\\\n}\n#define MOVNT_OP2(op,quad)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    MOVNT_OP(op, quad);\\\n    }\\\n}\n#define MOVU_OP(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=x\" (r.dq) : \"x\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\",#op, a.q[1], a.q[0], r.q[1], r.q[0]);\\\n}\n#define MOVU_OP2(op)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    MOVU_OP(op);\\\n    }\\\n}\n\n#define SSE_OP(op)\\\n{\\\n    asm volatile (#op \" %2, %0\" : \"=x\" (r.dq) : \"0\" (a.dq), \"x\" (b.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" b=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           b.q[1], b.q[0],\\\n           r.q[1], r.q[0]);\\\n}\n\n#define SSE_OP2(op)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    b.q[0] = test_values[2*i+1][0];\\\n    b.q[1] = test_values[2*i+1][1];\\\n    SSE_OP(op);\\\n    }\\\n}\n\n#define MMX_OP2(op)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    b.q[0] = test_values[2*i+1][0];\\\n    asm volatile (#op \" %2, %0\" : \"=y\" (r.q[0]) : \"0\" (a.q[0]), \"y\" (b.q[0]));\\\n    printf(\"%-9s: a=\" FMT64X \" b=\" FMT64X \" r=\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[0],\\\n           b.q[0],\\\n           r.q[0]);\\\n    }\\\n    SSE_OP2(op);\\\n}\n\n#define SHUF_OP(op, ib)\\\n{\\\n    a.q[0] = test_values[0][0];\\\n    a.q[1] = test_values[0][1];\\\n    b.q[0] = test_values[1][0];\\\n    b.q[1] = test_values[1][1];\\\n    asm volatile (#op \" $\" #ib \", %2, %0\" : \"=x\" (r.dq) : \"0\" (a.dq), \"x\" (b.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" b=\" FMT64X \"\" FMT64X \" ib=%02x r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           b.q[1], b.q[0],\\\n           ib,\\\n           r.q[1], r.q[0]);\\\n}\n\n#define PSHUF_OP(op, ib)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    asm volatile (#op \" $\" #ib \", %1, %0\" : \"=x\" (r.dq) : \"x\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" ib=%02x r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           ib,\\\n           r.q[1], r.q[0]);\\\n    }\\\n}\n\n#define SHIFT_IM(op, ib)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    asm volatile (#op \" $\" #ib \", %0\" : \"=x\" (r.dq) : \"0\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" ib=%02x r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           ib,\\\n           r.q[1], r.q[0]);\\\n    a.q[0] = test_values[2*i][0];\\\n    asm volatile (#op \" $\" #ib \", %0\" : \"=y\" (r.q[0]) : \"0\" (a.q[0]));\\\n    printf(\"%-9s: a=\" FMT64X \" ib=%02x r=\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[0],\\\n           ib,\\\n           r.q[0]);\\\n    }\\\n}\n\n#define SHIFT_DQ_IM(op, ib)\\\n{\\\n    int i;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    asm volatile (#op \" $\" #ib \", %0\" : \"=x\" (r.dq) : \"0\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" ib=%02x r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           ib,\\\n           r.q[1], r.q[0]);\\\n    }\\\n}\n\n#define SHIFT_OP(op, ib)\\\n{\\\n    int i;\\\n    SHIFT_IM(op, ib);\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    b.q[0] = ib;\\\n    b.q[1] = 0;\\\n    asm volatile (#op \" %2, %0\" : \"=x\" (r.dq) : \"0\" (a.dq), \"x\" (b.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" b=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           b.q[1], b.q[0],\\\n           r.q[1], r.q[0]);\\\n    }\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    b.q[0] = ib;\\\n    a.q[1] = b.q[1] = 0;\\\n    asm volatile (#op \" %2, %0\" : \"=y\" (r.q[0]) : \"0\" (a.q[0]), \"y\" (b.q[0]));\\\n    printf(\"%-9s: a=\" FMT64X \" b=\" FMT64X \" r=\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[0],\\\n           b.q[0],\\\n           r.q[0]);\\\n    }\\\n}\n\n#define MOVMSK(op)\\\n{\\\n    int i, reg;\\\n    for(i=0;i<2;i++) {\\\n    a.q[0] = test_values[2*i][0];\\\n    a.q[1] = test_values[2*i][1];\\\n    asm volatile (#op \" %1, %0\" : \"=r\" (reg) : \"x\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=%08x\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           reg);\\\n    }\\\n}\n\n#define SSE_OPS(a) \\\nSSE_OP(a ## ps);\\\nSSE_OP(a ## ss);\n\n#define SSE_OPD(a) \\\nSSE_OP(a ## pd);\\\nSSE_OP(a ## sd);\n\n#define SSE_OPS_S(a) \\\nSSE_OP(a ## ss);\n\n#define SSE_OPD_S(a) \\\nSSE_OP(a ## sd);\n\n#define SSE_COMI(op, field)\\\n{\\\n    unsigned long eflags;\\\n    XMMReg a, b;\\\n    a.field[0] = a1;\\\n    b.field[0] = b1;\\\n    asm volatile (#op \" %2, %1\\n\"\\\n        \"pushf\\n\"\\\n        \"pop %0\\n\"\\\n        : \"=rm\" (eflags)\\\n        : \"x\" (a.dq), \"x\" (b.dq));\\\n    printf(\"%-9s: a=%f b=%f cc=%04x\\n\",\\\n           #op, a1, b1,\\\n           eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\\\n}\n\nvoid test_sse_comi(double a1, double b1)\n{\n    SSE_COMI(ucomiss, s);\n    // SSE_COMI(ucomisd, d);\n    SSE_COMI(comiss, s);\n    // SSE_COMI(comisd, d);\n}\n\n#define CVT_OP_XMM(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=x\" (r.dq) : \"x\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           r.q[1], r.q[0]);\\\n}\n\n/* Force %xmm0 usage to avoid the case where both register index are 0\n   to test instruction decoding more extensively */\n#define CVT_OP_XMM2MMX(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=y\" (r.q[0]) : \"x\" (a.dq) \\\n                  : \"%xmm0\"); \\\n    asm volatile(\"emms\\n\"); \\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           r.q[0]);\\\n}\n\n#define CVT_OP_MMX2XMM(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=x\" (r.dq) : \"y\" (a.q[0]));\\\n    asm volatile(\"emms\\n\"); \\\n    printf(\"%-9s: a=\" FMT64X \" r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.q[0],\\\n           r.q[1], r.q[0]);\\\n}\n\n#define CVT_OP_REG2XMM(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=x\" (r.dq) : \"r\" (a.l[0]));\\\n    printf(\"%-9s: a=%08x r=\" FMT64X \"\" FMT64X \"\\n\",\\\n           #op,\\\n           a.l[0],\\\n           r.q[1], r.q[0]);\\\n}\n\n#define CVT_OP_XMM2REG(op)\\\n{\\\n    asm volatile (#op \" %1, %0\" : \"=r\" (r.l[0]) : \"x\" (a.dq));\\\n    printf(\"%-9s: a=\" FMT64X \"\" FMT64X \" r=%08x\\n\",\\\n           #op,\\\n           a.q[1], a.q[0],\\\n           r.l[0]);\\\n}\n\nstruct fpxstate {\n    uint16_t fpuc;\n    uint16_t fpus;\n    uint16_t fptag;\n    uint16_t fop;\n    uint32_t fpuip;\n    uint16_t cs_sel;\n    uint16_t dummy0;\n    uint32_t fpudp;\n    uint16_t ds_sel;\n    uint16_t dummy1;\n    uint32_t mxcsr;\n    uint32_t mxcsr_mask;\n    uint8_t fpregs1[8 * 16];\n    uint8_t xmm_regs[8 * 16];\n    uint8_t dummy2[224];\n};\n\nstatic struct fpxstate fpx_state __attribute__((aligned(16)));\nstatic struct fpxstate fpx_state2 __attribute__((aligned(16)));\n\nvoid test_fxsave(void)\n{\n    struct fpxstate *fp = &fpx_state;\n    struct fpxstate *fp2 = &fpx_state2;\n    int i, nb_xmm;\n    XMMReg a, b;\n    a.q[0] = test_values[0][0];\n    a.q[1] = test_values[0][1];\n    b.q[0] = test_values[1][0];\n    b.q[1] = test_values[1][1];\n\n    asm(\"movdqa %2, %%xmm0\\n\"\n        \"movdqa %3, %%xmm7\\n\"\n#if defined(__x86_64__)\n        \"movdqa %2, %%xmm15\\n\"\n#endif\n        \" fld1\\n\"\n        \" fldpi\\n\"\n        \" fldln2\\n\"\n        \" fxsave %0\\n\"\n        \" fxrstor %0\\n\"\n        \" fxsave %1\\n\"\n        \" fninit\\n\"\n        : \"=m\" (*(uint32_t *)fp2), \"=m\" (*(uint32_t *)fp)\n        : \"m\" (a), \"m\" (b));\n    printf(\"fpuc=%04x\\n\", fp->fpuc);\n    printf(\"fpus=%04x\\n\", fp->fpus);\n    printf(\"fptag=%04x\\n\", fp->fptag);\n    for(i = 0; i < 3; i++) {\n        printf(\"ST%d: \" FMT64X \" %04x\\n\",\n               i,\n               *(uint64_t *)&fp->fpregs1[i * 16],\n               *(uint16_t *)&fp->fpregs1[i * 16 + 8]);\n    }\n    printf(\"mxcsr=%08x\\n\", fp->mxcsr & 0x1f80);\n#if defined(__x86_64__)\n    nb_xmm = 16;\n#else\n    nb_xmm = 8;\n#endif\n    for(i = 0; i < nb_xmm; i++) {\n        printf(\"xmm%d: \" FMT64X \"\" FMT64X \"\\n\",\n               i,\n               *(uint64_t *)&fp->xmm_regs[i * 16],\n               *(uint64_t *)&fp->xmm_regs[i * 16 + 8]);\n    }\n}\n\nvoid test_sse(void)\n{\n    XMMReg r, a, b;\n    int i;\n\n    // NOTE: when the MMX op is implemented, just change SSE_OP2 to MMX_OP2, which tests both\n    SSE_OP2(punpcklbw);\n    SSE_OP2(punpcklwd);\n    SSE_OP2(punpckldq);\n    SSE_OP2(packsswb);\n    MMX_OP2(pcmpgtb);\n    MMX_OP2(pcmpgtw);\n    MMX_OP2(pcmpgtd);\n    SSE_OP2(packuswb);\n    SSE_OP2(punpckhbw);\n    SSE_OP2(punpckhwd);\n    SSE_OP2(punpckhdq);\n    SSE_OP2(packssdw);\n    MMX_OP2(pcmpeqb);\n    MMX_OP2(pcmpeqw);\n    MMX_OP2(pcmpeqd);\n\n    MMX_OP2(paddq);\n    MMX_OP2(pmullw);\n    SSE_OP2(psubusb);\n    SSE_OP2(psubusw);\n    SSE_OP2(pminub);\n    MMX_OP2(pand);\n    SSE_OP2(paddusb);\n    SSE_OP2(paddusw);\n    SSE_OP2(pmaxub);\n    SSE_OP2(pandn);\n\n    SSE_OP2(pmulhuw);\n    MMX_OP2(pmulhw);\n\n    SSE_OP2(psubsb);\n    SSE_OP2(psubsw);\n    SSE_OP2(pminsw);\n    MMX_OP2(por);\n    SSE_OP2(paddsb);\n    SSE_OP2(paddsw);\n    SSE_OP2(pmaxsw);\n    MMX_OP2(pxor);\n    MMX_OP2(pmuludq);\n    SSE_OP2(pmaddwd);\n    SSE_OP2(psadbw);\n    MMX_OP2(psubb);\n    MMX_OP2(psubw);\n    MMX_OP2(psubd);\n    MMX_OP2(psubq);\n    MMX_OP2(paddb);\n    MMX_OP2(paddw);\n    MMX_OP2(paddd);\n\n    SSE_OP2(pavgb);\n    SSE_OP2(pavgw);\n\n    a.q[0] = test_values[0][0];\n    asm volatile (\"pinsrw $1, %1, %0\" : \"=y\" (r.q[0]) : \"m\" (a.l[0]));\n    printf(\"%-9s: r=\" FMT64X \"\\n\", \"pinsrw\", r.q[0]);\n    asm volatile (\"pinsrw $1, %1, %0\" : \"=y\" (r.q[0]) : \"r\" (0x12345678));\n    printf(\"%-9s: r=\" FMT64X \"\\n\", \"pinsrw\", r.q[0]);\n\n    a.q[0] = test_values[0][0];\n    asm volatile (\"pinsrw $5, %1, %0\" : \"=x\" (r.dq) : \"m\" (a.l[0]));\n    printf(\"%-9s: r=\" FMT64X \"\" FMT64X \"\\n\", \"pinsrw\", r.q[1], r.q[0]);\n    asm volatile (\"pinsrw $5, %1, %0\" : \"=x\" (r.dq) : \"r\" (0x12345678));\n    printf(\"%-9s: r=\" FMT64X \"\" FMT64X \"\\n\", \"pinsrw\", r.q[1], r.q[0]);\n\n    a.q[0] = test_values[0][0];\n    a.q[1] = test_values[0][1];\n    // asm volatile (\"pextrw $1, %1, %0\" : \"=r\" (r.l[0]) : \"y\" (a.q[0]));\n    // printf(\"%-9s: r=%08x\\n\", \"pextrw\", r.l[0]);\n\n    asm volatile (\"pextrw $5, %1, %0\" : \"=r\" (r.l[0]) : \"x\" (a.dq));\n    printf(\"%-9s: r=%08x\\n\", \"pextrw\", r.l[0]);\n\n    asm volatile (\"pmovmskb %1, %0\" : \"=r\" (r.l[0]) : \"y\" (a.q[0]));\n    printf(\"%-9s: r=%08x\\n\", \"pmovmskb\", r.l[0]);\n\n    asm volatile (\"pmovmskb %1, %0\" : \"=r\" (r.l[0]) : \"x\" (a.dq));\n    printf(\"%-9s: r=%08x\\n\", \"pmovmskb\", r.l[0]);\n\n    // {\n        // r.q[0] = -1;\n        // r.q[1] = -1;\n\n        // a.q[0] = test_values[0][0];\n        // a.q[1] = test_values[0][1];\n        // b.q[0] = test_values[1][0];\n        // b.q[1] = test_values[1][1];\n        // asm volatile(\"maskmovq %1, %0\" :\n                     // : \"y\" (a.q[0]), \"y\" (b.q[0]), \"D\" (&r)\n                     // : \"memory\");\n        // printf(\"%-9s: r=\" FMT64X \" a=\" FMT64X \" b=\" FMT64X \"\\n\",\n               // \"maskmov\",\n               // r.q[0],\n               // a.q[0],\n               // b.q[0]);\n        // asm volatile(\"maskmovdqu %1, %0\" :\n                     // : \"x\" (a.dq), \"x\" (b.dq), \"D\" (&r)\n                     // : \"memory\");\n        // printf(\"%-9s: r=\" FMT64X \"\" FMT64X \" a=\" FMT64X \"\" FMT64X \" b=\" FMT64X \"\" FMT64X \"\\n\",\n               // \"maskmov\",\n               // r.q[1], r.q[0],\n               // a.q[1], a.q[0],\n               // b.q[1], b.q[0]);\n    // }\n\n    asm volatile (\"emms\");\n\n    SSE_OP2(punpcklqdq);\n    SSE_OP2(punpckhqdq);\n    SSE_OP2(andps);\n    SSE_OP2(andpd);\n    SSE_OP2(andnps);\n    SSE_OP2(andnpd);\n    SSE_OP2(orps);\n    SSE_OP2(orpd);\n    SSE_OP2(xorps);\n    SSE_OP2(xorpd);\n\n    SSE_OP2(unpcklps);\n    SSE_OP2(unpcklpd);\n    SSE_OP2(unpckhps);\n    SSE_OP2(unpckhpd);\n\n    SHUF_OP(shufps, 0x78);\n    SHUF_OP(shufpd, 0x02);\n\n    PSHUF_OP(pshufd, 0x78);\n    PSHUF_OP(pshuflw, 0x78);\n    PSHUF_OP(pshufhw, 0x78);\n\n    SHIFT_OP(psrlw, 7);\n    SHIFT_OP(psrlw, 16);\n    SHIFT_OP(psraw, 7);\n    SHIFT_OP(psraw, 16);\n    SHIFT_OP(psllw, 7);\n    SHIFT_OP(psllw, 16);\n\n    SHIFT_OP(psrld, 7);\n    SHIFT_OP(psrld, 32);\n    SHIFT_OP(psrad, 7);\n    SHIFT_OP(psrad, 32);\n    SHIFT_OP(pslld, 7);\n    SHIFT_OP(pslld, 32);\n\n    SHIFT_OP(psrlq, 7);\n    SHIFT_OP(psrlq, 32);\n    SHIFT_OP(psllq, 7);\n    SHIFT_OP(psllq, 32);\n\n    SHIFT_DQ_IM(psrldq, 16);\n    SHIFT_DQ_IM(psrldq, 7);\n    SHIFT_DQ_IM(pslldq, 16);\n    SHIFT_DQ_IM(pslldq, 7);\n\n    // MOVMSK(movmskps);\n    MOVMSK(movmskpd);\n\n    /* FPU specific ops */\n    asm volatile (\"emms\");\n\n    // {\n        // uint32_t mxcsr;\n        // asm volatile(\"stmxcsr %0\" : \"=m\" (mxcsr));\n        // printf(\"mxcsr=%08x\\n\", mxcsr & 0x1f80);\n        // asm volatile(\"ldmxcsr %0\" : : \"m\" (mxcsr));\n    // }\n\n    test_sse_comi(2, -1);\n    test_sse_comi(2, 2);\n    test_sse_comi(2, 3);\n    test_sse_comi(2, q_nan.d);\n    test_sse_comi(q_nan.d, -1);\n\n    for(i = 0; i < 2; i++) {\n        a.s[0] = 2.7;\n        a.s[1] = 3.4;\n        a.s[2] = 4;\n        a.s[3] = -6.3;\n        b.s[0] = 45.7;\n        b.s[1] = 353.4;\n        b.s[2] = 4;\n        b.s[3] = 56.3;\n        if (i == 1) {\n            a.s[0] = q_nan.d;\n            b.s[3] = q_nan.d;\n        }\n\n        SSE_OPS(add);\n        SSE_OPS(mul);\n        SSE_OPS(sub);\n        // SSE_OPS(min);\n        SSE_OPS_S(div);\n        // SSE_OPS(max);\n        // SSE_OPS(sqrt);\n        SSE_OPS_S(cmpeq);\n        SSE_OPS_S(cmplt);\n        SSE_OPS_S(cmple);\n        SSE_OPS_S(cmpunord);\n        SSE_OPS_S(cmpneq);\n        SSE_OPS_S(cmpnlt);\n        SSE_OPS_S(cmpnle);\n        SSE_OPS_S(cmpord);\n\n\n        a.d[0] = 2.7;\n        a.d[1] = -3.4;\n        b.d[0] = 45.7;\n        b.d[1] = -53.4;\n        if (i == 1) {\n            a.d[0] = q_nan.d;\n            b.d[1] = q_nan.d;\n        }\n        SSE_OPD(add);\n        SSE_OPD(mul);\n        SSE_OPD(sub);\n        SSE_OPD_S(min);\n        SSE_OPD_S(div);\n        SSE_OPD_S(max);\n        SSE_OPD_S(sqrt);\n        SSE_OPD(cmpeq);\n        SSE_OPD(cmplt);\n        SSE_OPD(cmple);\n        SSE_OPD(cmpunord);\n        SSE_OPD(cmpneq);\n        SSE_OPD(cmpnlt);\n        SSE_OPD(cmpnle);\n        SSE_OPD(cmpord);\n    }\n\n    /* float to float/int */\n    a.s[0] = 2.7;\n    a.s[1] = 3.4;\n    a.s[2] = 4;\n    a.s[3] = -6.3;\n    // CVT_OP_XMM(cvtps2pd);\n    CVT_OP_XMM(cvtss2sd);\n    // CVT_OP_XMM2MMX(cvtps2pi);\n    // CVT_OP_XMM2MMX(cvttps2pi);\n    // CVT_OP_XMM2REG(cvtss2si);\n    CVT_OP_XMM2REG(cvttss2si);\n    // CVT_OP_XMM(cvtps2dq);\n    CVT_OP_XMM(cvttps2dq);\n\n    a.d[0] = 2.6;\n    a.d[1] = -3.4;\n    // CVT_OP_XMM(cvtpd2ps);\n    CVT_OP_XMM(cvtsd2ss);\n    // CVT_OP_XMM2MMX(cvtpd2pi);\n    // CVT_OP_XMM2MMX(cvttpd2pi);\n    // CVT_OP_XMM2REG(cvtsd2si);\n    CVT_OP_XMM2REG(cvttsd2si);\n    // CVT_OP_XMM(cvtpd2dq);\n    CVT_OP_XMM(cvttpd2dq);\n\n    /* sse/mmx moves */\n    // CVT_OP_XMM2MMX(movdq2q);\n    // CVT_OP_MMX2XMM(movq2dq);\n\n    /* int to float */\n    a.l[0] = -6;\n    a.l[1] = 2;\n    a.l[2] = 100;\n    a.l[3] = -60000;\n    // CVT_OP_MMX2XMM(cvtpi2ps);\n    // CVT_OP_MMX2XMM(cvtpi2pd);\n    CVT_OP_REG2XMM(cvtsi2ss);\n    CVT_OP_REG2XMM(cvtsi2sd);\n    // CVT_OP_XMM(cvtdq2ps);\n    // CVT_OP_XMM(cvtdq2pd);\n\n    /* sse/sse2 moves */\n    MOVL_OP2(movlps);\n    MOVH_OP2(movhps);\n    MOVL_OP2(movlpd);\n    MOVH_OP2(movhpd);\n    MOVNT_OP2(movntq, 0);\n    MOVNT_OP2(movntdq, 1);\n    MOVU_OP2(movups);\n    MOVU_OP2(movupd);\n\n    /* misc sse ops*/\n    SSE_OP2(minss);\n    SSE_OP2(maxss);\n    SSE_OP2(sqrtss);\n\n    /* XXX: test PNI insns */\n#if 0\n    SSE_OP2(movshdup);\n#endif\n    asm volatile (\"emms\");\n}\n\n#endif\n\n#define TEST_CONV_RAX(op)\\\n{\\\n    unsigned long a, r;\\\n    a = i2l(0x8234a6f8);\\\n    r = a;\\\n    asm volatile(#op : \"=a\" (r) : \"0\" (r));\\\n    printf(\"%-10s A=\" FMTLX \" R=\" FMTLX \"\\n\", #op, a, r);\\\n}\n\n#define TEST_CONV_RAX_RDX(op)\\\n{\\\n    unsigned long a, d, r, rh;                   \\\n    a = i2l(0x8234a6f8);\\\n    d = i2l(0x8345a1f2);\\\n    r = a;\\\n    rh = d;\\\n    asm volatile(#op : \"=a\" (r), \"=d\" (rh) : \"0\" (r), \"1\" (rh));   \\\n    printf(\"%-10s A=\" FMTLX \" R=\" FMTLX \":\" FMTLX \"\\n\", #op, a, r, rh);  \\\n}\n\nvoid test_conv(void)\n{\n    TEST_CONV_RAX(cbw);\n    TEST_CONV_RAX(cwde);\n#if defined(__x86_64__)\n    TEST_CONV_RAX(cdqe);\n#endif\n\n    TEST_CONV_RAX_RDX(cwd);\n    TEST_CONV_RAX_RDX(cdq);\n#if defined(__x86_64__)\n    TEST_CONV_RAX_RDX(cqo);\n#endif\n\n    {\n        unsigned long a, r;\n        a = i2l(0x12345678);\n        asm volatile(\"bswapl %k0\" : \"=r\" (r) : \"0\" (a));\n        printf(\"%-10s: A=\" FMTLX \" R=\" FMTLX \"\\n\", \"bswapl\", a, r);\n    }\n#if defined(__x86_64__)\n    {\n        unsigned long a, r;\n        a = i2l(0x12345678);\n        asm volatile(\"bswapq %0\" : \"=r\" (r) : \"0\" (a));\n        printf(\"%-10s: A=\" FMTLX \" R=\" FMTLX \"\\n\", \"bswapq\", a, r);\n    }\n#endif\n}\n\nint main(int argc, char **argv)\n{\n    void **ptr;\n    void (*func)(void);\n\n    ptr = &__start_initcall;\n    while (ptr != &__stop_initcall) {\n        func = *ptr++;\n        func();\n    }\n    test_bsx();\n    test_mul();\n    test_jcc();\n    //test_loop();\n    test_floats();\n#if !defined(__x86_64__)\n    //test_bcd();\n#endif\n    test_xchg();\n    test_string();\n    //test_misc();\n    test_lea();\n#ifdef TEST_SEGS\n    test_segs();\n    test_code16();\n#endif\n#ifdef TEST_VM86\n    test_vm86();\n#endif\n#if !defined(__x86_64__)\n    //test_exceptions();\n    //test_self_modifying_code();\n    //test_single_step();\n#endif\n    //test_enter();\n    test_conv();\n#ifdef TEST_SSE\n    test_sse();\n    //test_fxsave();\n#endif\n    return 0;\n}\n"
  },
  {
    "path": "tests/e2e/qemu/qemu-test.h",
    "content": "\n#define exec_op glue(exec_, OP)\n#define exec_opq glue(glue(exec_, OP), q)\n#define exec_opl glue(glue(exec_, OP), l)\n#define exec_opw glue(glue(exec_, OP), w)\n#define exec_opb glue(glue(exec_, OP), b)\n\n#define EXECOP2(size, rsize, res, s1, flags) \\\n    asm (\"push %4\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         stringify(OP) size \" %\" rsize \"2, %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=q\" (res), \"=g\" (flags)\\\n         : \"q\" (s1), \"0\" (res), \"1\" (flags)); \\\n    printf(\"%-10s A=\" FMTLX \" B=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\", \\\n           stringify(OP) size, s0, s1, res, iflags, flags & CC_MASK);\n\n#define EXECOP1(size, rsize, res, flags) \\\n    asm (\"push %3\\n\\t\"\\\n         \"popf\\n\\t\"\\\n         stringify(OP) size \" %\" rsize \"0\\n\\t\" \\\n         \"pushf\\n\\t\"\\\n         \"pop %1\\n\\t\"\\\n         : \"=q\" (res), \"=g\" (flags)\\\n         : \"0\" (res), \"1\" (flags)); \\\n    printf(\"%-10s A=\" FMTLX \" R=\" FMTLX \" CCIN=%04lx CC=%04lx\\n\", \\\n           stringify(OP) size, s0, res, iflags, flags & CC_MASK);\n\n#ifdef OP1\n#if defined(__x86_64__)\nvoid exec_opq(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP1(\"q\", \"\", res, flags);\n}\n#endif\n\nvoid exec_opl(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP1(\"l\", \"k\", res, flags);\n}\n\nvoid exec_opw(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP1(\"w\", \"w\", res, flags);\n}\n\nvoid exec_opb(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP1(\"b\", \"b\", res, flags);\n}\n#else\n#if defined(__x86_64__)\nvoid exec_opq(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP2(\"q\", \"\", res, s1, flags);\n}\n#endif\n\nvoid exec_opl(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP2(\"l\", \"k\", res, s1, flags);\n}\n\nvoid exec_opw(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP2(\"w\", \"w\", res, s1, flags);\n}\n\nvoid exec_opb(long s0, long s1, long iflags)\n{\n    long res, flags;\n    res = s0;\n    flags = iflags;\n    EXECOP2(\"b\", \"b\", res, s1, flags);\n}\n#endif\n\nvoid exec_op(long s0, long s1)\n{\n    s0 = i2l(s0);\n    s1 = i2l(s1);\n#if defined(__x86_64__)\n    exec_opq(s0, s1, 0);\n#endif\n    exec_opl(s0, s1, 0);\n    exec_opw(s0, s1, 0);\n    exec_opb(s0, s1, 0);\n#ifdef OP_CC\n#if defined(__x86_64__)\n    exec_opq(s0, s1, CC_C);\n#endif\n    exec_opl(s0, s1, CC_C);\n    exec_opw(s0, s1, CC_C);\n    exec_opb(s0, s1, CC_C);\n#endif\n}\n\nvoid glue(test_, OP)(void)\n{\n    exec_op(0x12345678, 0x812FADA);\n    exec_op(0x12341, 0x12341);\n    exec_op(0x12341, -0x12341);\n    exec_op(0xffffffff, 0);\n    exec_op(0xffffffff, -1);\n    exec_op(0xffffffff, 1);\n    exec_op(0xffffffff, 2);\n    exec_op(0x7fffffff, 0);\n    exec_op(0x7fffffff, 1);\n    exec_op(0x7fffffff, -1);\n    exec_op(0x80000000, -1);\n    exec_op(0x80000000, 1);\n    exec_op(0x80000000, -2);\n    exec_op(0x12347fff, 0);\n    exec_op(0x12347fff, 1);\n    exec_op(0x12347fff, -1);\n    exec_op(0x12348000, -1);\n    exec_op(0x12348000, 1);\n    exec_op(0x12348000, -2);\n    exec_op(0x12347f7f, 0);\n    exec_op(0x12347f7f, 1);\n    exec_op(0x12347f7f, -1);\n    exec_op(0x12348080, -1);\n    exec_op(0x12348080, 1);\n    exec_op(0x12348080, -2);\n}\n\nvoid *glue(_test_, OP) __init_call = glue(test_, OP);\n\n#undef OP\n#undef OP_CC\n"
  },
  {
    "path": "tests/e2e/qemu/test.sh",
    "content": "#!/bin/sh\n# -no-pie because the test contains non-position-independent inline asm\n# silence a few warnings that I can't be bothered to fix\ngcc qemu-test.c -o qemu-test -msse2 -no-pie -Wno-attributes -Wno-format\n./qemu-test\n"
  },
  {
    "path": "tests/e2e/shell/expected.txt",
    "content": "builtin echo\nreal echo\nstarted sleep in background\nkilled it\n"
  },
  {
    "path": "tests/e2e/shell/test.sh",
    "content": "#!/bin/sh\nif [ ! -e /dev/null ]; then\n    mknod /dev/null c 1 3 # shell uses this internally\nfi\n\necho builtin echo\n/bin/echo real echo\n\n# some background stuff\nsleep 1000 &\nbg=$!\necho started sleep in background\nkill $bg\necho killed it\n"
  },
  {
    "path": "tests/e2e/sse2/expected.txt",
    "content": "movaps\n11.11 22.22 33.33 44.44\n55.55 66.66 77.77 88.88\n55.55 66.66 77.77 88.88\nmovss\n11.11 22.22 33.33 44.44\n55.55 66.66 77.77 88.88\n55.55 22.22 33.33 44.44\n16.12 00.00 00.00 00.00\n16.12\nxorps\n0.00E+00 0.00E+00 0.00E+00 0.00E+00\n1.11E+01 2.22E+01 3.33E+01 4.44E+01\n5.55E+01 6.67E+01 7.78E+01 8.89E+01\n1.11E+01 2.22E+01 3.33E+01 4.44E+01\n0.00E+00 0.00E+00 0.00E+00 0.00E+00\n7.05E-37 5.31E-37 1.46E-38 1.18E-38\n1.11E+01 2.22E+01 3.33E+01 4.44E+01\n0.00E+00 0.00E+00 0.00E+00 0.00E+00\npsllq\n01234 05678\n02468 11356\n00000 00000\npsrlq\n01234 05678\n00617 02839\n00000 00000\npaddq\n01234 05678\n11111 11111\n12345 16789\n"
  },
  {
    "path": "tests/e2e/sse2/movaps.c",
    "content": "#include <stdio.h>\n#include <xmmintrin.h>\n\n#define printout() printf(\"%05.2f %05.2f %05.2f %05.2f\\n\", out[0], out[1], out[2], out[3])\n\nvoid main(void) {\n\tfloat out[4] = { 0, 0, 0, 0 };\n\tfloat buf1234[4] = { 11.11, 22.22, 33.33, 44.44 };\n\tfloat buf5678[4] = { 55.55, 66.66, 77.77, 88.88 };\n    float fa = 16.12;\n\n\t// xmm1 Initially 1234\n\t__m128 xmm1 = _mm_load_ps((float*) buf1234);\n\t_mm_store_ps((float*) out, xmm1);\n\tprintout();\n\n\t// xmm2 Initially 5678\n\t__m128 xmm2 = _mm_load_ps((float*) buf5678);\n\t_mm_store_ps((float*) out, xmm2);\n\tprintout();\n\n\t// Move xmm2 onto xmm1\n\tasm volatile(\t\"movaps %[vec2], %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (xmm1)\n\t\t: [vec2] \"x\" (xmm2));\n\n\t_mm_store_ps((float*) out, xmm1);\n    printout();\n} \n"
  },
  {
    "path": "tests/e2e/sse2/movss.c",
    "content": "#include <stdio.h>\n#include <xmmintrin.h>\n\n#define NOINLINE __attribute__ ((noinline))\n#define printout() printf(\"%05.2f %05.2f %05.2f %05.2f\\n\", out[0], out[1], out[2], out[3])\n\nvoid move5(__m128 *xmm1, __m128 *xmm2);\nvoid move1612(__m128 *xmm1, float fa);\nvoid store1612(__m128 *xmm1, float *fa);\n\nvoid main(void) {\n\tfloat out[4] = { 0, 0, 0, 0 };\n\tfloat buf1234[4] = { 11.11, 22.22, 33.33, 44.44 };\n\tfloat buf5678[4] = { 55.55, 66.66, 77.77, 88.88 };\n    float fa = 16.12;\n\n\t// xmm1 Initially 1234\n\t__m128 xmm1 = _mm_load_ps((float*) buf1234);\n\t_mm_store_ps((float*) out, xmm1);\n\tprintout();\n\n\t// xmm2 Initially 5678\n\t__m128 xmm2 = _mm_load_ps((float*) buf5678);\n\t_mm_store_ps((float*) out, xmm2);\n\tprintout();\n\n    //move5(&xmm1, &xmm2);\n\t__m128 xmm3 = _mm_move_ss(xmm1, xmm2);\n\t_mm_store_ps((float*) out, xmm3);\n    printout();\n\n    move1612(&xmm1, fa);\n\t_mm_store_ps((float*) out, xmm1);\n    printout();\n\n    fa = 00.00;\n    store1612(&xmm1, &fa);\n    printf(\"%05.2f\\n\", fa);\n}\n\nvoid NOINLINE move5(__m128 *xmm1, __m128 *xmm2) {\n\t// Move the 5 from 5678, rest should remain: 5234.\n\t//*xmm1 = _mm_move_ss(*xmm1, *xmm2);\n\tasm volatile(\t\"movss %[vec2], %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (*xmm1)\n\t\t: [vec2] \"x\" (*xmm2));\n}\n\nvoid NOINLINE move1612(__m128 *xmm1, float fa) {\n    // Move the 16.12 into first position of xmm1.\n    // This is mem, so rest should be zeroed.\n\tasm volatile(\t\"movss %[flt], %[vec]\\n\\t\"\n\t\t: [vec] \"+x\" (*xmm1)\n\t\t: [flt] \"m\" (fa));\n}\n\nvoid NOINLINE store1612(__m128 *xmm1, float *fa) {\n    // Store the 16.12 into float.\n\tasm volatile(\t\"movss %[vec], %[flt]\\n\\t\"\n\t\t: [flt] \"+m\" (*fa)\n\t\t: [vec] \"x\" (*xmm1));\n}\n"
  },
  {
    "path": "tests/e2e/sse2/paddq.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <emmintrin.h>\n#include <xmmintrin.h>\n\n#define printout() printf(\"%05lld %05lld\\n\", (long long) out[0], (long long) out[1])\n\nvoid main(void) {\n    int64_t out[2] = { 0, 0 };\n    int64_t buf1234[2] = {  1234,  5678 };\n    int64_t buf1111[2] = { 11111, 11111 };\n\n    // xmm1 Initially 1234\n    __m128i xmm1 = _mm_load_si128((__m128i*) buf1234);\n    _mm_store_si128((__m128i*) out, xmm1);\n    printout();\n\n    // xmm2 Initially 1111\n    __m128i xmm2 = _mm_load_si128((__m128i*) buf1111);\n    _mm_store_si128((__m128i*) out, xmm2);\n    printout();\n\n    // Result is just each added by 1\n    asm volatile(\t\"paddq %[vec2], %[vec1]\\n\\t\"\n            : [vec1] \"+x\" (xmm1), [vec2] \"+x\" (xmm2)\n            :);\n    _mm_store_si128((__m128i*) out, xmm1);\n    printout();\n}\n"
  },
  {
    "path": "tests/e2e/sse2/psllq.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <emmintrin.h>\n#include <xmmintrin.h>\n\n#define printout() printf(\"%05lld %05lld\\n\", (long long) out[0], (long long) out[1])\n\nvoid main(void) {\n\tint64_t out[2] = { 0, 0 };\n\tint64_t buf1234[2] = { 1234, 5678 };\n\n\t// xmm1 Initially 1234\n\t__m128i xmm1 = _mm_load_si128((__m128i*) buf1234);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n\n\t// xmm2 is just multiply by 2\n\tasm volatile(\t\"psllq $1, %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (xmm1)\n\t\t:);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n\n\t// xmm1 should be cleared\n\tasm volatile(\t\"psllq $66, %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (xmm1)\n\t\t:);\n\t//shift_right(&xmm1, 66);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n}\n\n"
  },
  {
    "path": "tests/e2e/sse2/psrlq.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <emmintrin.h>\n#include <xmmintrin.h>\n\n#define printout() printf(\"%05lld %05lld\\n\", (long long) out[0], (long long) out[1])\n\nvoid main(void) {\n\tint64_t out[2] = { 0, 0 };\n\tint64_t buf1234[2] = { 1234, 5678 };\n\n\t// xmm1 Initially 1234\n\t__m128i xmm1 = _mm_load_si128((__m128i*) buf1234);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n\n\t// xmm2 is just divide by 2\n\tasm volatile(\t\"psrlq $1, %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (xmm1)\n\t\t:);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n\n\t// xmm1 should be cleared\n\tasm volatile(\t\"psrlq $66, %[vec1]\\n\\t\"\n\t\t: [vec1] \"+x\" (xmm1)\n\t\t:);\n\t//shift_right(&xmm1, 66);\n\t_mm_store_si128((__m128i*) out, xmm1);\n\tprintout();\n}\n\n"
  },
  {
    "path": "tests/e2e/sse2/test.sh",
    "content": "#!/bin/sh\ngcc -msse2 movaps.c -o test_movaps\necho movaps\n./test_movaps\n\necho movss\ngcc -msse2 movss.c -o test_movss\n./test_movss\n\necho xorps\ngcc -msse2 xorps.c -o test_xorps\n./test_xorps\n\necho psllq\ngcc -msse2 psllq.c -o test_psllq\n./test_psllq\n\necho psrlq\ngcc -msse2 psrlq.c -o test_psrlq\n./test_psrlq\n\necho paddq\ngcc -msse2 paddq.c -o test_paddq\n./test_paddq\n"
  },
  {
    "path": "tests/e2e/sse2/xorps.c",
    "content": "#include <stdio.h>\n#include <xmmintrin.h>\n\n#define printout() printf(\"%05.2E %05.2E %05.2E %05.2E\\n\", out[0], out[1], out[2], out[3])\n\nvoid main(void) {\n\tfloat out[4] = { 0, 0, 0, 0 };\n\tfloat buf0000[4] = { 00.00, 00.00, 00.00, 00.00 };\n\tfloat buf1234[4] = { 11.11, 22.22, 33.33, 44.44 };\n\tfloat buf5678[4] = { 55.55, 66.66, 77.77, 88.88 };\n\n\t// xmm0 Initially 1234\n\t__m128 xmm0 = _mm_load_ps((float*) buf0000);\n\t_mm_store_ps((float*) out, xmm0);\n\tprintout();\n\n\t// xmm1 Initially 1234\n\t__m128 xmm1 = _mm_load_ps((float*) buf1234);\n\t_mm_store_ps((float*) out, xmm1);\n\tprintout();\n\n\t// xmm2 Initially 5678\n\t__m128 xmm2 = _mm_load_ps((float*) buf5678);\n\t_mm_store_ps((float*) out, xmm2);\n\tprintout();\n\n    // 0000 ^ 1234 = 1234\n    _mm_store_ps((float*) out, _mm_xor_ps(xmm0, xmm1));\n    printout();\n\n    // 1234 ^ 1234 = 0000\n    _mm_store_ps((float*) out, _mm_xor_ps(xmm1, xmm1));\n    printout();\n\n    // 1234 ^ 5678 = some known value\n    _mm_store_ps((float*) out, _mm_xor_ps(xmm1, xmm2));\n    printout();\n\n    // 5678 ^ (1234 ^ 5678) = 1234\n    _mm_store_ps((float*) out, _mm_xor_ps(xmm2, _mm_xor_ps(xmm1, xmm2)));\n    printout();\n\n    // setzero with xorps = 0000\n    _mm_store_ps((float*) out, _mm_setzero_ps());\n    printout();\n}\n"
  },
  {
    "path": "tests/manual/cat.c",
    "content": "#include <stdio.h>\n\nint main(int argc, const char *argv[]) {\n    FILE *f = stdin;\n    if (argc > 1)\n        f = fopen(argv[1], \"r\");\n    if (f == NULL) {\n        fprintf(stderr, \"cat: \");\n        perror(argv[1]);\n        return 1;\n    }\n\n    int c;\n    while ((c = fgetc(f)) != EOF)\n        putchar(c);\n}\n"
  },
  {
    "path": "tests/manual/fibbonaci.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nunsigned long fib(unsigned long n) {\n    if (n <= 1)\n        return 1;\n    return fib(n - 1) + fib(n - 2);\n}\n\nint main(int argc, const char *argv[]) {\n    if (argc != 2) {\n        printf(\"argc is %d\\n\", argc);\n        printf(\"please specify a number to fibbonaci\\n\");\n        return 1;\n    }\n    unsigned long n = strtoul(argv[1], NULL, 10);\n    printf(\"%lu\\n\", fib(n));\n    return 0;\n}\n\n"
  },
  {
    "path": "tests/manual/forkexec.c",
    "content": "#include <unistd.h>\n#include <sys/wait.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nint main(int argc, char *argv[]) {\n    int pid = fork();\n    if (pid < 0) {\n        perror(\"fork\");\n        abort();\n    }\n    if (pid == 0) {\n        // child\n        if (execv(argv[1], argv + 1) < 0) {\n            perror(\"exec\");\n            abort();\n        }\n    } else {\n        // parent\n        if (waitpid(pid, NULL, 0) != pid) {\n            perror(\"wait\");\n            abort();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/manual/get-busybox.sh",
    "content": "#!/bin/bash\ncd $MESON_SOURCE_ROOT/$MESON_SUBDIR\nfile=busybox-1.26.2\ncurl -LO http://busybox.net/downloads/$file.tar.bz2\ntar -xf $file.tar.bz2\ncd $file\nexport CFLAGS=-m32 LDFLAGS=-m32\nmake defconfig\nmake\ncp busybox_unstripped ../busybox\n"
  },
  {
    "path": "tests/manual/getdents.c",
    "content": "#define _GNU_SOURCE\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/syscall.h>\n\nint main() {\n    int fd = open(\".\", O_RDONLY | O_DIRECTORY);\n    char buf[100];\n    int count = syscall(SYS_getdents64, fd, buf, sizeof(buf));\n    write(STDOUT_FILENO, buf, count);\n}\n"
  },
  {
    "path": "tests/manual/hello-clib.c",
    "content": "#include <stdio.h>\n\nint main() {\n    printf(\"Hello, world!\\n\");\n}\n"
  },
  {
    "path": "tests/manual/hello.c",
    "content": "// compile with cc -m32 -nostdlib\nchar hello[] = \"Hello, world!\\n\";\n\nvoid _start() {\n    long result;\n    __asm__ volatile(\"int $0x80\"\n            : \"=a\" (result)\n            : \"a\" (4), \"b\" (1), \"c\" (hello), \"d\" (sizeof(hello) - 1));\n    __asm__ volatile(\"int $0x80\"\n            : \"=a\" (result)\n            : \"a\" (1), \"b\" (0));\n}\n"
  },
  {
    "path": "tests/manual/looper.c",
    "content": "#include <unistd.h>\n#include <stdio.h>\n\n#define nop() __asm__ volatile(\"\")\n\nint main() {\n    int loops = 100000000;\n    printf(\"looping %d times\\n\", loops);\n    for (int i = 0; i < loops; i++)\n        nop();\n    printf(\"done looping\\n\");\n}\n"
  },
  {
    "path": "tests/manual/meson.build",
    "content": "project('ish-tests', 'c')\nadd_project_arguments(['-m32', '-fno-stack-protector'], language: 'c')\nadd_project_link_arguments('-m32', language: 'c')\n\n# hello world\nexecutable('hello', ['hello.c'], link_args: ['-static', '-nostdlib'])\nexecutable('hello-libc-static', ['hello-clib.c'], link_args: ['-static'])\nexecutable('hello-libc', ['hello-clib.c'])\n\n# simple benchmark\nexecutable('looper', ['looper.c'])\nexecutable('fibbonaci', ['fibbonaci.c'])\n\n# filesystem\nexecutable('cat', ['cat.c'])\nexecutable('stat', ['stat.c'], c_args: ['-D_FILE_OFFSET_BITS=64'])\nexecutable('getdents', ['getdents.c'])\n\nexecutable('signal', ['signal.c'], link_args: ['-static'])\nexecutable('forkexec', ['forkexec.c'])\n\nexecutable('thread', ['thread.c'], dependencies: dependency('threads'))\n\n# various tests for code that modifies itself\nexecutable('modify', ['modify.c'], link_args: ['-zexecstack'])\n\n# qemu test program\nexecutable('qemu-test', ['qemu-test.c'], link_args: ['-lm'])\n\nrun_target('busybox',\n    command: ['get-busybox.sh'])\n"
  },
  {
    "path": "tests/manual/modify.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <setjmp.h>\n#include <signal.h>\n\nstatic char code[] = {\n    0xb8, 0x01, 0x00, 0x00, 0x00, // movl $1, %eax\n    0xc3, // ret\n};\n\nstatic sigjmp_buf env;\nstatic int catching = 0;\nstatic void handle_segfault(int sig) {\n    if (catching) {\n        catching = 0;\n        siglongjmp(env, 1);\n    }\n}\n\nstatic void test(char *code, const char *name) {\n    printf(\"%-6s before: %d expected 1\\n\", name, ((int (*)()) code)());\n    code[1] = 2;\n    printf(\"%-6s after:  %d expected 2\\n\", name, ((int (*)()) code)());\n}\n\nint main() {\n    signal(SIGSEGV, handle_segfault);\n    void *code_copy = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);\n    memcpy(code_copy, code, sizeof(code));\n\n    test(code, \"static\");\n    test(code_copy, \"mmap\");\n    munmap(code_copy, 0x1000);\n    printf(\"call nonexistent: \");\n    catching = 1;\n    if (!sigsetjmp(env, 1))\n        printf(\"%d expected segfault\\n\", ((int (*)()) code_copy)());\n    else\n        printf(\"segfault\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "tests/manual/signal.c",
    "content": "#include <signal.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid handler(int signal, siginfo_t *info, void *ucontext) {\n    printf(\"caught signal %d code %d at %p\\n\", signal, info->si_code, info->si_addr);\n}\n\nint main() {\n    struct sigaction act;\n    act.sa_sigaction = handler;\n    act.sa_flags = SA_SIGINFO;\n    sigaction(SIGILL, &act, NULL);\n    __asm__(\"ud2\");\n    printf(\"back in main\\n\");\n}\n"
  },
  {
    "path": "tests/manual/stat.c",
    "content": "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <unistd.h>\n#include <sys/stat.h>\n\nint main() {\n    struct stat64 statbuf;\n    if (fstat64(1, &statbuf) < 0) {\n        perror(\"fstat\"); return 1;\n    }\n    /* write(1, &statbuf, sizeof(statbuf)); */\n    char buf[1000];\n    int len;\n#define PTIF(n) \\\n    len = sprintf(buf, #n \" %llx\\n\", (long long) statbuf.st_##n); \\\n    write(1, buf, len);\n    PTIF(atime);\n    PTIF(mtime);\n    PTIF(ctime);\n    /* PTIF(dev) */\n    /* PTIF(ino); */\n    /* PTIF(mode); */\n    /* PTIF(nlink); */\n    /* PTIF(uid); */\n    /* PTIF(gid); */\n    /* PTIF(rdev); */\n    /* PTIF(size); */\n    /* PTIF(blksize); */\n    /* PTIF(blocks); */\n    return 0;\n}\n"
  },
  {
    "path": "tests/manual/thread.c",
    "content": "#include <stdio.h>\n#include <pthread.h>\n\nvoid *thread(void *data) {\n    printf(\"thread\\n\");\n    return data;\n}\n\nint main() {\n    pthread_t t;\n    printf(\"main before create\\n\");\n    pthread_create(&t, NULL, thread, NULL);\n    pthread_detach(t);\n    printf(\"main after create\\n\");\n}\n"
  },
  {
    "path": "tools/fakefs.c",
    "content": "#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <archive.h>\n#include <archive_entry.h>\n\n#define ISH_INTERNAL\n#include \"fs/fake-db.h\"\n#include \"fs/sqlutil.h\"\n#include \"tools/fakefs.h\"\n#include \"util/fchdir.h\"\n#include \"misc.h\"\n\n#ifndef MAX_PATH\n#define MAX_PATH 4096\n#endif\n\n// I have a weird way of error handling\n#define FILL_ERR(_type, _code, _message) do { \\\n    err_out->line = __LINE__; \\\n    err_out->type = _type; \\\n    err_out->code = _code; \\\n    err_out->message = strdup(_message); \\\n    return false; \\\n} while (0)\n#define ARCHIVE_ERR(archive) FILL_ERR(ERR_ARCHIVE, archive_errno(archive), archive_error_string(archive))\n#define POSIX_ERR() FILL_ERR(ERR_POSIX, errno, strerror(errno))\n#undef HANDLE_ERR // for sqlite\n#define HANDLE_ERR(db) FILL_ERR(ERR_SQLITE, sqlite3_extended_errcode(db), sqlite3_errmsg(db))\n#define CANCEL() FILL_ERR(ERR_CANCELLED, 0, \"\");\n\nstatic bool progress_update(struct progress *p, double progress, const char *message) {\n    bool cancelled = false;\n    if (p && p->callback)\n        p->callback(p->cookie, progress, message, &cancelled);\n    return !cancelled;\n}\n\n// This isn't linked with ish which is why there's so much copy/pasted code\n\n// I hate this code\nstatic bool path_normalize(const char *path, char *out) {\n#define ends_path(c) (c == '\\0' || c == '/')\n    // normalized format:\n    // ( '/' path-component ) *\n    while (path[0] != '\\0') {\n        while (path[0] == '/')\n            path++;\n        if (path[0] == '\\0')\n            break; // if the path ends with a slash\n        // path points to the start of a path component\n        if (path[0] == '.' && path[1] == '.' && ends_path(path[2]))\n            return false; // no dotdot allowed!\n        if (path[0] == '.' && ends_path(path[1])) {\n            path++;\n        } else {\n            *out++ = '/';\n            while (path[0] != '/' && path[0] != '\\0')\n                *out++ = *path++;\n        }\n    }\n    *out = '\\0';\n    return true;\n}\n\nstatic const char *schema = Q(\n    create table meta (id integer unique default 0, db_inode integer);\n    insert into meta (db_inode) values (0);\n    create table stats (inode integer primary key, stat blob);\n    create table paths (path blob primary key, inode integer references stats(inode));\n    create index inode_to_path on paths (inode, path);\n    // no index is needed on stats, because the rows are ordered by the primary key\n    pragma user_version=3;\n);\n\nbool fakefs_import(const char *archive_path, const char *fs, struct fakefsify_error *err_out, struct progress p) {\n    int err = mkdir(fs, 0777);\n    if (err < 0)\n        POSIX_ERR();\n\n    // make the data root dir\n    char path_tmp[PATH_MAX];\n    snprintf(path_tmp, sizeof(path_tmp), \"%s/data\", fs);\n    err = mkdir(path_tmp, 0777);\n    int root_fd = open(path_tmp, O_RDONLY);\n    if (root_fd < 0)\n        POSIX_ERR();\n\n    // open the database\n    snprintf(path_tmp, sizeof(path_tmp), \"%s/meta.db\", fs);\n    sqlite3 *db;\n    err = sqlite3_open_v2(path_tmp, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);\n    CHECK_ERR();\n    EXEC(\"pragma journal_mode=wal\")\n    EXEC(\"begin\");\n    EXEC(schema);\n\n    // open the archive\n    struct archive *archive = archive_read_new();\n    if (archive == NULL)\n        ARCHIVE_ERR(archive);\n    archive_read_support_filter_gzip(archive);\n    archive_read_support_format_tar(archive);\n    if (archive_read_open_filename(archive, archive_path, 65536) != ARCHIVE_OK)\n        ARCHIVE_ERR(archive);\n\n    struct stat real_stat;\n    if (stat(archive_path, &real_stat) < 0)\n        POSIX_ERR();\n    size_t archive_bytes = real_stat.st_size;\n\n    sqlite3_stmt *insert_stat = PREPARE(\"insert into stats (stat) values (?)\");\n    sqlite3_stmt *insert_path = PREPARE(\"insert or replace into paths values (?, ?)\");\n    sqlite3_stmt *insert_hardlink = PREPARE(\"insert or replace into paths values (?, (select inode from paths where path = ? limit 1))\");\n\n    bool archive_has_root = false;\n\n    // do actual shit\n    struct archive_entry *entry;\n    while ((err = archive_read_next_header(archive, &entry)) == ARCHIVE_OK) {\n        char entry_path[MAX_PATH];\n        if (!path_normalize(archive_entry_pathname(entry), entry_path)) {\n            // Avoid pwnage\n            fprintf(stderr, \"warning: skipped possible path traversal %s\\n\", archive_entry_pathname(entry));\n            continue;\n        }\n        if (!progress_update(&p, (double) archive_filter_bytes(archive, -1) / archive_bytes, entry_path))\n            CANCEL();\n        if (strcmp(entry_path, \"\") == 0)\n            archive_has_root = true;\n\n        const char *hardlink = archive_entry_hardlink(entry);\n        if (hardlink) {\n            char hardlink_path[MAX_PATH];\n            if (!path_normalize(hardlink, hardlink_path)) {\n                fprintf(stderr, \"warning: almost pwned by hardlink %s\\n\", hardlink);\n                continue;\n            }\n            if (linkat(root_fd, fix_path(hardlink_path), root_fd, fix_path(entry_path), 0) < 0)\n                POSIX_ERR();\n            sqlite3_bind_blob64(insert_hardlink, 1, entry_path, strlen(entry_path), SQLITE_TRANSIENT);\n            sqlite3_bind_blob64(insert_hardlink, 2, hardlink_path, strlen(hardlink_path), SQLITE_TRANSIENT);\n            STEP_RESET(insert_hardlink);\n            continue;\n        }\n\n        // mkdir -p\n        char *entry_path_copy = strdup(entry_path);\n        char *slash = entry_path_copy;\n        while ((slash = strchr(*slash ? slash + 1 : slash, '/')) != NULL) {\n            *slash = '\\0';\n            int err = mkdirat(root_fd, fix_path(entry_path_copy), 0777);\n            *slash = '/';\n            if (err < 0) {\n                if (errno == EEXIST) continue;\n                POSIX_ERR();\n            }\n        }\n        free(entry_path_copy);\n\n        int fd = -1;\n        // first, create node\n        switch (archive_entry_filetype(entry)) {\n            case AE_IFREG:\n            case AE_IFLNK:\n            case AE_IFBLK:\n            case AE_IFCHR:\n            case AE_IFSOCK:\n                fd = openat(root_fd, fix_path(entry_path), O_WRONLY | O_CREAT | O_TRUNC, 0666);\n                if (fd < 0) {\n                    if (errno == EISDIR) continue; // assuming it's case insensitivity\n                    POSIX_ERR();\n                }\n                break;\n\n            case AE_IFDIR:\n                err = mkdirat(root_fd, fix_path(entry_path), 0777);\n                if (err < 0 && errno != EEXIST)\n                    POSIX_ERR();\n                break;\n\n            case AE_IFIFO:\n                lock_fchdir(root_fd);\n                err = mkfifo(fix_path(entry_path), 0666);\n                unlock_fchdir();\n                break;\n        }\n        // second, fill in contents, if needed\n        switch (archive_entry_filetype(entry)) {\n            case AE_IFREG:\n                if (archive_read_data_into_fd(archive, fd) != ARCHIVE_OK)\n                    ARCHIVE_ERR(archive);\n                break;\n\n            case AE_IFLNK:\n                err = (int) write(fd, archive_entry_symlink(entry), strlen(archive_entry_symlink(entry)));\n                if (err < 0)\n                    POSIX_ERR();\n                break;\n\n            case AE_IFDIR:\n            case AE_IFBLK:\n            case AE_IFCHR:\n            case AE_IFSOCK:\n            case AE_IFIFO:\n                break;\n        }\n        if (fd != -1)\n            close(fd);\n\n        struct timespec times[2] = {\n            // for utimes, atime is first, mtime is second\n            {.tv_sec = archive_entry_atime(entry), .tv_nsec = archive_entry_atime_nsec(entry)},\n            {.tv_sec = archive_entry_mtime(entry), .tv_nsec = archive_entry_mtime_nsec(entry)},\n            // utimes cannot set ctime\n        };\n        if (!archive_entry_atime_is_set(entry))\n            times[0].tv_nsec = UTIME_OMIT;\n        if (!archive_entry_mtime_is_set(entry))\n            times[1].tv_nsec = UTIME_OMIT;\n        err = utimensat(root_fd, fix_path(entry_path), times, 0);\n        if (err < 0)\n            POSIX_ERR();\n\n        struct ish_stat stat = {\n            .mode = (uint32_t) archive_entry_mode(entry),\n            .uid = (uint32_t) archive_entry_uid(entry),\n            .gid = (uint32_t) archive_entry_gid(entry),\n            .rdev = (uint32_t) archive_entry_rdev(entry),\n        };\n        sqlite3_bind_blob64(insert_stat, 1, &stat, sizeof(stat), SQLITE_TRANSIENT);\n        STEP_RESET(insert_stat);\n        sqlite3_bind_blob64(insert_path, 1, entry_path, strlen(entry_path), SQLITE_TRANSIENT);\n        sqlite3_bind_int64(insert_path, 2, sqlite3_last_insert_rowid(db));\n        STEP_RESET(insert_path);\n    }\n    if (err != ARCHIVE_EOF)\n        ARCHIVE_ERR(archive);\n\n    // Add a path entry for the root if it's missing\n    if (!archive_has_root) {\n        struct ish_stat stat = {.mode = 0755};\n        sqlite3_bind_blob64(insert_stat, 1, &stat, sizeof(stat), SQLITE_TRANSIENT);\n        STEP_RESET(insert_stat);\n        sqlite3_bind_blob64(insert_path, 1, \"\", 0, SQLITE_TRANSIENT);\n        sqlite3_bind_int64(insert_path, 2, sqlite3_last_insert_rowid(db));\n        STEP_RESET(insert_path);\n    }\n\n    FINALIZE(insert_stat);\n    FINALIZE(insert_path);\n    FINALIZE(insert_hardlink);\n    EXEC(\"commit\");\n    sqlite3_close(db);\n    close(root_fd);\n\n    if (archive_read_free(archive) != ARCHIVE_OK)\n        ARCHIVE_ERR(archive);\n    return true;\n}\n\nbool fakefs_export(const char *fs, const char *archive_path, struct fakefsify_error *err_out, struct progress p) {\n    // open the archive\n    struct archive *archive = archive_write_new();\n    if (archive == NULL)\n        ARCHIVE_ERR(archive);\n    archive_write_add_filter_gzip(archive);\n    archive_write_set_format_pax(archive);\n    if (archive_write_open_filename(archive, archive_path) != ARCHIVE_OK)\n        ARCHIVE_ERR(archive);\n\n    // open the data root dir\n    char path_tmp[PATH_MAX];\n    snprintf(path_tmp, sizeof(path_tmp), \"%s/data\", fs);\n    int root_fd = open(path_tmp, O_RDONLY);\n    if (root_fd < 0)\n        POSIX_ERR();\n\n    // open the database\n    snprintf(path_tmp, sizeof(path_tmp), \"%s/meta.db\", fs);\n    sqlite3 *db;\n    int err = sqlite3_open_v2(path_tmp, &db, SQLITE_OPEN_READONLY, NULL);\n    CHECK_ERR();\n    EXEC(\"begin\");\n\n    sqlite3_stmt *count_stmt = PREPARE(\"select count(path) from paths\");\n    STEP(count_stmt);\n    int64_t paths_total = sqlite3_column_int64(count_stmt, 0);\n    FINALIZE(count_stmt);\n    int64_t paths_done = 0;\n\n    struct archive_entry_linkresolver *linkresolver = archive_entry_linkresolver_new();\n    archive_entry_linkresolver_set_strategy(linkresolver, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE);\n\n    sqlite3_stmt *query = PREPARE(\"select path, inode, stat from paths, stats using (inode)\");\n    while (STEP(query)) {\n        struct archive_entry *entry = archive_entry_new();\n\n        const char *path_in_db = sqlite3_column_blob(query, 0);\n        size_t path_len = sqlite3_column_bytes(query, 0);\n        char *path = malloc(path_len + 2);\n        path[0] = '.';\n        memcpy(path + 1, path_in_db, path_len);\n        path[path_len + 1] = '\\0';\n        archive_entry_set_pathname(entry, path);\n\n        if (!progress_update(&p, (double) paths_done / paths_total, path))\n            CANCEL();\n\n        archive_entry_set_ino64(entry, sqlite3_column_int64(query, 1));\n        struct ish_stat stat = *(struct ish_stat *) sqlite3_column_blob(query, 2);\n        archive_entry_set_mode(entry, stat.mode);\n        archive_entry_set_uid(entry, stat.uid);\n        archive_entry_set_gid(entry, stat.gid);\n        archive_entry_set_rdev(entry, stat.rdev);\n\n        struct stat real_stat;\n        if (fstatat(root_fd, path, &real_stat, 0) < 0) {\n            if (errno == ENOENT) {\n                printf(\"skipping %s\\n\", path);\n                goto skip;\n            }\n            POSIX_ERR();\n        }\n        archive_entry_set_size(entry, real_stat.st_size);\n#if __APPLE__\n#define TIMESPEC(x) st_##x##timespec\n#elif __linux__\n#define TIMESPEC(x) st_##x##tim\n#endif\n        archive_entry_set_atime(entry, real_stat.st_atime, real_stat.TIMESPEC(a).tv_nsec);\n        archive_entry_set_mtime(entry, real_stat.st_mtime, real_stat.TIMESPEC(m).tv_nsec);\n        archive_entry_set_ctime(entry, real_stat.st_ctime, real_stat.TIMESPEC(c).tv_nsec);\n\n        int fd = -1;\n        S_IFMT;\n        if (S_ISREG(stat.mode) || S_ISLNK(stat.mode))\n            fd = openat(root_fd, path, O_RDONLY);\n        if (S_ISLNK(stat.mode)) {\n            char buf[MAX_PATH+1];\n            ssize_t len = read(fd, buf, sizeof(buf)-1);\n            if (len < 0)\n                POSIX_ERR();\n            buf[len] = '\\0';\n            archive_entry_set_symlink(entry, buf);\n        }\n\n        struct archive_entry *sparse;\n        archive_entry_linkify(linkresolver, &entry, &sparse);\n        if (entry != NULL)\n            archive_write_header(archive, entry);\n        if (sparse != NULL)\n            archive_write_header(archive, sparse);\n\n        if (S_ISREG(stat.mode) && archive_entry_size(entry) != 0) {\n            char buf[8192];\n            ssize_t len;\n            while ((len = read(fd, buf, sizeof(buf))) > 0) {\n                ssize_t written = archive_write_data(archive, buf, len);\n                if (written < 0)\n                    ARCHIVE_ERR(archive);\n                if (written != len)\n                    printf(\"uh oh\\n\");\n            }\n            if (len < 0)\n                POSIX_ERR();\n        }\n        if (fd != -1)\n            close(fd);\n\n    skip:\n        paths_done++;\n        free(path);\n        archive_entry_free(entry);\n    }\n\n    FINALIZE(query);\n    archive_entry_linkresolver_free(linkresolver);\n    sqlite3_close(db);\n    close(root_fd);\n    if (archive_write_close(archive) != ARCHIVE_OK)\n        ARCHIVE_ERR(archive);\n    archive_write_free(archive);\n    return true;\n}\n"
  },
  {
    "path": "tools/fakefs.h",
    "content": "#ifndef FS_FAKEFSIFY_H\n#define FS_FAKEFSIFY_H\n#include <stdbool.h>\n\nstruct fakefsify_error {\n    int line;\n    enum {\n        ERR_ARCHIVE,\n        ERR_SQLITE,\n        ERR_POSIX,\n        ERR_CANCELLED,\n    } type;\n    int code;\n    char *message;\n};\n\nstruct progress {\n    void *cookie;\n    void (*callback)(void *cookie, double progress, const char *message, bool *cancel_out);\n};\n\nbool fakefs_import(const char *archive_path, const char *fs, struct fakefsify_error *err_out, struct progress progress);\nbool fakefs_export(const char *fs, const char *archive_path, struct fakefsify_error *err_out, struct progress progress);\n\n#endif\n"
  },
  {
    "path": "tools/fakefsify.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <libgen.h>\n\n#define ISH_INTERNAL\n#include \"fs/fake.h\"\n#include \"tools/fakefs.h\"\n\nenum cmd {\n    cmd_import,\n    cmd_export,\n};\n\nint main(int argc, const char *argv[]) {\n    enum cmd cmd = cmd_import;\n    if (strcmp(basename((char *) argv[0]), \"unfakefsify\") == 0) {\n        cmd = cmd_export;\n    }\n\n    if (argc != 3) {\n        fprintf(stderr, \"wrong number of arguments\\n\");\n        switch (cmd) {\n            case cmd_import:\n                fprintf(stderr, \"usage: %s <rootfs.tar.gz> <fakefs>\\n\", argv[0]);\n                break;\n            case cmd_export:\n                fprintf(stderr, \"usage: %s <fakefs> <rootfs.tar.gz>\\n\", argv[0]);\n                break;\n        }\n        return 1;\n    }\n\n    struct fakefsify_error err;\n    bool (*func)(const char *, const char *, struct fakefsify_error *, struct progress) = NULL;\n    if (cmd == cmd_import)\n        func = fakefs_import;\n    else if (cmd == cmd_export)\n        func = fakefs_export;\n    if (!(*func)(argv[1], argv[2], &err, (struct progress) {})) {\n        fprintf(stderr, \"error!!1! %d %d %s\\n\", err.line, err.type, err.message);\n        return 1;\n    }\n}\n"
  },
  {
    "path": "tools/meson.build",
    "content": "if get_option('kernel') == 'ish'\n    # these tools are specific to x86_64 linux\n    if host_machine.system() == 'linux' and host_machine.cpu() == 'x86_64'\n        transplant_src = [\n            'vdso-transplant.c',\n            'ptutil.c',\n        ]\n        executable('ptraceomatic', ['ptraceomatic.c', 'undefined-flags.c', transplant_src], dependencies: [ish])\n        configure_file(input: 'ptraceomatic-gdb.gdb', output: '@PLAINNAME@', copy: true)\n\n        # tools for messing with vdsos\n        # executable('vdso-dump', ['vdso-dump.c'], c_args: ['-m32'], link_args: ['-m32'])\n        executable('vdso-transplant', ['vdso-transplant-main.c', transplant_src],\n            include_directories: includes)\n    endif\n\n    unicorn = cc.find_library('unicorn', required: false)\n    if unicorn.found()\n        executable('unicornomatic', ['unicornomatic.c', 'undefined-flags.c'], dependencies: [ish, unicorn])\n        configure_file(input: 'ptraceomatic-gdb.gdb', output: 'unicornomatic-gdb.gdb', copy: true)\n    endif\nendif\n\nlibarchive = dependency('libarchive', required: false)\nif not libarchive.found()\n    # homebrew\n    libarchive_lib = cc.find_library('archive', dirs: ['/usr/local/opt/libarchive/lib'], required: false)\n    if libarchive_lib.found()\n        libarchive = declare_dependency(dependencies: [libarchive_lib], include_directories: include_directories(['/usr/local/opt/libarchive/include']))\n    else\n        # Apple Silicon Homebrew\n        libarchive_lib = cc.find_library('archive', dirs: ['/opt/homebrew/opt/libarchive/lib'], required: false)\n        if libarchive_lib.found()\n            libarchive = declare_dependency(dependencies: [libarchive_lib], include_directories: include_directories(['/opt/homebrew/opt/libarchive/include']))\n        endif\n    endif\nendif\n\nif libarchive.found()\n    fakefsify_src = [\n        'fakefsify.c',\n        'fakefs.c',\n        '../util/fchdir.c',\n    ]\n    fakefsify = executable('fakefsify', fakefsify_src, dependencies: [libarchive], link_with: [libfakefs], include_directories: [includes])\n    custom_target('unfakefsify',\n        build_by_default: true,\n        command: ['ln', '-sf', 'fakefsify', '@OUTPUT@'],\n        output: 'unfakefsify')\nendif\n"
  },
  {
    "path": "tools/ptraceomatic-config.h",
    "content": "#define VVAR_PAGES 4\n#define VDSO_PAGES 2\n"
  },
  {
    "path": "tools/ptraceomatic-gdb.gdb",
    "content": "source ish-gdb.gdb\n\ndefine diff-mem\n    dump binary memory real.bin real_page real_page+4096\n    dump binary memory fake.bin fake_page fake_page+4096\n    shell xxd real.bin > real.xxd\n    shell xxd fake.bin > fake.xxd\n    shell nvim -d real.xxd fake.xxd\nend\n"
  },
  {
    "path": "tools/ptraceomatic.c",
    "content": "// Fun little utility that single-steps a program using ptrace and\n// simultaneously runs the program in ish, and asserts that everything's\n// working the same.\n// Many apologies for the messy code.\n#include <stdio.h>\n#include <string.h>\n#include <signal.h>\n#include <unistd.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/wait.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#undef PAGE_SIZE // defined in sys/user.h, but we want the version from kernel/memory.h\n#include <sys/personality.h>\n#include <sys/socket.h>\n\n#include \"debug.h\"\n#include \"kernel/calls.h\"\n#include \"fs/path.h\"\n#include \"fs/fd.h\"\n#include \"emu/interrupt.h\"\n#include \"emu/cpuid.h\"\n\n#include \"kernel/elf.h\"\n#include \"tools/transplant.h\"\n#include \"tools/ptutil.h\"\n#include \"undefined-flags.h\"\n#include \"kernel/vdso.h\"\n\n#include \"xX_main_Xx.h\"\n\n// ptrace utility functions\n\n// returns 1 for a signal stop\nstatic inline int step(int pid) {\n    trycall(ptrace(PTRACE_SINGLESTEP, pid, NULL, 0), \"ptrace step\");\n    int status;\n    trycall(waitpid(pid, &status, 0), \"wait step\");\n    if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGTRAP) {\n        int signal = WSTOPSIG(status);\n        printk(\"child received signal %d\\n\", signal);\n        // a signal arrived, we now have to actually deliver it\n        trycall(ptrace(PTRACE_SINGLESTEP, pid, NULL, signal), \"ptrace step\");\n        trycall(waitpid(pid, &status, 0), \"wait step\");\n        return 1;\n    }\n    return 0;\n}\n\nstatic inline void getregs(int pid, struct user_regs_struct *regs) {\n    trycall(ptrace(PTRACE_GETREGS, pid, NULL, regs), \"ptrace getregs\");\n}\n\nstatic inline void setregs(int pid, struct user_regs_struct *regs) {\n    trycall(ptrace(PTRACE_SETREGS, pid, NULL, regs), \"ptrace setregs\");\n}\n\nstatic int compare_cpus(struct cpu_state *cpu, struct tlb *tlb, int pid, int undefined_flags) {\n    struct user_regs_struct regs;\n    struct user_fpregs_struct fpregs;\n    getregs(pid, &regs);\n    trycall(ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs), \"ptrace getregs compare\");\n    collapse_flags(cpu);\n#define CHECK(real, fake, fmt, ...) do { \\\n    if ((real) != (fake)) { \\\n        printk(fmt \": real 0x%llx, fake 0x%llx\\n\", ##__VA_ARGS__, (unsigned long long) (real), (unsigned long long) (fake)); \\\n        debugger; \\\n        return -1; \\\n    } \\\n} while (0)\n#define CHECK_REG(pt, cp) CHECK(regs.pt, cpu->cp, #cp)\n    CHECK_REG(rax, eax);\n    CHECK_REG(rbx, ebx);\n    CHECK_REG(rcx, ecx);\n    CHECK_REG(rdx, edx);\n    CHECK_REG(rsi, esi);\n    CHECK_REG(rdi, edi);\n    CHECK_REG(rsp, esp);\n    CHECK_REG(rbp, ebp);\n    CHECK_REG(rip, eip);\n    undefined_flags |= (1 << 8); // treat trap flag as undefined\n    regs.eflags = (regs.eflags & ~undefined_flags) | (cpu->eflags & undefined_flags);\n    // give a nice visual representation of the flags\n    if (regs.eflags != cpu->eflags) {\n#define f(x,n) ((regs.eflags & (1 << n)) ? #x : \"-\"),\n        printf(\"real eflags = 0x%llx %s%s%s%s%s%s%s%s%s, fake eflags = 0x%x %s%s%s%s%s%s%s%s%s\\r\\n%0d\",\n                regs.eflags, f(o,11)f(d,10)f(i,9)f(t,8)f(s,7)f(z,6)f(a,4)f(p,2)f(c,0)\n#undef f\n#define f(x,n) ((cpu->eflags & (1 << n)) ? #x : \"-\"),\n                cpu->eflags, f(o,11)f(d,10)f(i,9)f(t,8)f(s,7)f(z,6)f(a,4)f(p,2)f(c,0)0);\n        debugger;\n        return -1;\n    }\n\n    for (int i = 0; i < 8; i++) {\n        CHECK(*(uint64_t *) &fpregs.xmm_space[i * 4], cpu->xmm[i].qw[0], \"xmm%d low\", i);\n        CHECK(*(uint64_t *) &fpregs.xmm_space[i*4+2], cpu->xmm[i].qw[1], \"xmm%d high\", i);\n    }\n\n#define FSW_MASK 0x7d00 // only look at top, c0, c2, c3\n    CHECK(fpregs.swd & FSW_MASK, cpu->fsw & FSW_MASK, \"fsw\");\n    CHECK(fpregs.cwd, cpu->fcw, \"fcw\");\n    fpregs.swd &= FSW_MASK;\n    for (int i = 0; i < 8; i++) {\n        int ii = (cpu->top + i) % 8;\n        uint64_t mm = cpu->mm[ii].qw;\n        uint64_t f_signif =  cpu->fp[ii].signif;\n        uint64_t expected = *(uint64_t *) &fpregs.st_space[i * 4];\n        if (f_signif != expected && mm != expected) {\n            printk(\"mm/st(%d) signif: real %#llx, fake fp %#llx, fake mm %#llx\\n\", i, (unsigned long long) expected, (unsigned long long) f_signif, (unsigned long long) mm);\n            debugger;\n            return -1;\n        }\n        if (f_signif == expected && mm != expected) {\n            CHECK(*(uint16_t *) &fpregs.st_space[i*4+2], cpu->fp[ii].signExp, \"st(%d) sign/exp\", i);\n        }\n    }\n\n    // compare pages marked dirty\n    if (tlb->dirty_page != TLB_PAGE_EMPTY) {\n        int fd = open_mem(pid);\n        page_t dirty_page = tlb->dirty_page;\n        char real_page[PAGE_SIZE];\n        trycall(lseek(fd, dirty_page, SEEK_SET), \"compare seek mem\");\n        trycall(read(fd, real_page, PAGE_SIZE), \"compare read mem\");\n        close(fd);\n        struct pt_entry entry = *mem_pt(current->mem, PAGE(dirty_page));\n        void *fake_page = entry.data->data + entry.offset;\n\n        if (memcmp(real_page, fake_page, PAGE_SIZE) != 0) {\n            printk(\"page %x doesn't match\\n\", dirty_page);\n            debugger;\n            return -1;\n        }\n        tlb->dirty_page = TLB_PAGE_EMPTY;\n    }\n\n    setregs(pid, &regs);\n    trycall(ptrace(PTRACE_SETFPREGS, pid, NULL, &fpregs), \"ptrace setregs compare\");\n    return 0;\n}\n\n// I'd like to apologize in advance for this code\nstatic int transmit_fd(int pid, int sender, int receiver, int fake_fd) {\n    // this sends the fd over a unix domain socket. yes, I'm crazy\n\n    // sending part\n    int real_fd = f_get(fake_fd)->real_fd;\n    struct msghdr msg = {};\n    char cmsg[CMSG_SPACE(sizeof(int))];\n    memset(cmsg, 0, sizeof(cmsg));\n\n    msg.msg_control = cmsg;\n    msg.msg_controllen = sizeof(cmsg);\n\n    struct cmsghdr *cmsg_hdr = CMSG_FIRSTHDR(&msg);\n    cmsg_hdr->cmsg_level = SOL_SOCKET;\n    cmsg_hdr->cmsg_type = SCM_RIGHTS;\n    cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(int));\n    *(int *) CMSG_DATA(cmsg_hdr) = real_fd;\n\n    trycall(sendmsg(sender, &msg, 0), \"sendmsg insanity\");\n\n    // receiving part\n    // painful, because we're 64-bit and the child is 32-bit and I want to kill myself\n    struct user_regs_struct saved_regs;\n    getregs(pid, &saved_regs);\n    struct user_regs_struct regs = saved_regs;\n\n    // reserve space for 32-bit version of cmsg\n    regs.rsp -= 16; // according to my calculations\n    addr_t cmsg_addr = regs.rsp;\n    char cmsg_bak[16];\n    pt_readn(pid, regs.rsp, cmsg_bak, sizeof(cmsg_bak));\n\n    // copy 32-bit msghdr\n    regs.rsp -= 32;\n    int msg32[] = {0, 0, 0, 0, cmsg_addr, 20, 0};\n    addr_t msg_addr = regs.rsp;\n    char msg_bak[32];\n    pt_readn(pid, regs.rsp, msg_bak, sizeof(msg_bak));\n    pt_writen(pid, regs.rsp, &msg32, sizeof(msg32));\n\n    regs.rax = 372;\n    regs.rbx = receiver;\n    regs.rcx = msg_addr;\n    regs.rdx = 0;\n    // assume we're already on an int $0x80\n    setregs(pid, &regs);\n    step(pid);\n    getregs(pid, &regs);\n\n    int sent_fd;\n    if ((long) regs.rax >= 0)\n        pt_readn(pid, cmsg_addr + 12, &sent_fd, sizeof(sent_fd));\n    else\n        sent_fd = regs.rax;\n\n    // restore crap\n    pt_writen(pid, cmsg_addr, cmsg_bak, sizeof(cmsg_bak));\n    pt_writen(pid, msg_addr, msg_bak, sizeof(msg_bak));\n    setregs(pid, &regs);\n\n    if (sent_fd < 0) {\n        errno = -sent_fd;\n        perror(\"remote recvmsg insanity\");\n        exit(1);\n    }\n\n    return sent_fd;\n}\n\nstatic void remote_close_fd(int pid, int fd, long int80_ip) {\n    // lettuce spray\n    struct user_regs_struct saved_regs;\n    getregs(pid, &saved_regs);\n    struct user_regs_struct regs = saved_regs;\n    regs.rip = int80_ip;\n    regs.rax = 6;\n    regs.rbx = fd;\n    setregs(pid, &regs);\n    step(pid);\n    getregs(pid, &regs);\n    if ((long) regs.rax < 0) {\n        errno = -regs.rax;\n        perror(\"remote close fd\");\n        exit(1);\n    }\n    setregs(pid, &regs);\n}\n\n#define _ignore(x) {}; int UNUSED(x) =\n#define ignore _ignore(__COUNTER__)\n\nstatic void pt_copy(int pid, addr_t start, size_t size) {\n    if (start == 0)\n        return;\n    byte_t byte;\n    for (addr_t addr = start; addr < start + size; addr++) {\n        ignore user_get(addr, byte);\n        pt_write8(pid, addr, byte);\n    }\n}\n\n// Please don't use unless absolutely necessary.\nstatic void pt_copy_to_real(int pid, addr_t start, size_t size) {\n    byte_t byte;\n    for (addr_t addr = start; addr < start + size; addr++) {\n        pt_readn(pid, addr, &byte, sizeof(byte));\n        ignore user_put(addr, byte);\n    }\n}\n\nstatic void step_tracing(struct cpu_state *cpu, struct tlb *tlb, int pid, int sender, int receiver) {\n    // step fake cpu\n    cpu->tf = 1;\n    int interrupt = cpu_run_to_interrupt(cpu, tlb);\n    // hack to clean up before the exit syscall\n    if (interrupt == INT_SYSCALL && cpu->eax == 1) {\n        if (kill(pid, SIGKILL) < 0) {\n            perror(\"kill tracee during exit\");\n            exit(1);\n        }\n    }\n    if (interrupt != INT_DEBUG)\n        handle_interrupt(interrupt);\n\n    // step real cpu\n    // intercept cpuid, rdtsc, and int $0x80, though\n    struct user_regs_struct regs;\n    errno = 0;\n    getregs(pid, &regs);\n    long inst = trycall(ptrace(PTRACE_PEEKTEXT, pid, regs.rip, NULL), \"ptrace get inst step\");\n    long saved_fd = -1; // annoying hack for mmap\n    long old_sp = regs.rsp; // so we know where a sigframe ends\n\n    if ((inst & 0xff) == 0x0f) {\n        if (((inst & 0xff00) >> 8) == 0xa2) {\n            // cpuid\n            do_cpuid((dword_t *) &regs.rax, (dword_t *) &regs.rbx, (dword_t *) &regs.rcx, (dword_t *) &regs.rdx);\n            regs.rip += 2;\n        } else if (((inst & 0xff00) >> 8) == 0x31) {\n            // rdtsc, no good way to get the same result here except copy from fake cpu\n            regs.rax = cpu->eax;\n            regs.rdx = cpu->edx;\n            regs.rip += 2;\n        } else {\n            goto do_step;\n        }\n    } else if ((inst & 0xff) == 0xcd && ((inst & 0xff00) >> 8) == 0x80) {\n        // int $0x80, intercept the syscall unless it's one of a few actually important ones\n        dword_t syscall_num = (dword_t) regs.rax;\n        switch (syscall_num) {\n            // put syscall result from fake process into real process\n            case 3: // read\n                pt_copy(pid, regs.rcx, cpu->edx); break;\n            case 7: // waitpid\n                pt_copy(pid, regs.rcx, sizeof(dword_t)); break;\n            case 13: // time\n                if (regs.rbx != 0)\n                    pt_copy(pid, regs.rbx, sizeof(dword_t));\n                break;\n            case 43:\n                pt_copy(pid, regs.rbx, sizeof(struct tms_)); break;\n            case 54: { // ioctl (god help us)\n                struct fd *fd = f_get(cpu->ebx);\n                if (fd && fd->ops->ioctl_size) {\n                    ssize_t ioctl_size = fd->ops->ioctl_size(cpu->ecx);\n                    if (ioctl_size >= 0)\n                        pt_copy(pid, regs.rdx, ioctl_size);\n                }\n                break;\n            }\n            case 85: // readlink\n                pt_copy(pid, regs.rcx, regs.rdx); break;\n            case 102: { // socketcall\n                dword_t args[6];\n                ignore user_get(regs.rcx, args);\n                dword_t len;\n                switch (cpu->ebx) {\n                    case 6: // getsockname\n                        ;ignore user_get(args[2], len);\n                        pt_copy(pid, args[1], len);\n                        break;\n                    case 8: // socketpair\n                        pt_copy(pid, args[3], sizeof(dword_t[2]));\n                        break;\n                    case 12: // recvfrom\n                        pt_copy(pid, args[1], args[2]);\n                        ignore user_get(args[5], len);\n                        pt_copy(pid, args[4], len);\n                        break;\n                }\n                break;\n            }\n            case 104: // setitimer\n                pt_copy(pid, regs.rdx, sizeof(struct itimerval_)); break;\n            case 116: // sysinfo\n                pt_copy(pid, regs.rbx, sizeof(struct sys_info)); break;\n            case 122: // uname\n                pt_copy(pid, regs.rbx, sizeof(struct uname)); break;\n            case 140: // _llseek\n                pt_copy(pid, regs.rsi, 8); break;\n            case 145: { // readv\n                struct iovec_ vecs[regs.rdx];\n                ignore user_get(regs.rcx, vecs);\n                for (unsigned i = 0; i < regs.rdx; i++)\n                    pt_copy(pid, vecs[i].base, vecs[i].len);\n                break;\n            }\n            case 162: // nanosleep\n                pt_copy(pid, regs.rcx, sizeof(struct timespec_)); break;\n            case 168: // poll\n                pt_copy(pid, regs.rbx, sizeof(struct pollfd_) * regs.rcx); break;\n            case 183: // getcwd\n                pt_copy(pid, regs.rbx, cpu->eax); break;\n            case 186: // sigaltstack\n                if (regs.rcx != 0) pt_copy(pid, regs.rcx, sizeof(struct stack_t_));\n                break;\n            case 195: // stat64\n            case 196: // lstat64\n            case 197: // fstat64\n                pt_copy(pid, regs.rcx, sizeof(struct newstat64)); break;\n            case 220: // getdents64\n                pt_copy(pid, regs.rcx, cpu->eax); break;\n            case 242: // sched_getaffinity\n                pt_copy(pid, regs.rdx, regs.rcx); break;\n            case 265: // clock_gettime\n                pt_copy(pid, regs.rcx, sizeof(struct timespec_)); break;\n            case 300: // fstatat64\n                pt_copy(pid, regs.rdx, sizeof(struct newstat64)); break;\n            case 305: // readlinkat\n                if (cpu->eax < 0xffff000) pt_copy(pid, regs.rdx, cpu->eax);\n                break;\n            case 340: // prlimit\n                if (regs.rsi != 0) pt_copy(pid, regs.rsi, sizeof(struct rlimit_));\n                break;\n            case 355: // getrandom\n                pt_copy(pid, regs.rbx, regs.rcx); break;\n\n            case 90: // mmap\n            case 192: // mmap2\n                if (cpu->eax < 0xfffff000 && cpu->edi != (dword_t) -1) {\n                    // fake mmap didn't fail, change fd\n                    saved_fd = regs.rdi;\n                    regs.rdi = transmit_fd(pid, sender, receiver, cpu->edi);\n                }\n                goto do_step;\n\n            // some syscalls need to just happen\n            case 45: // brk\n            case 91: // munmap\n            case 119: // sigreturn\n            case 125: // mprotect\n            case 173: // rt_sigreturn\n            case 174: // rt_sigaction\n            case 175: // rt_sigprocmask\n            case 243: // set_thread_area\n                //regs.rax = cpu->eax;\n                goto do_step;\n        }\n        regs.rax = cpu->eax;\n        regs.rip += 2;\n    } else {\ndo_step:\n        setregs(pid, &regs);\n        // single step on a repeated string instruction only does one\n        // iteration, so loop until ip changes\n        unsigned long ip = regs.rip;\n        int was_signal;\n        while (regs.rip == ip) {\n            was_signal = step(pid);\n            getregs(pid, &regs);\n        }\n        if (saved_fd >= 0) {\n            remote_close_fd(pid, regs.rdi, ip);\n            regs.rdi = saved_fd;\n        }\n\n        if (was_signal) {\n            // copy the return address\n            pt_copy(pid, regs.rsp, sizeof(addr_t));\n            // and copy the rest the other way\n            pt_copy_to_real(pid, regs.rsp + sizeof(addr_t), old_sp - regs.rsp - sizeof(addr_t));\n        }\n    }\n    setregs(pid, &regs);\n}\n\nstatic void prepare_tracee(int pid) {\n    transplant_vdso(pid, vdso_data, sizeof(vdso_data));\n\n    // copy the stack\n    pt_copy(pid, 0xffffd000, 0x1000);\n    struct user_regs_struct regs;\n    getregs(pid, &regs);\n    regs.rsp = current->cpu.esp;\n    setregs(pid, &regs);\n\n    // find out how big the signal stack frame needs to be\n    __asm__(\"cpuid\"\n            : \"=b\" (xsave_extra)\n            : \"a\" (0xd), \"c\" (0)\n            : \"edx\");\n\n    int features_ecx, features_edx;\n    __asm__(\"cpuid\"\n            : \"=c\" (features_ecx), \"=d\" (features_edx)\n            : \"a\" (1)\n            : \"ebx\");\n    // if xsave is supported, add 4 bytes. why? idk\n    if (features_ecx & (1 << 26))\n        xsave_extra += 4;\n    // if fxsave/fxrestore is supported, use 112 bytes for that\n    if (features_edx & (1 << 24))\n        fxsave_extra = 112;\n\n}\n\nint main(int argc, char *const argv[]) {\n    char envp[100] = {0};\n    if (getenv(\"TERM\"))\n        strcpy(envp, getenv(\"TERM\") - strlen(\"TERM\") - 1);\n    int err = xX_main_Xx(argc, argv, envp);\n    if (err < 0) {\n        fprintf(stderr, \"%s\\n\", strerror(-err));\n        return err;\n    }\n\n    // execute the traced program in a new process and throw up some sockets\n    char exec_path[MAX_PATH];\n    if (path_normalize(AT_PWD, argv[optind], exec_path, N_SYMLINK_FOLLOW) != 0) {\n        fprintf(stderr, \"enametoolong\\n\"); exit(1);\n    }\n    struct mount *mount = find_mount_and_trim_path(exec_path);\n    int fds[2];\n    trycall(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), \"socketpair\");\n    int pid = start_tracee(mount->root_fd, fix_path(exec_path), argv + optind, (char *[]) {NULL});\n    int sender = fds[0], receiver = fds[1];\n    /* close(receiver); // only needed in the child */\n    prepare_tracee(pid);\n\n    struct cpu_state *cpu = &current->cpu;\n    cpu->tf = true;\n    struct tlb tlb;\n    tlb_refresh(&tlb, cpu->mmu);\n    int undefined_flags = 2;\n    struct cpu_state old_cpu = *cpu;\n    int i = 0;\n    while (true) {\n        while (compare_cpus(cpu, &tlb, pid, undefined_flags) < 0) {\n            printk(\"failure: resetting cpu\\n\");\n            *cpu = old_cpu;\n            __asm__(\"int3\");\n            cpu_run_to_interrupt(cpu, &tlb);\n        }\n        undefined_flags = undefined_flags_mask(cpu, &tlb);\n        old_cpu = *cpu;\n        step_tracing(cpu, &tlb, pid, sender, receiver);\n        i++;\n    }\n}\n\n// useful for calling from the debugger\nvoid dump_memory(int pid, const char *file, addr_t start, addr_t end) {\n    FILE *f = fopen(file, \"w\");\n    for (addr_t addr = start; addr <= end; addr += sizeof(dword_t)) {\n        dword_t val = pt_read(pid, addr);\n        fwrite(&val, sizeof(dword_t), 1, f);\n    }\n    fclose(f);\n}\n"
  },
  {
    "path": "tools/ptutil.c",
    "content": "#define _GNU_SOURCE\n#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <sys/personality.h>\n#include <sys/wait.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include <sched.h>\n#include <sys/prctl.h>\n#include <sys/syscall.h>\n#include <asm/prctl.h>\n#undef PAGE_SIZE // want definition from kernel/memory.h\n#include \"../misc.h\"\n\nlong trycall(long res, const char *msg) {\n    if (res == -1 && errno != 0) {\n        perror(msg); printf(\"\\r\\n\"); exit(1);\n    }\n    return res;\n}\n\nint start_tracee(int at, const char *path, char *const argv[], char *const envp[]) {\n    // shut off aslr\n    int persona = personality(0xffffffff);\n    persona |= ADDR_NO_RANDOMIZE;\n    personality(persona);\n\n    int pid = fork();\n    if (pid < 0) {\n        perror(\"fork\");\n        exit(1);\n    }\n    if (pid == 0) {\n        // child\n        // enable segfaulting on rdtsc and cpuid\n        trycall(prctl(PR_SET_TSC, PR_TSC_SIGSEGV), \"rdtsc faulting\");\n        trycall(ptrace(PTRACE_TRACEME, 0, NULL, NULL), \"ptrace traceme\");\n        // get rid of signal handlers\n        for (int sig = 0; sig < SIGRTMIN - 1; sig++)\n            signal(sig, SIG_DFL);\n        trycall(fexecve(openat(at, path, O_RDONLY), argv, envp), \"execve\");\n    } else {\n        // parent, wait for child to stop after exec\n        int status;\n        trycall(wait(&status), \"wait\");\n        if (!WIFSTOPPED(status)) {\n            fprintf(stderr, \"child failed to start\\n\");\n            exit(1);\n        }\n    }\n    return pid;\n}\n\nint open_mem(int pid) {\n    char filename[1024];\n    sprintf(filename, \"/proc/%d/mem\", pid);\n    return trycall(open(filename, O_RDWR), \"open mem\");\n}\n\nvoid pt_readn(int pid, addr_t addr, void *buf, size_t count) {\n    int fd = open_mem(pid);\n    trycall(lseek(fd, addr, SEEK_SET), \"read seek\");\n    trycall(read(fd, buf, count), \"read read\");\n    close(fd);\n}\n\nvoid pt_writen(int pid, addr_t addr, void *buf, size_t count) {\n    int fd = open_mem(pid);\n    trycall(lseek(fd, addr, SEEK_SET), \"write seek\");\n    trycall(write(fd, buf, count), \"write write\");\n    close(fd);\n}\n\ndword_t pt_read(int pid, addr_t addr) {\n    dword_t res;\n    pt_readn(pid, addr, &res, sizeof(res));\n    return res;\n}\n\nvoid pt_write(int pid, addr_t addr, dword_t val) {\n    pt_writen(pid, addr, &val, sizeof(val));\n}\n\nvoid pt_write8(int pid, addr_t addr, byte_t val) {\n    pt_writen(pid, addr, &val, sizeof(val));\n}\n"
  },
  {
    "path": "tools/ptutil.h",
    "content": "#include \"../misc.h\"\n\nlong trycall(long res, const char *msg);\nint start_tracee(int at, const char *path, char *const argv[], char *const envp[]);\nint open_mem(int pid);\ndword_t pt_read(int pid, addr_t addr);\nvoid pt_write8(int pid, addr_t addr, byte_t val);\nvoid pt_write(int pid, addr_t addr, dword_t val);\nvoid pt_readn(int pid, addr_t addr, void *buf, size_t count);\nvoid pt_writen(int pid, addr_t addr, void *buf, size_t count);\n"
  },
  {
    "path": "tools/staticdefine.h",
    "content": "// credit goes to include/linux/kbuild.h\n#define _DEFINE(sym, val) \\\n    asm volatile(\"\\n.ascii \\\"->\" sym \" %0 \" #val \"\\\"\" : : \"i\" (val))\n#define DEFINE(sym, val) \\\n    _DEFINE(#sym, val)\n\n#define BLANK() asm volatile(\"\\n.ascii \\\"->\\\"\" : : )\n\n#define OFFSET(sym, str, mem) \\\n    DEFINE(sym##_##mem, offsetof(struct str, mem))\n\n#define MACRO(macro) \\\n    _DEFINE(#macro, macro)\n\n#define COMMENT(x) \\\n    asm volatile(\"\\n.ascii \\\"->#\" x \"\\\"\")\n"
  },
  {
    "path": "tools/staticdefine.sh",
    "content": "#!/bin/bash -e\ncompile_commands=$1\ninput=$2\noutput=$3\ndep=$4\nflags=$(python3 - <<END\nimport json\nwith open('$compile_commands') as f:\n    commands = json.load(f)\nfor command in commands:\n    if command['file'].endswith('asbestos/asbestos.c'):\n        break\ncommand = command['command']\ncommand = command.split()[:-9] + ['-MD', '-MQ', '$output', '-MF', '$dep']\nprint(' '.join(command))\nEND\n)\n$flags $input -include \"$(dirname $0)/staticdefine.h\" -S -o - | \\\nsed -ne 's:^[[:space:]]*\\.ascii[[:space:]]*\"\\(.*\\)\".*:\\1:;\n         /^->/{s:->#\\(.*\\):/* \\1 */:;\n         s:^->\\([^ ]*\\) [\\$$#]*\\([^ ]*\\) \\(.*\\):#define \\1 \\2 /* \\3 */:;\n         s:->::; p;}' > $output\n# sed magic was copied from the linux kernel build system\n"
  },
  {
    "path": "tools/transplant.h",
    "content": "void transplant_vdso(int pid, const void *new_vdso, size_t new_vdso_size);\n"
  },
  {
    "path": "tools/undefined-flags.c",
    "content": "#include \"emu/modrm.h\"\n#include \"undefined-flags.h\"\n#include \"ptutil.h\"\n\n#define C (1 << 0)\n#define P (1 << 2)\n#define A (1 << 4)\n#define Z (1 << 6)\n#define S (1 << 7)\n#define O (1 << 11)\n\nint undefined_flags_mask(struct cpu_state *cpu, struct tlb *tlb) {\n    addr_t ip = cpu->eip;\n    byte_t opcode;\n#define read(x) tlb_read(tlb, ip++, &x, sizeof(x));\nskip:\n    read(opcode);\n    switch (opcode) {\n        // shift or rotate, of is undefined if shift count is greater than 1\n        case 0x0f:\n            read(opcode);\n            switch(opcode) {\n                // shrd/shld\n                case 0xa4:\n                case 0xa5:\n                case 0xac:\n                case 0xad: {\n                    ip++;\n                    byte_t shift = -1;\n                    if (opcode == 0xad)\n                        shift = cpu->cl;\n                    else\n                        read(shift);\n                    if (shift == 1)\n                        return O|A;\n                    else if (shift > 1)\n                        return O|A;\n                    break;\n                }\n                case 0xaf: return S|Z|A|P; // imul\n                case 0xbd: case 0xbc: return O|S|A|P|C; // bsr/bsf\n            }\n            break;\n\n        case 0x69:\n        case 0x6b: return S|Z|A|P; // imul\n\n        case 0xc0:\n        case 0xc1:\n        case 0xd0:\n        case 0xd1:\n        case 0xd2:\n        case 0xd3: {\n            ip++; // skip modrm\n            byte_t shift_count = -1;\n            if (opcode == 0xd0 || opcode == 0xd1)\n                shift_count = 1;\n            else if (opcode == 0xd2 || opcode == 0xd3)\n                shift_count = cpu->cl;\n            else\n                read(shift_count);\n            if (shift_count > 1)\n                return O;\n            break;\n        }\n\n        case 0xf6: case 0xf7: {\n            // group 3\n            byte_t modrm = -1;\n            read(modrm);\n            switch (REG(modrm)) {\n                case 4: return S|Z|A|P; // mul\n                case 5: return S|Z|A|P; // imul\n            }\n            break;\n        }\n        case 0x66: goto skip;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "tools/undefined-flags.h",
    "content": "#include \"emu/cpu.h\"\n#include \"emu/tlb.h\"\n\nint undefined_flags_mask(struct cpu_state *cpu, struct tlb *tlb);\n"
  },
  {
    "path": "tools/unicornomatic.c",
    "content": "// Runs a program simultaneously in ish and unicorn, single steps, and asserts\n// everything is the same. Basically the same deal as ptraceomatic, except\n// ptraceomatic doesn't run on my raspberry pi and I need to verify the damn\n// thing still works on a raspberry pi.\n// Oh and hopefully the code is somewhat less messy.\n#include <stdio.h>\n#include <errno.h>\n#include <sys/mman.h>\n\n#include <unicorn/unicorn.h>\n\n#include \"misc.h\"\n#include \"debug.h\"\n#include \"kernel/calls.h\"\n#include \"emu/interrupt.h\"\n#include \"xX_main_Xx.h\"\n\n#include \"undefined-flags.h\"\n\n// unicorn wrappers\n\nlong trycall(long res, const char *msg) {\n    if (res == -1 && errno != 0) {\n        perror(msg); printf(\"\\r\\n\"); exit(1);\n    }\n    return res;\n}\n\nvoid uc_trycall(uc_err res, const char *msg) {\n    if (res != UC_ERR_OK) {\n        printf(\"%s: %s\\r\\n\", msg, uc_strerror(res));\n        exit(1);\n    }\n}\n\nuint32_t uc_getreg(uc_engine *uc, int reg_id) {\n    uint32_t value;\n    uc_trycall(uc_reg_read(uc, reg_id, &value), \"uc_getreg\");\n    return value;\n}\n\nvoid uc_setreg(uc_engine *uc, int reg_id, uint32_t value) {\n    uc_trycall(uc_reg_write(uc, reg_id, &value), \"uc_setreg\");\n}\n\nvoid uc_read(uc_engine *uc, addr_t addr, void *buf, size_t size) {\n    uc_trycall(uc_mem_read(uc, addr, buf, size), \"uc_read\");\n}\n\nvoid uc_write(uc_engine *uc, addr_t addr, void *buf, size_t size) {\n    uc_trycall(uc_mem_write(uc, addr, buf, size), \"uc_write\");\n}\n\nstatic void uc_unmap(uc_engine *uc, addr_t start, dword_t size) {\n    for (addr_t addr = start; addr < start + size; addr += PAGE_SIZE) {\n        uc_mem_unmap(uc, addr, PAGE_SIZE); // ignore errors\n    }\n}\nstatic void uc_map(uc_engine *uc, addr_t start, dword_t size) {\n    uc_unmap(uc, start, size);\n    for (addr_t addr = start; addr < start + size; addr += PAGE_SIZE) {\n        uc_trycall(uc_mem_map(uc, addr, PAGE_SIZE, UC_PROT_ALL), \"mmap emulation\");\n    }\n}\nstatic void uc_map_ptr(uc_engine *uc, addr_t start, void *mem, dword_t size) {\n    uc_unmap(uc, start, size);\n    for (addr_t addr = start; addr < start + size; addr += PAGE_SIZE) {\n        uc_trycall(uc_mem_map_ptr(uc, addr, PAGE_SIZE, UC_PROT_ALL, mem + (addr - start)), \"mmap emulation\");\n    }\n}\n\nstruct uc_regs {\n    dword_t eax;\n    dword_t ebx;\n    dword_t ecx;\n    dword_t edx;\n    dword_t esi;\n    dword_t edi;\n    dword_t ebp;\n    dword_t esp;\n    dword_t eip;\n    dword_t eflags;\n    word_t fpcw;\n    word_t fpsw;\n    float80 fp[8];\n};\nstatic int uc_regs_ids[] = {\n    UC_X86_REG_EAX, UC_X86_REG_EBX, UC_X86_REG_ECX, UC_X86_REG_EDX,\n    UC_X86_REG_ESI, UC_X86_REG_EDI, UC_X86_REG_EBP, UC_X86_REG_ESP,\n    UC_X86_REG_EIP, UC_X86_REG_EFLAGS,\n    UC_X86_REG_FPCW, UC_X86_REG_FPSW,\n    UC_X86_REG_FP0, UC_X86_REG_FP1, UC_X86_REG_FP2, UC_X86_REG_FP3,\n    UC_X86_REG_FP4, UC_X86_REG_FP5, UC_X86_REG_FP6, UC_X86_REG_FP7,\n};\nvoid uc_getregs(uc_engine *uc, struct uc_regs *regs) {\n    void *ptrs[sizeof(uc_regs_ids)/sizeof(uc_regs_ids[0])] = {\n        &regs->eax, &regs->ebx, &regs->ecx, &regs->edx,\n        &regs->esi, &regs->edi, &regs->ebp, &regs->esp,\n        &regs->eip, &regs->eflags,\n        &regs->fpcw, &regs->fpsw,\n        &regs->fp[0], &regs->fp[1], &regs->fp[2], &regs->fp[3],\n        &regs->fp[4], &regs->fp[5], &regs->fp[6], &regs->fp[7],\n    };\n    uc_trycall(uc_reg_read_batch(uc, uc_regs_ids, ptrs, sizeof(ptrs)/sizeof(ptrs[0])), \"uc_reg_read_batch\");\n}\nvoid uc_setregs(uc_engine *uc, struct uc_regs *regs) {\n    void *const ptrs[sizeof(uc_regs_ids)/sizeof(uc_regs_ids[0])] = {\n        &regs->eax, &regs->ebx, &regs->ecx, &regs->edx,\n        &regs->esi, &regs->edi, &regs->ebp, &regs->esp,\n        &regs->eip, &regs->eflags,\n        &regs->fpcw, &regs->fpsw,\n        &regs->fp[0], &regs->fp[1], &regs->fp[2], &regs->fp[3],\n        &regs->fp[4], &regs->fp[5], &regs->fp[6], &regs->fp[7],\n    };\n    uc_trycall(uc_reg_write_batch(uc, uc_regs_ids, ptrs, sizeof(ptrs)/sizeof(ptrs[0])), \"uc_reg_write_batch\");\n}\n\nint compare_cpus(struct cpu_state *cpu, struct tlb *tlb, uc_engine *uc, int undefined_flags) {\n    int res = 0;\n    struct uc_regs regs;\n    uc_getregs(uc, &regs);\n    collapse_flags(cpu);\n\n#define CHECK(uc, ish, name) \\\n    if ((uc) != (ish)) { \\\n        printk(\"check failed: \" name \": uc 0x%llx, ish 0x%llx\\n\", (unsigned long long) (uc), (unsigned long long) (ish)); \\\n        res = -1; \\\n        ish = uc; \\\n    }\n\n#define CHECK_REG(reg) \\\n    CHECK(regs.reg, cpu->reg, #reg)\n    CHECK_REG(eip);\n    CHECK_REG(eax);\n    CHECK_REG(ebx);\n    CHECK_REG(ecx);\n    CHECK_REG(edx);\n    CHECK_REG(esi);\n    CHECK_REG(edi);\n    CHECK_REG(esp);\n    CHECK_REG(ebp);\n\n    // check the flags, with a nice visual representation\n    regs.eflags = (regs.eflags & ~undefined_flags) | (cpu->eflags & undefined_flags);\n    if (regs.eflags != cpu->eflags) {\n#define f(x,n) ((regs.eflags & (1 << n)) ? #x : \"-\"),\n        printk(\"real eflags = 0x%x %s%s%s%s%s%s%s%s%s, fake eflags = 0x%x %s%s%s%s%s%s%s%s%s\\n%0d\",\n                regs.eflags, f(o,11)f(d,10)f(i,9)f(t,8)f(s,7)f(z,6)f(a,4)f(p,2)f(c,0)\n#undef f\n#define f(x,n) ((cpu->eflags & (1 << n)) ? #x : \"-\"),\n                cpu->eflags, f(o,11)f(d,10)f(i,9)f(t,8)f(s,7)f(z,6)f(a,4)f(p,2)f(c,0)0);\n        res = -1;\n        cpu->eflags = regs.eflags;\n    }\n    // sync up the flags so undefined flags won't error out next time\n\n#define FSW_MASK 0x7d00 // only look at top, c0, c2, c3\n    regs.fpsw &= FSW_MASK;\n    cpu->fsw &= FSW_MASK;\n    CHECK(regs.fpsw, cpu->fsw, \"fsw\");\n    CHECK(regs.fpcw, cpu->fcw, \"fcw\");\n\n#define CHECK_FPREG(i) \\\n    CHECK(regs.fp[i].signif, cpu->fp[i].signif, \"fp\"#i\" signif\"); \\\n    CHECK(regs.fp[i].signExp, cpu->fp[i].signExp, \"fp\"#i\" signExp\")\n    CHECK_FPREG(0);\n    CHECK_FPREG(1);\n    CHECK_FPREG(2);\n    CHECK_FPREG(3);\n    CHECK_FPREG(4);\n    CHECK_FPREG(5);\n    CHECK_FPREG(6);\n    CHECK_FPREG(7);\n\n    uc_setregs(uc, &regs);\n\n    // compare pages marked dirty\n    if (tlb->dirty_page != TLB_PAGE_EMPTY) {\n        char real_page[PAGE_SIZE];\n        uc_trycall(uc_mem_read(uc, tlb->dirty_page, real_page, PAGE_SIZE), \"compare read\");\n        void *fake_page = mmu_translate(cpu->mmu, tlb->dirty_page, MEM_READ);\n\n        if (memcmp(real_page, fake_page, PAGE_SIZE) != 0) {\n            printk(\"page %x doesn't match\\n\", tlb->dirty_page);\n            debugger;\n            return -1;\n        }\n        tlb->dirty_page = TLB_PAGE_EMPTY;\n    }\n\n    return res;\n}\n\nstatic int uc_interrupt;\n\nstatic void set_tls_pointer(uc_engine *uc, dword_t tls_ptr);\n\nstatic void _mem_sync(struct tlb *tlb, uc_engine *uc, addr_t addr, dword_t size) {\n    char buf[size];\n    tlb_read(tlb, addr, buf, size);\n    uc_write(uc, addr, buf, size);\n}\n#define mem_sync(addr, size) _mem_sync(tlb, uc, addr, size)\nvoid step_tracing(struct cpu_state *cpu, struct tlb *tlb, uc_engine *uc) {\n    // step ish\n    addr_t old_brk = current->mm->brk; // this is important\n    int interrupt = cpu_run_to_interrupt(cpu, tlb);\n    handle_interrupt(interrupt);\n\n    // step unicorn\n    uc_interrupt = -1;\n    dword_t eip = uc_getreg(uc, UC_X86_REG_EIP);\n    // intercept cpuid and rdtsc\n    uint8_t code[2];\n    uc_read(uc, eip, code, sizeof(code));\n    if (code[0] == 0x0f && (code[1] == 0x31 || code[1] == 0xa2)) {\n        if (code[1] == 0x31) {\n            uc_setreg(uc, UC_X86_REG_EAX, cpu->eax);\n            uc_setreg(uc, UC_X86_REG_EDX, cpu->edx);\n        } else if (code[1] == 0xa2) {\n            uc_setreg(uc, UC_X86_REG_EAX, cpu->eax);\n            uc_setreg(uc, UC_X86_REG_EBX, cpu->ebx);\n            uc_setreg(uc, UC_X86_REG_ECX, cpu->ecx);\n            uc_setreg(uc, UC_X86_REG_EDX, cpu->edx);\n        }\n        uc_setreg(uc, UC_X86_REG_EIP, eip+2);\n    } else {\n        while (uc_getreg(uc, UC_X86_REG_EIP) == eip)\n            uc_trycall(uc_emu_start(uc, eip, -1, 0, 1), \"unicorn step\");\n    }\n\n    // handle unicorn interrupts\n    struct uc_regs regs;\n    uc_getregs(uc, &regs);\n    if (uc_interrupt == 0x80) {\n        uint32_t syscall_num = regs.eax;\n        switch (syscall_num) {\n            // put syscall result from fake process into real process\n            case 3: // read\n                mem_sync(regs.ecx, cpu->edx); break;\n            case 7: // waitpid\n                mem_sync(regs.ecx, sizeof(dword_t)); break;\n            case 13: // time\n                if (regs.ebx != 0)\n                    mem_sync(regs.ebx, sizeof(dword_t));\n                break;\n            case 54: { // ioctl (god help us)\n                struct fd *fd = f_get(cpu->ebx);\n                if (fd && fd->ops->ioctl_size) {\n                    ssize_t ioctl_size = fd->ops->ioctl_size(cpu->ecx);\n                    if (ioctl_size >= 0)\n                        mem_sync(regs.edx, ioctl_size);\n                }\n                break;\n            }\n            case 85: // readlink\n                mem_sync(regs.ecx, regs.edx); break;\n            case 102: { // socketcall\n                dword_t args[6];\n                (void) user_get(regs.ecx, args);\n                dword_t len;\n                switch (cpu->ebx) {\n                    case 6: // getsockname\n                        (void) user_get(args[2], len);\n                        mem_sync(args[1], len);\n                        break;\n                    case 8: // socketpair\n                        mem_sync(args[3], sizeof(dword_t[2]));\n                        break;\n                    case 12: // recvfrom\n                        mem_sync(args[1], args[2]);\n                        (void) user_get(args[5], len);\n                        mem_sync(args[4], len);\n                        break;\n                }\n                break;\n            }\n            case 104: // setitimer\n                mem_sync(regs.edx, sizeof(struct itimerval_)); break;\n            case 116: // sysinfo\n                mem_sync(regs.ebx, sizeof(struct sys_info)); break;\n            case 122: // uname\n                mem_sync(regs.ebx, sizeof(struct uname)); break;\n            case 140: // _llseek\n                mem_sync(regs.esi, 8); break;\n            case 145: { // readv\n                struct iovec_ vecs[regs.edx];\n                (void) user_get(regs.ecx, vecs);\n                for (unsigned i = 0; i < regs.edx; i++)\n                    mem_sync(vecs[i].base, vecs[i].len);\n                break;\n            }\n            case 162: // nanosleep\n                mem_sync(regs.ecx, sizeof(struct timespec_)); break;\n            case 168: // poll\n                mem_sync(regs.ebx, sizeof(struct pollfd_) * regs.ecx); break;\n            case 174: // rt_sigaction\n                if (regs.edx)\n                    mem_sync(regs.edx, sizeof(struct sigaction_));\n                break;\n            case 183: // getcwd\n                mem_sync(regs.ebx, cpu->eax); break;\n\n            case 195: // stat64\n            case 196: // lstat64\n            case 197: // fstat64\n                mem_sync(regs.ecx, sizeof(struct newstat64)); break;\n            case 300: // fstatat64\n                mem_sync(regs.edx, sizeof(struct newstat64)); break;\n            case 220: // getdents64\n                mem_sync(regs.ecx, cpu->eax); break;\n            case 265: // clock_gettime\n                mem_sync(regs.ecx, sizeof(struct timespec_)); break;\n\n            case 192: // mmap2\n            case 90: // mmap\n                if (cpu->eax >= 0xfffff000) {\n                    // fake mmap failed, so don't try real mmap\n                    break;\n                }\n                // IMPORTANT: if you try to understand this code you will get brain cancer\n                addr_t start = cpu->eax;\n                dword_t size = cpu->ecx;\n                int prot = cpu->edx;\n                struct fd *fd = f_get(cpu->edi);\n                int real_fd = fd ? fd->real_fd : -1;\n                int flags = cpu->esi & ~MAP_FIXED;\n                off_t offset = cpu->ebp;\n                if (syscall_num == 192)\n                    offset <<= PAGE_BITS;\n                void *mem = mmap(NULL, size, prot, flags, real_fd, offset);\n                if (mem == MAP_FAILED) {\n                    perror(\"mmap emulation\");\n                    exit(1);\n                }\n                uc_map_ptr(uc, start, mem, size);\n                break;\n\n            case 91: // munmap\n                if ((int) cpu->eax >= 0)\n                    uc_unmap(uc, cpu->ebx, cpu->ecx);\n                break;\n\n            case 45: // brk\n                // matches up with the logic in kernel/mmap.c\n                if (current->mm->brk > old_brk) {\n                    uc_map(uc, BYTES_ROUND_UP(old_brk), BYTES_ROUND_UP(current->mm->brk) - BYTES_ROUND_UP(old_brk));\n                } else if (current->mm->brk < old_brk) {\n                    uc_unmap(uc, BYTES_ROUND_DOWN(current->mm->brk), BYTES_ROUND_DOWN(old_brk) - BYTES_ROUND_DOWN(current->mm->brk));\n                }\n                break;\n\n            case 243: { // set_thread_area\n                // icky hacky\n                addr_t tls_ptr;\n                uc_read(uc, regs.ebx + 4, &tls_ptr, sizeof(tls_ptr));\n                set_tls_pointer(uc, tls_ptr);\n                mem_sync(regs.ebx, 4);\n                break;\n            }\n        }\n        uc_setreg(uc, UC_X86_REG_EAX, cpu->eax);\n    } else if (uc_interrupt != -1) {\n        printk(\"unhandled unicorn interrupt 0x%x\\n\", uc_interrupt);\n        exit(1);\n    }\n}\n\nstatic void uc_interrupt_callback(uc_engine *uc, uint32_t interrupt, void *UNUSED(user_data)) {\n    uc_interrupt = interrupt;\n    uc_emu_stop(uc);\n}\n\nstatic bool uc_unmapped_callback(uc_engine *uc, uc_mem_type UNUSED(type), uint64_t address, int size, int64_t UNUSED(value), void *UNUSED(user_data)) {\n    struct pt_entry *pt = mem_pt(current->mem, PAGE(address));\n    // handle stack growing\n    if (pt != NULL && pt->flags & P_GROWSDOWN) {\n        uc_map(uc, BYTES_ROUND_DOWN(address), PAGE_SIZE);\n        return true;\n    }\n    printk(\"unicorn reports unmapped access at 0x%lx size %d\\n\", address, size);\n    return false;\n}\n\n// thread local bullshit {{{\nstruct gdt_entry {\n    uint16_t limit0;\n    uint16_t base0;\n    uint8_t base1;\n    bitfield type:4;\n    bitfield system:1;\n    bitfield dpl:2;\n    bitfield present:1;\n    unsigned limit1:4;\n    bitfield avail:1;\n    bitfield is_64_code:1;\n    bitfield db:1;\n    bitfield granularity:1;\n    uint8_t base2;\n} __attribute__((packed));\n\n// it has to go somewhere, so why not page 1, where nothing can go normally\n#define GDT_ADDR 0x1000\n\nstatic void setup_gdt(uc_engine *uc) {\n    // construct gdt\n    struct gdt_entry gdt[13] = {};\n    // descriptor 0 can't be used\n    // descriptor 1 = all of memory as code\n    gdt[1].limit0 = 0xffff; gdt[1].limit1 = 0xf; gdt[1].granularity = 1;\n    gdt[1].system = 1; gdt[1].type = 0xf; gdt[1].db = 1; gdt[1].present = 1;\n    // descriptor 2 = all of memory as data\n    gdt[2] = gdt[1]; gdt[2].type = 0x3;\n    // descriptor 12 = thread locals\n    gdt[12] = gdt[2]; gdt[12].dpl = 3;\n\n    // put gdt into memory, somewhere, idgaf where\n    uc_trycall(uc_mem_map(uc, GDT_ADDR, PAGE_SIZE, UC_PROT_READ), \"map gdt\");\n    uc_trycall(uc_mem_write(uc, GDT_ADDR, &gdt, sizeof(gdt)), \"write gdt\");\n\n    // load segment registers\n    uc_x86_mmr gdtr = {.base = GDT_ADDR, .limit = sizeof(gdt)};\n    uc_trycall(uc_reg_write(uc, UC_X86_REG_GDTR, &gdtr), \"write gdtr\");\n    uc_setreg(uc, UC_X86_REG_CS, 1<<3);\n    uc_setreg(uc, UC_X86_REG_DS, 2<<3);\n    uc_setreg(uc, UC_X86_REG_ES, 2<<3);\n    uc_setreg(uc, UC_X86_REG_FS, 2<<3);\n    uc_setreg(uc, UC_X86_REG_SS, 2<<3);\n}\n\nstatic void set_tls_pointer(uc_engine *uc, dword_t tls_ptr) {\n    struct gdt_entry tls_entry;\n    uc_read(uc, GDT_ADDR + 12 * sizeof(struct gdt_entry), &tls_entry, sizeof(tls_entry));\n    tls_entry.base0 = (tls_ptr & 0x0000ffff);\n    tls_entry.base1 = (tls_ptr & 0x00ff0000) >> 16;\n    tls_entry.base2 = (tls_ptr & 0xff000000) >> 24;\n    uc_write(uc, GDT_ADDR + 12 * sizeof(struct gdt_entry), &tls_entry, sizeof(tls_entry));\n}\n// }}}\n\nuc_engine *start_unicorn(struct cpu_state *cpu, struct mem *mem) {\n    uc_engine *uc;\n    uc_trycall(uc_open(UC_ARCH_X86, UC_MODE_32, &uc), \"uc_open\");\n\n    // copy registers\n    uc_setreg(uc, UC_X86_REG_ESP, cpu->esp);\n    uc_setreg(uc, UC_X86_REG_EIP, cpu->eip);\n    uc_setreg(uc, UC_X86_REG_EFLAGS, cpu->eflags);\n    uc_setreg(uc, UC_X86_REG_FPCW, cpu->fcw);\n\n    // copy memory\n    // XXX unicorn has a ?bug? where setting up 334 mappings takes five\n    // seconds on my raspi. it seems to be accidentally quadratic (dot tumblr dot com)\n    for (page_t page = 0; page < MEM_PAGES; page++) {\n        struct pt_entry *pt = mem_pt(mem, page);\n        if (pt == NULL)\n            continue;\n        int prot = UC_PROT_READ | UC_PROT_EXEC;\n        // really only the write bit is meaningful (FIXME)\n        if (pt->flags & P_WRITE) prot |= UC_PROT_WRITE;\n        addr_t addr = page << PAGE_BITS;\n        void *data = pt->data->data + pt->offset;\n        uc_trycall(uc_mem_map(uc, addr, PAGE_SIZE, prot), \"uc_mem_map\");\n        uc_trycall(uc_mem_write(uc, addr, data, PAGE_SIZE), \"uc_mem_write\");\n    }\n\n    // set up some sort of gdt, because we need gs to work for thread locals\n    setup_gdt(uc);\n\n    // set up exception handler\n    uc_hook hook;\n    uc_trycall(uc_hook_add(uc, &hook, UC_HOOK_INTR, uc_interrupt_callback, NULL, 1, 0), \"uc_hook_add\");\n    uc_trycall(uc_hook_add(uc, &hook, UC_HOOK_MEM_UNMAPPED, uc_unmapped_callback, NULL, 1, 0), \"uc_hook_add\");\n\n    return uc;\n}\n\nint main(int argc, char *const argv[]) {\n    int err = xX_main_Xx(argc, argv, NULL);\n    if (err < 0) {\n        // FIXME this often prints the wrong error message on non-x86_64\n        fprintf(stderr, \"%s\\n\", strerror(-err));\n        return err;\n    }\n\n    // create a unicorn and set it up exactly the same as the current process\n    uc_engine *uc = start_unicorn(&current->cpu, &current->mm->mem);\n\n    struct cpu_state *cpu = &current->cpu;\n    struct tlb tlb;\n    tlb_refresh(&tlb, cpu->mmu);\n    int undefined_flags = 0;\n    struct cpu_state old_cpu = *cpu;\n    while (true) {\n        while (compare_cpus(cpu, &tlb, uc, undefined_flags) < 0) {\n            printk(\"resetting cpu\\n\");\n            *cpu = old_cpu;\n            debugger;\n            cpu_run_to_interrupt(cpu, &tlb);\n        }\n        undefined_flags = undefined_flags_mask(cpu, &tlb);\n        old_cpu = *cpu;\n        step_tracing(cpu, &tlb, uc);\n    }\n}\n\nvoid dump_memory(uc_engine *uc, const char *file, addr_t start, size_t size) {\n    char buf[size];\n    uc_trycall(uc_mem_read(uc, start, buf, size), \"uc_mem_read\");\n    FILE *f = fopen(file, \"w\");\n    fwrite(buf, 1, sizeof(buf), f);\n    fclose(f);\n}\n\n"
  },
  {
    "path": "tools/vdso-dump.c",
    "content": "// Dumps the VDSO to a file called libvdso.so\n#include <stdio.h>\n#include <sys/auxv.h>\n\nint main() {\n    void *vdso = (void *) getauxval(AT_SYSINFO_EHDR);\n    FILE *f = fopen(\"libvdso.so\", \"w\");\n    if (f == NULL) {\n        perror(\"fopen libvdso.so\");\n        return 1;\n    }\n    fwrite(vdso, 0x2000, 1, f);\n}\n"
  },
  {
    "path": "tools/vdso-transplant-main.c",
    "content": "// Run a process but replace the vdso with the one in the given file.\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/mman.h>\n#include <sys/personality.h>\n#include <sys/ptrace.h>\n#include <sys/wait.h>\n#include <signal.h>\n\n#include \"debug.h\"\n#include \"ptutil.h\"\n#include \"transplant.h\"\n\nint main(int UNUSED(argc), char *const argv[]) {\n    char *const envp[] = {NULL};\n    int pid = start_tracee(AT_FDCWD, argv[2], argv + 2, envp);\n\n    int vdso_fd = trycall(open(argv[1], O_RDONLY), \"open vdso\");\n    struct stat statbuf;\n    trycall(fstat(vdso_fd, &statbuf), \"stat vdso\");\n    size_t vdso_size = statbuf.st_size;\n    void *vdso = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, vdso_fd, 0);\n    if (vdso == MAP_FAILED) {\n        perror(\"mmap vdso\"); exit(1);\n    }\n    transplant_vdso(pid, vdso, vdso_size);\n\n    trycall(kill(pid, SIGSTOP), \"pause process\");\n    printf(\"attach debugger to %d\\n\", pid);\n    return 0;\n}\n"
  },
  {
    "path": "tools/vdso-transplant.c",
    "content": "// Uses ptrace to overwrite the vdso of a running process.\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/user.h>\n#include <sys/ptrace.h>\n#include \"kernel/elf.h\"\n#include \"tools/ptutil.h\"\n#include \"misc.h\"\n\nstatic addr_t aux_addr(int pid, unsigned type) {\n    struct user_regs_struct regs;\n    trycall(ptrace(PTRACE_GETREGS, pid, NULL, &regs), \"ptrace get sp for aux\");\n    dword_t sp = (dword_t) regs.rsp;\n    // skip argc\n    sp += 4;\n    // skip argv\n    while (pt_read(pid, sp) != 0)\n        sp += 4;\n    sp += 4;\n    // skip envp\n    while (pt_read(pid, sp) != 0)\n        sp += 4;\n    sp += 4;\n    // dig through auxv\n    dword_t aux_type;\n    while ((aux_type = pt_read(pid, sp)) != 0) {\n        sp += 4;\n        if (aux_type == type) {\n            return sp;\n        }\n        sp += 4;\n    }\n    return 0;\n}\n\nstatic void aux_write(int pid, int type, dword_t value) {\n    return pt_write(pid, aux_addr(pid, type), value);\n}\n\nvoid transplant_vdso(int pid, const void *new_vdso, size_t new_vdso_size) {\n    // get the vdso address and size from /proc/pid/maps\n    char maps_file[32];\n    sprintf(maps_file, \"/proc/%d/maps\", pid);\n    FILE *maps = fopen(maps_file, \"r\");\n\n    char line[256];\n    dword_t start, end;\n    while (fgets(line, sizeof(line), maps) != NULL) {\n        char *map_type = NULL;\n        sscanf(line, \"%8x-%8x %*s %*s %*s %*s %ms\\n\", &start, &end, &map_type);\n        if (map_type) {\n            if (strcmp(map_type, \"[vdso]\") == 0) {\n                free(map_type);\n                break;\n            }\n            free(map_type);\n        }\n    }\n    fclose(maps);\n\n    // copy the new vdso in\n    for (dword_t addr = start; addr < end; addr += sizeof(unsigned long)) {\n        unsigned long new_vdso_data = 0;\n        if (addr - start < new_vdso_size) {\n            new_vdso_data = *(unsigned long *) ((char *) new_vdso + addr - start);\n        }\n        if (ptrace(PTRACE_POKEDATA, pid, addr, new_vdso_data) < 0) {\n            perror(\"ptrace vdso poke\"); exit(1);\n        }\n    }\n\n    // find the entry point\n    dword_t entry = *(dword_t *) ((char *) new_vdso + 24) + start;\n\n    aux_write(pid, AX_SYSINFO, entry);\n    aux_write(pid, AX_SYSINFO_EHDR, start);\n}\n"
  },
  {
    "path": "util/bits.h",
    "content": "#ifndef BITS_H\n#define BITS_H\n\ntypedef void bits_t;\n#define BITS_SIZE(bits) ((((bits) - 1) / 8) + 1)\n\nstatic inline bool bit_test(size_t i, bits_t *data) {\n    char *c = data;\n    return c[i >> 3] & (1 << (i & 7)) ? 1 : 0;\n}\n\nstatic inline void bit_set(size_t i, bits_t *data) {\n    char *c = data;\n    c[i >> 3] |= 1 << (i & 7);\n}\n\nstatic inline void bit_clear(size_t i, bits_t *data) {\n    char *c = data;\n    c[i >> 3] &= ~(1 << (i & 7));\n}\n\n#endif\n"
  },
  {
    "path": "util/fchdir.c",
    "content": "#include <unistd.h>\n#include \"util/sync.h\"\n\nstatic lock_t fchdir_lock = LOCK_INITIALIZER;\n\nvoid lock_fchdir(int dirfd) {\n    lock(&fchdir_lock);\n    fchdir(dirfd);\n}\n\nvoid unlock_fchdir() {\n    unlock(&fchdir_lock);\n}"
  },
  {
    "path": "util/fchdir.h",
    "content": "// temporarily change directory and block other threads from doing so\n// useful for simulating mknodat on ios, dealing with long unix socket paths, etc\nvoid lock_fchdir(int dirfd);\nvoid unlock_fchdir();\n"
  },
  {
    "path": "util/fifo.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include \"util/fifo.h\"\n\nvoid fifo_init(struct fifo *fifo, size_t capacity) {\n    fifo->buf = malloc(capacity);\n    fifo->capacity = capacity;\n    fifo->size = fifo->start = 0;\n}\n\nvoid fifo_destroy(struct fifo *fifo) {\n    free(fifo->buf);\n}\n\nsize_t fifo_capacity(struct fifo *fifo) {\n    return fifo->capacity;\n}\nsize_t fifo_size(struct fifo *fifo) {\n    return fifo->size;\n}\nsize_t fifo_remaining(struct fifo *fifo) {\n    return fifo->capacity - fifo->size;\n}\n\nint fifo_write(struct fifo *fifo, const void *data, size_t size, int flags) {\n    if (size > fifo_remaining(fifo)) {\n        if (!(flags & FIFO_OVERWRITE))\n            return 1;\n        size_t excess = size - fifo_remaining(fifo);\n        fifo->start = (fifo->start + excess) % fifo->capacity;\n        fifo->size -= excess;\n    }\n\n    size_t tail = (fifo->start + fifo->size) % fifo->capacity;;\n    size_t first_copy_size = fifo->capacity - tail;\n    if (first_copy_size > size)\n        first_copy_size = size;\n    memcpy(&fifo->buf[tail], data, first_copy_size);\n    memcpy(&fifo->buf[0], (char *) data + first_copy_size, size - first_copy_size);\n    fifo->size += size;\n    return 0;\n}\n\nint fifo_read(struct fifo *fifo, void *buf, size_t size, int flags) {\n    if (size > fifo_size(fifo))\n        return 1;\n\n    size_t start = fifo->start;\n    if (flags & FIFO_LAST)\n        start = (start + (fifo->size - size)) % fifo->capacity;\n\n    size_t first_copy_size = fifo->capacity - fifo->start;\n    if (first_copy_size > size)\n        first_copy_size = size;\n    memcpy(buf, &fifo->buf[start], first_copy_size);\n    memcpy((char *) buf + first_copy_size, &fifo->buf[0], size - first_copy_size);\n\n    if (!(flags & FIFO_PEEK)) {\n        fifo->start = (start + size) % fifo->capacity;\n        fifo->size -= size;\n    }\n    return 0;\n}\n\nvoid fifo_flush(struct fifo *fifo) {\n    fifo->size = 0;\n}\n"
  },
  {
    "path": "util/fifo.h",
    "content": "#ifndef UTIL_FIFO_H\n#define UTIL_FIFO_H\n#include <sys/types.h>\n\nstruct fifo {\n    char *buf;\n    size_t capacity;\n    size_t size;\n    size_t start;\n};\n\n#define FIFO_INIT(b) ((struct fifo) {.buf = b, .capacity = sizeof(b)})\nvoid fifo_init(struct fifo *fifo, size_t capacity);\nvoid fifo_destroy(struct fifo *fifo);\n\nsize_t fifo_capacity(struct fifo *fifo);\nsize_t fifo_size(struct fifo *fifo);\nsize_t fifo_remaining(struct fifo *fifo);\n\n// return 0 on success, 1 if size > fifo_remaining and FIFO_OVERWRITE was not specified\n#define FIFO_OVERWRITE 1\nint fifo_write(struct fifo *fifo, const void *data, size_t size, int flags);\n\n// return 0 on success, 1 if size > fifo_size\n#define FIFO_PEEK 1 // don't remove the bytes\n#define FIFO_LAST 2 // return bytes from the end, not the start\nint fifo_read(struct fifo *fifo, void *buf, size_t size, int flags);\n\nvoid fifo_flush(struct fifo *fifo);\n\n#endif\n"
  },
  {
    "path": "util/list.h",
    "content": "#ifndef LIST_H\n#define LIST_H\n\n#include <stdbool.h>\n#include <stddef.h>\n\nstruct list {\n    struct list *next, *prev;\n};\n\n#ifndef __KERNEL__\n\nstatic inline void list_init(struct list *list) {\n    list->next = list;\n    list->prev = list;\n}\n\n#define LIST_INITIALIZER(x) {.prev = &x, .next = &x}\n\nstatic inline bool list_null(struct list *list) {\n    return list->next == NULL && list->prev == NULL;\n}\n\nstatic inline bool list_empty(struct list *list) {\n    return list->next == list || list_null(list);\n}\n\nstatic inline void _list_add_between(struct list *prev, struct list *next, struct list *item) {\n    prev->next = item;\n    item->prev = prev;\n    item->next = next;\n    next->prev = item;\n}\n\nstatic inline void list_add_tail(struct list *list, struct list *item) {\n    _list_add_between(list->prev, list, item);\n}\n\nstatic inline void list_add(struct list *list, struct list *item) {\n    _list_add_between(list, list->next, item);\n}\n\nstatic inline void list_add_before(struct list *before, struct list *item) {\n    list_add_tail(before, item);\n}\nstatic inline void list_add_after(struct list *after, struct list *item) {\n    list_add(after, item);\n}\n\nstatic inline void list_init_add(struct list *list, struct list *item) {\n    if (list_null(list))\n        list_init(list);\n    list_add(list, item);\n}\n\nstatic inline void list_remove(struct list *item) {\n    item->prev->next = item->next;\n    item->next->prev = item->prev;\n    item->next = item->prev = NULL;\n}\n\nstatic inline void list_remove_safe(struct list *item) {\n    if (!list_null(item))\n        list_remove(item);\n}\n\n#define list_entry(item, type, member) \\\n    container_of(item, type, member)\n#define list_first_entry(list, type, member) \\\n    list_entry((list)->next, type, member)\n#define list_next_entry(item, member) \\\n    list_entry((item)->member.next, typeof(*(item)), member)\n\n#define list_for_each(list, item) \\\n    for (item = (list)->next; item != (list); item = item->next)\n#define list_for_each_safe(list, item, tmp) \\\n    for (item = (list)->next, tmp = item->next; item != (list); \\\n            item = tmp, tmp = item->next)\n\n#define list_for_each_entry(list, item, member) \\\n    for (item = list_entry((list)->next, typeof(*item), member); \\\n            &item->member != (list); \\\n            item = list_entry(item->member.next, typeof(*item), member))\n#define list_for_each_entry_safe(list, item, tmp, member) \\\n    for (item = list_first_entry(list, typeof(*(item)), member), \\\n            tmp = list_next_entry(item, member); \\\n            &item->member != (list); \\\n            item = tmp, tmp = list_next_entry(item, member))\n\nstatic inline unsigned long list_size(struct list *list) {\n    unsigned long count = 0;\n    struct list *item;\n    list_for_each(list, item) {\n        count++;\n    }\n    return count;\n}\n\n#endif\n\n#endif\n"
  },
  {
    "path": "util/refcount.h",
    "content": "#ifndef UTIL_REFCOUNT_H\n#define UTIL_REFCOUNT_H\n#include <stdatomic.h>\n\n// An industrial-strength refcounting implementation.\n// Safety first! Make sure to:\n// - Write a release for every retain\n// - Get exclusive access to the reference before retaining or releasing it\n\nstruct refcount {\n    atomic_uint rc;\n};\n\nstatic inline void __refcount_init(struct refcount *refcount) {\n    refcount->rc = 1;\n}\n#define refcount_init(obj) __refcount_init(&(obj)->refcount)\n\nstatic inline int __refcount_get(struct refcount *refcount) {\n    return refcount->rc;\n}\n#define refcount_get(obj) __refcount_get(&(obj)->refcount)\n\n#define DECLARE_REFCOUNT(type) \\\n    void type##_retain(struct type *obj); \\\n    void type##_release(struct type *obj)\n\n#define __DEFINE_REFCOUNT(type, qualifiers) \\\n    qualifiers struct type *type##_retain(struct type *obj) { \\\n        obj->refcount.rc++; \\\n        return obj; \\\n    } \\\n    static void type##_cleanup(struct type *obj); \\\n    qualifiers void type##_release(struct type *obj) { \\\n        if (--obj->refcount.rc == 0) \\\n            type##_cleanup(obj); \\\n    }\n\n#define DEFINE_REFCOUNT(type) __DEFINE_REFCOUNT(type, )\n#define DEFINE_REFCOUNT_STATIC(type) __DEFINE_REFCOUNT(type, static)\n\n#endif\n"
  },
  {
    "path": "util/sync.c",
    "content": "#include <errno.h>\n#include <limits.h>\n#include \"kernel/task.h\"\n#include \"util/sync.h\"\n#include \"debug.h\"\n#include \"kernel/errno.h\"\n\nvoid cond_init(cond_t *cond) {\n    pthread_condattr_t attr;\n    pthread_condattr_init(&attr);\n#if __linux__\n    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);\n#endif\n    pthread_cond_init(&cond->cond, &attr);\n}\nvoid cond_destroy(cond_t *cond) {\n    pthread_cond_destroy(&cond->cond);\n}\n\nstatic bool is_signal_pending(lock_t *lock) {\n    if (!current)\n        return false;\n    if (lock != &current->sighand->lock)\n        lock(&current->sighand->lock);\n    bool pending = !!(current->pending & ~current->blocked);\n    if (lock != &current->sighand->lock)\n        unlock(&current->sighand->lock);\n    return pending;\n}\n\nint wait_for(cond_t *cond, lock_t *lock, struct timespec *timeout) {\n    if (is_signal_pending(lock))\n        return _EINTR;\n    int err = wait_for_ignore_signals(cond, lock, timeout);\n    if (err < 0)\n        return _ETIMEDOUT;\n    if (is_signal_pending(lock))\n        return _EINTR;\n    return 0;\n}\n\nint wait_for_ignore_signals(cond_t *cond, lock_t *lock, struct timespec *timeout) {\n    if (current) {\n        lock(&current->waiting_cond_lock);\n        current->waiting_cond = cond;\n        current->waiting_lock = lock;\n        unlock(&current->waiting_cond_lock);\n    }\n    int rc = 0;\n#if LOCK_DEBUG\n    struct lock_debug lock_tmp = lock->debug;\n    lock->debug = (struct lock_debug) { .initialized = lock->debug.initialized };\n#endif\n    if (!timeout) {\n        pthread_cond_wait(&cond->cond, &lock->m);\n    } else {\n#if __linux__\n        struct timespec abs_timeout;\n        clock_gettime(CLOCK_MONOTONIC, &abs_timeout);\n        abs_timeout.tv_sec += timeout->tv_sec;\n        abs_timeout.tv_nsec += timeout->tv_nsec;\n        if (abs_timeout.tv_nsec > 1000000000) {\n            abs_timeout.tv_sec++;\n            abs_timeout.tv_nsec -= 1000000000;\n        }\n        rc = pthread_cond_timedwait(&cond->cond, &lock->m, &abs_timeout);\n#elif __APPLE__\n        rc = pthread_cond_timedwait_relative_np(&cond->cond, &lock->m, timeout);\n#else\n#error Unimplemented pthread_cond_wait relative timeout.\n#endif\n    }\n#if LOCK_DEBUG\n    lock->debug = lock_tmp;\n#endif\n\n    if (current) {\n        lock(&current->waiting_cond_lock);\n        current->waiting_cond = NULL;\n        current->waiting_lock = NULL;\n        unlock(&current->waiting_cond_lock);\n    }\n    if (rc == ETIMEDOUT)\n        return _ETIMEDOUT;\n    return 0;\n}\n\nvoid notify(cond_t *cond) {\n    pthread_cond_broadcast(&cond->cond);\n}\nvoid notify_once(cond_t *cond) {\n    pthread_cond_signal(&cond->cond);\n}\n\n__thread sigjmp_buf unwind_buf;\n__thread bool should_unwind = false;\n\nvoid sigusr1_handler() {\n    if (should_unwind) {\n        should_unwind = false;\n        siglongjmp(unwind_buf, 1);\n    }\n}\n\n// This is how you would mitigate the unlock/wait race if the wait\n// is async signal safe. wait_for *should* be safe from this race\n// because of synchronization involving the waiting_cond_lock.\n#if 0\n    sigset_t sigusr1;\n    sigemptyset(&sigusr1);\n    sigaddset(&sigusr1, SIGUSR1);\n\n    if (current) {\n        if (sigsetjmp(unwind_buf, 1)) {\n            return _EINTR;\n        }\n        should_unwind = true;\n        sigprocmask(SIG_BLOCK, &sigusr1, NULL);\n        if (lock != &current->sighand->lock)\n            lock(&current->sighand->lock);\n        bool pending = !!(current->pending & ~current->blocked);\n        if (lock != &current->sighand->lock)\n            unlock(&current->sighand->lock);\n        sigprocmask(SIG_UNBLOCK, &sigusr1, NULL);\n        if (pending) {\n            should_unwind = false;\n            return _EINTR;\n        }\n    }\n#endif\n\n"
  },
  {
    "path": "util/sync.h",
    "content": "#ifndef UTIL_SYNC_H\n#define UTIL_SYNC_H\n\n#include <stdatomic.h>\n#include <pthread.h>\n#include <stdbool.h>\n#include <assert.h>\n#include <setjmp.h>\n#include \"misc.h\"\n#include \"debug.h\"\n\n// locks, implemented using pthread\n\n#define LOCK_DEBUG 0\n\ntypedef struct {\n    pthread_mutex_t m;\n    pthread_t owner;\n#if LOCK_DEBUG\n    struct lock_debug {\n        const char *file; // doubles as locked\n        int line;\n        int pid;\n        bool initialized;\n    } debug;\n#endif\n} lock_t;\n\nstatic inline void lock_init(lock_t *lock) {\n    pthread_mutex_init(&lock->m, NULL);\n#if LOCK_DEBUG\n    lock->debug = (struct lock_debug) {\n        .initialized = true,\n    };\n#endif\n}\n\n#if LOCK_DEBUG\n#define LOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, 0, { .initialized = true }}\n#else\n#define LOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, 0}\n#endif\nstatic inline void __lock(lock_t *lock, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {\n    pthread_mutex_lock(&lock->m);\n    lock->owner = pthread_self();\n#if LOCK_DEBUG\n    assert(lock->debug.initialized);\n    assert(!lock->debug.file && \"Attempting to recursively lock\");\n    lock->debug.file = file;\n    lock->debug.line = line;\n    extern int current_pid(void);\n    lock->debug.pid = current_pid();\n#endif\n}\n#define lock(lock) __lock(lock, __FILE__, __LINE__)\nstatic inline void unlock(lock_t *lock) {\n#if LOCK_DEBUG\n    assert(lock->debug.initialized);\n    assert(lock->debug.file && \"Attempting to unlock an unlocked lock\");\n    lock->debug = (struct lock_debug) { .initialized = true };\n#endif\n    lock->owner = zero_init(pthread_t);\n    pthread_mutex_unlock(&lock->m);\n}\n\nstatic inline int trylock(lock_t *lock, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {\n    int status = pthread_mutex_trylock(&lock->m);\n#if LOCK_DEBUG\n    if (!status) {\n        lock->debug.file = file;\n        lock->debug.line = line;\n        extern int current_pid(void);\n        lock->debug.pid = current_pid();\n    }\n#endif\n    return status;\n}\n#define trylock(lock) trylock(lock, __FILE__, __LINE__)\n\n// conditions, implemented using pthread conditions but hacked so you can also\n// be woken by a signal\n\ntypedef struct {\n    pthread_cond_t cond;\n} cond_t;\n#define COND_INITIALIZER ((cond_t) {PTHREAD_COND_INITIALIZER})\n\n// Must call before using the condition\nvoid cond_init(cond_t *cond);\n// Must call when finished with the condition (currently doesn't do much but might do something important eventually I guess)\nvoid cond_destroy(cond_t *cond);\n// Releases the lock, waits for the condition, and reacquires the lock.\n// Returns _EINTR if waiting stopped because the thread received a signal,\n// _ETIMEDOUT if waiting stopped because the timout expired, 0 otherwise.\n// Will never return _ETIMEDOUT if timeout is NULL.\nint must_check wait_for(cond_t *cond, lock_t *lock, struct timespec *timeout);\n// Same as wait_for, except it will never return _EINTR\nint wait_for_ignore_signals(cond_t *cond, lock_t *lock, struct timespec *timeout);\n// Wake up all waiters.\nvoid notify(cond_t *cond);\n// Wake up one waiter.\nvoid notify_once(cond_t *cond);\n\n// this is a read-write lock that prefers writers, i.e. if there are any\n// writers waiting a read lock will block.\n// on darwin pthread_rwlock_t is already like this, on linux you can configure\n// it to prefer writers. not worrying about anything else right now.\ntypedef struct {\n    pthread_rwlock_t l;\n    // 0: unlocked\n    // -1: write-locked\n    // >0: read-locked with this many readers\n    atomic_int val;\n    const char *file;\n    int line;\n    int pid;\n} wrlock_t;\nstatic inline void wrlock_init(wrlock_t *lock) {\n    pthread_rwlockattr_t *pattr = NULL;\n#if defined(__GLIBC__)\n    pthread_rwlockattr_t attr;\n    pattr = &attr;\n    pthread_rwlockattr_init(pattr);\n    pthread_rwlockattr_setkind_np(pattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);\n#endif\n    if (pthread_rwlock_init(&lock->l, pattr)) __builtin_trap();\n    lock->val = lock->line = lock->pid = 0;\n    lock->file = NULL;\n}\n\nextern int current_pid(void);\nstatic inline void wrlock_destroy(wrlock_t *lock) {\n    if (pthread_rwlock_destroy(&lock->l) != 0) __builtin_trap();\n}\nstatic inline void read_wrlock(wrlock_t *lock) {\n    if (pthread_rwlock_rdlock(&lock->l) != 0) __builtin_trap();\n    assert(lock->val >= 0);\n    lock->val++;\n}\nstatic inline void read_wrunlock(wrlock_t *lock) {\n    assert(lock->val > 0);\n    lock->val--;\n    if (pthread_rwlock_unlock(&lock->l) != 0) __builtin_trap();\n}\nstatic inline void __write_wrlock(wrlock_t *lock, const char *file, int line) {\n    if (pthread_rwlock_wrlock(&lock->l) != 0) __builtin_trap();\n    assert(lock->val == 0);\n    lock->val = -1;\n    lock->file = file;\n    lock->line = line;\n    lock->pid = current_pid();\n}\n#define write_wrlock(lock) __write_wrlock(lock, __FILE__, __LINE__)\nstatic inline void write_wrunlock(wrlock_t *lock) {\n    assert(lock->val == -1);\n    lock->val = lock->line = lock->pid = 0;\n    lock->file = NULL;\n    if (pthread_rwlock_unlock(&lock->l) != 0) __builtin_trap();\n}\n\nextern __thread sigjmp_buf unwind_buf;\nextern __thread bool should_unwind;\nstatic inline int sigunwind_start(void) {\n    if (sigsetjmp(unwind_buf, 1)) {\n        should_unwind = false;\n        return 1;\n    } else {\n        should_unwind = true;\n        return 0;\n    }\n}\nstatic inline void sigunwind_end(void) {\n    should_unwind = false;\n}\n\n#endif\n"
  },
  {
    "path": "util/timer.c",
    "content": "#include <stdlib.h>\n#include <signal.h>\n#include <time.h>\n#include \"util/timer.h\"\n#include \"misc.h\"\n\nstruct timer *timer_new(clockid_t clockid, timer_callback_t callback, void *data) {\n//    assert(clockid == CLOCK_MONOTONIC || clockid == CLOCK_REALTIME);\n    struct timer *timer = malloc(sizeof(struct timer));\n    timer->clockid = clockid;\n    timer->callback = callback;\n    timer->data = data;\n    timer->active = false;\n    timer->thread_running = false;\n    lock_init(&timer->lock);\n    timer->dead = false;\n    return timer;\n}\n\nvoid timer_free(struct timer *timer) {\n    lock(&timer->lock);\n    timer->active = false;\n    if (timer->thread_running) {\n        timer->dead = true;\n        pthread_kill(timer->thread, SIGUSR1);\n        unlock(&timer->lock);\n    } else {\n        unlock(&timer->lock);\n        free(timer);\n    }\n}\n\nstatic void *timer_thread(void *param) {\n    struct timer *timer = param;\n    lock(&timer->lock);\n    while (true) {\n        struct timespec remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));\n        while (timer->active && timespec_positive(remaining)) {\n            unlock(&timer->lock);\n            nanosleep(&remaining, NULL);\n            lock(&timer->lock);\n            remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));\n        }\n        if (timer->active)\n            timer->callback(timer->data);\n        if (timer->active && timespec_positive(timer->interval)) {\n            timer->start = timer->end;\n            timer->end = timespec_add(timer->start, timer->interval);\n        } else {\n            break;\n        }\n    }\n    timer->thread_running = false;\n    if (timer->dead)\n        free(timer);\n    else\n        unlock(&timer->lock);\n    return NULL;\n}\n\nint timer_set(struct timer *timer, struct timer_spec spec, struct timer_spec *oldspec) {\n    lock(&timer->lock);\n    struct timespec now = timespec_now(timer->clockid);\n    if (oldspec != NULL) {\n        oldspec->value = timespec_subtract(timer->end, now);\n        oldspec->interval = timer->interval;\n    }\n\n    timer->start = now;\n    timer->end = timespec_add(timer->start, spec.value);\n    timer->interval = spec.interval;\n    timer->active = !timespec_is_zero(spec.value);\n    if (timer->thread_running) {\n        pthread_kill(timer->thread, SIGUSR1);\n    } else if (timer->active) {\n        timer->thread_running = true;\n        pthread_create(&timer->thread, NULL, timer_thread, timer);\n        pthread_detach(timer->thread);\n    }\n    unlock(&timer->lock);\n    return 0;\n}\n"
  },
  {
    "path": "util/timer.h",
    "content": "#ifndef UTIL_TIMER_H\n#define UTIL_TIMER_H\n\n#include <stdbool.h>\n#include <time.h>\n#include <pthread.h>\n#include <assert.h>\n#include \"util/sync.h\"\n\nstatic inline struct timespec timespec_now(clockid_t clockid) {\n    assert(clockid == CLOCK_MONOTONIC || clockid == CLOCK_REALTIME);\n    struct timespec now;\n    clock_gettime(clockid, &now); // can't fail, according to posix spec\n    return now;\n}\n\nstatic inline struct timespec timespec_add(struct timespec x, struct timespec y) {\n    x.tv_sec += y.tv_sec;\n    x.tv_nsec += y.tv_nsec;\n    if (x.tv_nsec >= 1000000000) {\n        x.tv_nsec -= 1000000000;\n        x.tv_sec++;\n    }\n    return x;\n}\n\nstatic inline struct timespec timespec_subtract(struct timespec x, struct timespec y) {\n    struct timespec result;\n    if (x.tv_nsec < y.tv_nsec) {\n        x.tv_sec -= 1;\n        x.tv_nsec += 1000000000;\n    }\n    result.tv_sec = x.tv_sec - y.tv_sec;\n    result.tv_nsec = x.tv_nsec - y.tv_nsec;\n    return result;\n}\n\nstatic inline bool timespec_is_zero(struct timespec ts) {\n    return ts.tv_sec == 0 && ts.tv_nsec == 0;\n}\n\nstatic inline bool timespec_positive(struct timespec ts) {\n    return ts.tv_sec > 0 || (ts.tv_sec == 0 && ts.tv_nsec > 0);\n}\n\nstatic inline struct timespec timespec_normalize(struct timespec ts) {\n    ts.tv_sec += ts.tv_nsec / 1000000000;\n    ts.tv_nsec %= 1000000000;\n    return ts;\n}\n\ntypedef void (*timer_callback_t)(void *data);\nstruct timer {\n    clockid_t clockid;\n    struct timespec start;\n    struct timespec end;\n    struct timespec interval;\n\n    bool active;\n    bool thread_running;\n    pthread_t thread;\n    timer_callback_t callback;\n    void *data;\n    lock_t lock;\n\n    bool dead; // set by timer_free, the thread will free the timer if this is set when it finishes\n};\n\nstruct timer *timer_new(clockid_t clockid, timer_callback_t callback, void *data);\nvoid timer_free(struct timer *timer);\n// value is how long to wait until the next fire\n// interval is how long after that to wait until the next fire (if non-zero)\n// bizarre interface is based off setitimer, because this is going to be used\n// to implement setitimer\nstruct timer_spec {\n    struct timespec value;\n    struct timespec interval;\n};\nint timer_set(struct timer *timer, struct timer_spec spec, struct timer_spec *oldspec);\n\n#endif\n"
  },
  {
    "path": "vdso/check-cc.sh",
    "content": "#!/bin/sh\ncc=$1\ntest_c=$(mktemp)\ncat > $test_c <<END\n#if !defined(__i386__) && !defined(__ELF__)\n#error \"__i386__ or __ELF__ is not defined\"\n#endif\nEND\ncmd=\"$cc -target i386-linux -fuse-ld=lld -shared -nostdlib -x c $test_c -o /dev/null\"\necho $ $cmd\n$cmd\nstatus=$?\nexit $status\n"
  },
  {
    "path": "vdso/meson.build",
    "content": "# The VDSO gets inserted into the address space of emulated processes, so it\n# needs to be compiled as an i386 ELF shared library. This requires a cross\n# compiler and linker on most of the platforms that matter (Mac, 64-bit Linux,\n# Raspberry Pi). Clang and LLD are easy to install on all of these platforms,\n# so that's currently the only supported option (though if you have some other\n# compatible toolchain you can edit this file to point to it instead.)\n\n# Default install paths for Homebrew on Intel, Homebrew on Apple silicon, and MacPorts, respectively.\nclang = find_program('/usr/local/opt/llvm/bin/clang', '/opt/homebrew/opt/llvm/bin/clang', '/opt/local/bin/clang', 'clang')\ncheck_cc = find_program('check-cc.sh')\nresult = run_command(check_cc, clang, check: false)\nif result.returncode() != 0\n    message('\\n' + result.stdout() + result.stderr())\n    if build_machine.system() == 'darwin'\n        install_msg = 'Install LLVM through Homebrew (brew install llvm)'\n    elif build_machine.system() == 'linux'\n        install_msg = 'Install Clang and LLD using your package manager (e.g. sudo apt install clang lld)'\n    endif\n    error('Could not find usable VDSO compiler. ' + install_msg)\nendif\nvdso_compiler = [clang, '-target', 'i386-linux', '-fuse-ld=lld']\n\nvdso = custom_target('vdso', input: ['vdso.S', 'vdso.c', 'vdso.lds'], output: 'libvdso.so.elf',\n    command: vdso_compiler + ['-o', '@OUTPUT@', '@INPUT0@', '@INPUT1@',\n        '-nostdlib', '-Wl,-T,@INPUT2@', '-Wl,--hash-style,sysv', '-shared', '-fPIC']\n        + get_option('vdso_c_args').split())\n"
  },
  {
    "path": "vdso/note.S",
    "content": ".pushsection .note.Linux, \"\",@note\n    .balign 4\n    .long after_name - name // namesz\n    .long after_note - note // descz\n    .long 0\nname:\n    .asciz \"Linux\"\nafter_name:\n    .balign 4\nnote:\n    .long (4 << 16) | (20 << 8) | 69 // 4.20.69\nafter_note:\n    .balign 4\n.popsection\n"
  },
  {
    "path": "vdso/vdso.S",
    "content": "    .text\n    .global __kernel_vsyscall\n    .type __kernel_vsyscall,@function\n__kernel_vsyscall:\n    int $0x80\n    ret\n\n    .global __kernel_sigreturn\n    .type __kernel_sigreturn,@function\n__kernel_sigreturn:\n    popl %ebx // I have no idea what this is for but apparently it needs unwind info?\n    mov $119, %eax // NR_sigreturn\n    int $0x80\n    nop\n\n    .global __kernel_rt_sigreturn\n    .type __kernel_rt_sigreturn,@function\n__kernel_rt_sigreturn:\n    mov $173, %eax // NR_rt_sigreturn\n    int $0x80\n    nop\n"
  },
  {
    "path": "vdso/vdso.c",
    "content": "#if !__i386__ || !__ELF__\n#error \"VDSO must be built for i386 elf\"\n#endif\n\ntypedef long time_t;\ntypedef int clockid_t;\n\ntime_t __vdso_time(time_t *t) {\n    time_t result;\n    __asm__(\"int $0x80\" : \"=a\" (result) :\n            \"0\" (13 /* __NR_time */), \"b\" (t));\n    return result;\n}\n\nint __vdso_gettimeofday(void *timeval, void *timezone) {\n    int result;\n    __asm__(\"int $0x80\" : \"=a\" (result) :\n            \"0\" (78 /* __NR_gettimeofday */), \"b\" (timeval), \"c\" (timezone));\n    return result;\n}\n\nint __vdso_clock_gettime(clockid_t clock, void *timespec) {\n    int result;\n    __asm__(\"int $0x80\" : \"=a\" (result) :\n            \"0\" (265 /* __NR_clock_gettime */), \"b\" (clock), \"c\" (timespec));\n    return result;\n}\n\n"
  },
  {
    "path": "vdso/vdso.lds",
    "content": "ENTRY(__kernel_vsyscall);\n\nVERSION {\n    LINUX_2.5 {\n    global:\n        __kernel_vsyscall;\n        __kernel_sigreturn;\n        __kernel_rt_sigreturn;\n    local: *;\n    };\n\n    LINUX_2.6 {\n    global:\n        __vdso_clock_gettime;\n        __vdso_gettimeofday;\n        __vdso_time;\n    };\n}\n\nSECTIONS {\n    . = SIZEOF_HEADERS;\n\n\t.hash          : {*(.hash)}            :text\n    .dynsym        : {*(.dynsym)}\n    .dynstr        : {*(.dynstr)}\n    .gnu.version   : {*(.gnu.version)}\n    .gnu.version_d : {*(.gnu.version_d)}\n    .gnu.version_r : {*(.gnu.version_r)}\n\n    .dynamic       : {*(.dynamic)}         :text :dynamic\n\n    .rodata        : {\n\t\t*(.rodata*)\n\t\t*(.data*)\n\t\t*(.sdata*)\n\t\t*(.got.plt) *(.got)\n\t\t*(.gnu.linkonce.d.*)\n\t\t*(.bss*)\n\t\t*(.dynbss*)\n\t\t*(.gnu.linkonce.b.*)\n    }                                      :text\n    .note          : {*(.note.*)}          :text :note\n\n    .eh_frame_hdr  : {*(.eh_frame_hdr)}    :text :eh_frame_hdr\n    .eh_frame      : {KEEP (*(.eh_frame))} :text\n\n    .text          : {*(.text*)}           :text\n\n    /DISCARD/ : {\n        *(.debug*)\n        *(.comment)\n    }\n}\n\nPHDRS {\n\ttext         PT_LOAD    FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */\n\tdynamic      PT_DYNAMIC FLAGS(4);               /* PF_R */\n\tnote         PT_NOTE    FLAGS(4);               /* PF_R */\n\teh_frame_hdr PT_GNU_EH_FRAME;\n}\n"
  },
  {
    "path": "xX_main_Xx.h",
    "content": "#include <string.h>\n#include <unistd.h>\n#include <signal.h>\n#include <errno.h>\n#include <syslog.h>\n#include \"kernel/init.h\"\n#include \"kernel/fs.h\"\n#include \"fs/devices.h\"\n#include \"fs/real.h\"\n#ifdef __APPLE__\n#include <sys/resource.h>\n#define IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY 1\n#define IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE 1\n#endif\n\nvoid real_tty_reset_term(void);\n\nstatic void exit_handler(struct task *task, int code) {\n    if (task->parent != NULL)\n        return;\n    real_tty_reset_term();\n    if (code & 0xff)\n        raise(code & 0xff);\n    exit(code >> 8);\n}\n\n// this function parses command line arguments and initializes global\n// data structures. thanks programming discussions discord server for the name.\n// https://discord.gg/9zT7NHP\nstatic inline int xX_main_Xx(int argc, char *const argv[], const char *envp) {\n#ifdef __APPLE__\n    // Enable case-sensitive filesystem mode on macOS, if possible.\n    // In order for this to succeed, either we need to be running as root, or\n    // be given the com.apple.private.iopol.case_sensitivity entitlement. The\n    // second option isn't possible so you'll need to give iSH the setuid root\n    // bit. In that case it's important to drop root permissions ASAP.\n    // https://worthdoingbadly.com/casesensitive-iossim/\n    int iopol_err = setiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY,\n            IOPOL_SCOPE_PROCESS,\n            IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE);\n    if (iopol_err != 0 && errno != EPERM)\n        perror(\"could not enable case sensitivity\");\n    setgid(getgid());\n    setuid(getuid());\n#endif\n\n    // parse cli options\n    int opt;\n    const char *root = NULL;\n    const char *workdir = NULL;\n    const struct fs_ops *fs = &realfs;\n    const char *console = \"/dev/tty1\";\n    while ((opt = getopt(argc, argv, \"+r:f:d:c:\")) != -1) {\n        switch (opt) {\n            case 'r':\n            case 'f':\n                root = optarg;\n                if (opt == 'f')\n                    fs = &fakefs;\n                break;\n            case 'd':\n                workdir = optarg;\n                break;\n            case 'c':\n                console = optarg;\n                break;\n\n        }\n    }\n\n    openlog(argv[0], 0, LOG_USER);\n\n    char root_realpath[MAX_PATH + 1] = \"/\";\n    if (root != NULL && realpath(root, root_realpath) == NULL) {\n        perror(root);\n        exit(1);\n    }\n    if (fs == &fakefs)\n        strcat(root_realpath, \"/data\");\n    int err = mount_root(fs, root_realpath);\n    if (err < 0)\n        return err;\n\n    become_first_process();\n    current->thread = pthread_self();\n    char cwd[MAX_PATH + 1];\n    if (root == NULL && workdir == NULL) {\n        getcwd(cwd, sizeof(cwd));\n        workdir = cwd;\n    }\n    if (workdir != NULL) {\n        struct fd *pwd = generic_open(workdir, O_RDONLY_, 0);\n        if (IS_ERR(pwd)) {\n            fprintf(stderr, \"error opening working dir: %ld\\n\", PTR_ERR(pwd));\n            return 1;\n        }\n        fs_chdir(current->fs, pwd);\n    }\n\n    char argv_copy[4096];\n    int i = optind;\n    size_t p = 0;\n    while (i < argc) {\n        strcpy(&argv_copy[p], argv[i]);\n        p += strlen(argv[i]) + 1;\n        i++;\n    }\n    argv_copy[p] = '\\0';\n    if (argv[optind] == NULL)\n\t    return _ENOENT;\n    err = do_execve(argv[optind], argc - optind, argv_copy, envp == NULL ? \"\\0\" : envp);\n    if (err < 0)\n        return err;\n    tty_drivers[TTY_CONSOLE_MAJOR] = &real_tty_driver;\n    if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {\n        err = create_stdio(console, TTY_CONSOLE_MAJOR, 1);\n        if (err < 0)\n            return err;\n    } else {\n        err = create_piped_stdio();\n        if (err < 0)\n            return err;\n    }\n    exit_hook = exit_handler;\n    return 0;\n}\n"
  }
]