pax_global_header00006660000000000000000000000064147304616330014521gustar00rootroot0000000000000052 comment=9e0790c5060c5346039f92f4c9d97a2dbc7316ed luasystem-0.4.5/000077500000000000000000000000001473046163300135555ustar00rootroot00000000000000luasystem-0.4.5/.busted000066400000000000000000000001361473046163300150440ustar00rootroot00000000000000return { default = { verbose = true, coverage = false, output = "gtest", }, } luasystem-0.4.5/.editorconfig000066400000000000000000000005331473046163300162330ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 [*.c;*.h] indent_style = space indent_size = 4 [*.lua] indent_style = space indent_size = 2 [*.rockspec] indent_style = space indent_size = 2 [*.md] indent_style = space indent_size = 2 [Makefile] indent_style = tab indent_size = 4 luasystem-0.4.5/.github/000077500000000000000000000000001473046163300151155ustar00rootroot00000000000000luasystem-0.4.5/.github/workflows/000077500000000000000000000000001473046163300171525ustar00rootroot00000000000000luasystem-0.4.5/.github/workflows/build.yml000066400000000000000000000065401473046163300210010ustar00rootroot00000000000000name: "Build" concurrency: # for PR's cancel the running task, if another commit is pushed group: ${{ github.workflow }} ${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} on: # build on PR and push-to-main. This works for short-lived branches, and saves # CPU cycles on duplicated tests. # For long-lived branches that diverge, you'll want to run on all pushes, not # just on push-to-main. pull_request: {} push: branches: - master jobs: test: strategy: fail-fast: false matrix: os: ['ubuntu-20.04', 'macos-13'] luaVersion: - "5.1" - "5.2" - "5.3" - "5.4" - "luajit" - "luajit-openresty" include: - os: "macos-latest" luaVersion: "5.4" # On Windows builds: # 'hishamhm/gh-actions-lua' will build the PuC Rio Lua versions using MSVC, and # the LuaJIT version using MinGW/gcc. By running against both below, we test # both toolchains. - os: "windows-latest" toolchain: "msvc" luaVersion: "5.1" - os: "windows-latest" toolchain: "msvc" luaVersion: "5.2" - os: "windows-latest" toolchain: "msvc" luaVersion: "5.3" - os: "windows-latest" toolchain: "msvc" luaVersion: "5.4" - os: "windows-latest" toolchain: "mingw" # unused, other than for display in the UI luaVersion: "luajit" runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@master - name: Setup MSVC # the 'hishamhm/gh-actions-lua' step requires msvc to build PuC Rio Lua # versions on Windows (LuaJIT will be build using MinGW/gcc). if: ${{ matrix.toolchain == 'msvc' }} uses: ilammy/msvc-dev-cmd@v1 # - name: install Dependencies analyzer # # action step used for troubleshooting if Windows dll's build incorrectly # run: | # $version = "1.11.1" # echo "Installing Dependencies version: $version" # $url = 'https://github.com/lucasg/Dependencies/releases/download/v' + $version + '/Dependencies_x64_Release.zip' # $dest = Join-Path -Path $PWD -ChildPath ".dependencies" # # Download and extract Dependencies # New-Item -ItemType Directory -Path "$dest" # Invoke-WebRequest -Uri $url -OutFile "$dest\dependencies.zip" # Expand-Archive -Path "$dest\dependencies.zip" -DestinationPath "$dest" # Remove-Item -Path "$dest\dependencies.zip" # # dir "$dest" # # Add Dependencies to PATH # $env:PATH += ";$dest" # echo $env:PATH # # Verify Dependencies Installation # dir "$dest\*.exe" # dir ".\.dependencies\Dependencies.exe" # .\.dependencies\Dependencies.exe -help - uses: hishamhm/gh-actions-lua@master with: luaVersion: ${{ matrix.luaVersion }} - uses: hishamhm/gh-actions-luarocks@master with: luarocksVersion: "3.11.0" - name: install run: | luarocks make - name: test dependencies run: | luarocks install busted - name: test run: | busted --exclude-tags=manual --Xoutput "--color" luasystem-0.4.5/.github/workflows/deploy.yml000066400000000000000000000023321473046163300211710ustar00rootroot00000000000000name: Deploy on: [ push, workflow_dispatch ] jobs: affected: uses: lunarmodules/.github/.github/workflows/list_affected_rockspecs.yml@main build: needs: affected if: ${{ needs.affected.outputs.rockspecs }} uses: lunarmodules/.github/.github/workflows/test_build_rock.yml@main with: rockspecs: ${{ needs.affected.outputs.rockspecs }} upload: needs: [ affected, build ] # Only run upload if: # 1. We are on the canonical repository (no uploads from forks) # 2. The current commit is either tagged or on the default branch (the workflow will upload dev/scm rockspecs any # time they are touched, tagged ones whenever the edited rockspec and tag match) # 3. Some rockspecs were changed — this implies the commit changing the rockspec is the same one that gets tagged if: >- ${{ github.repository == 'lunarmodules/luasystem' && ( github.ref_name == 'master' || startsWith(github.ref, 'refs/tags/') ) && needs.affected.outputs.rockspecs }} uses: lunarmodules/.github/.github/workflows/upload_to_luarocks.yml@main with: rockspecs: ${{ needs.affected.outputs.rockspecs }} secrets: apikey: ${{ secrets.LUAROCKS_APIKEY }} luasystem-0.4.5/.github/workflows/luacheck.yml000066400000000000000000000003311473046163300214510ustar00rootroot00000000000000name: Luacheck on: [push, pull_request] jobs: luacheck: runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Luacheck uses: lunarmodules/luacheck@v1 luasystem-0.4.5/.gitignore000066400000000000000000000006441473046163300155510ustar00rootroot00000000000000# Compiled Lua sources luac.out # LuaCov files *.report.out *.stats.out # luarocks build files *.rock *.zip *.tar.gz # Object files *.o *.os *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo *.def *.exp # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # VIM files **/.*.swp **/.*.swo # VS Code files .vscode/ luasystem-0.4.5/.luacheckrc000066400000000000000000000007021473046163300156610ustar00rootroot00000000000000unused_args = false redefined = false max_line_length = false globals = { "win_it", "nix_it", } not_globals = { -- deprecated Lua 5.0 functions "string.len", "table.getn", } include_files = { "**/*.lua", "**/*.rockspec", ".busted", ".luacheckrc", } files["spec/**/*.lua"] = { std = "+busted", } exclude_files = { -- The Github Actions Lua Environment ".lua", ".luarocks", ".install", } luasystem-0.4.5/CHANGELOG.md000066400000000000000000000066551473046163300154020ustar00rootroot00000000000000# CHANGELOG ## Versioning This library is versioned based on Semantic Versioning ([SemVer](https://semver.org/)). #### Version scoping The scope of what is covered by the version number excludes: - error messages; the text of the messages can change, unless specifically documented. #### Releasing new versions - create a release branch - update the changelog below - update version and copyright-years in `./LICENSE.md` and `./src/core.c` (in module constants) - create a new rockspec and update the version inside the new rockspec:
`cp luasystem-scm-0.rockspec ./rockspecs/luasystem-X.Y.Z-1.rockspec` - clean and render the docs: run `ldoc .` - commit the changes as `Release vX.Y.Z` - push the commit, and create a release PR - after merging tag the release commit with `vX.Y.Z` - upload to LuaRocks:
`luarocks upload ./rockspecs/luasystem-X.Y.Z-1.rockspec --api-key=ABCDEFGH` - test the newly created rock:
`luarocks install luasystem` ## Version history ### version 0.4.5, released 18-Dec-2024 - Fix: suppress a warning when building with clang - Fix: do not rely on luaconf.h to include limits.h, fixes builds with latest LuaJIT (#38). ### version 0.4.4, released 03-Sep-2024 - Fix: include all objects in Makefile ### version 0.4.3, released 28-Aug-2024 - Chore: add compiler error on Windows if Virtual Terminal Processing is unavailable. - Fix: fix the freebsd build ### Version 0.4.2, released 25-Jun-2024 - Fix: include additional headers for some MinGW installations ### Version 0.4.1, released 25-Jun-2024 - Fix: when compiling with `msys2` the `conio.h` header is required ### Version 0.4.0, released 20-Jun-2024 - Feat: `getconsoleflags` and `setconsoleflags` for getting/setting the current console configuration flags on Windows - Feat: `getconsolecp` and `setconsolecp` for getting/setting the console codepage on Windows - Feat: `getconsoleoutputcp` and `setconsoleoutputcp` for getting/setting the console output codepage on Windows - Feat: `tcgetattr` and `tcsetattr` for getting/setting the current console configuration flags on Posix - Feat: `getnonblock` and `setnonblock` for getting/setting the non-blocking flag on Posix - Feat: `bitflags`: a support feature for the above flag type controls to facilitate bit manipulation without resorting to binary operations (to also support PuC Lua 5.1) - Feat: `readkey` reads a keyboard input from `stdin` in a non-blocking way (utf8, also on Windows) - Feat: `readansi` reads a keyboard input from `stdin` in a non-blocking way, parses ansi and utf8 sequences - Feat: `termsize` gets the current terminal size in rows and columns - Feat: `utf8cwidth` and `utf8swidth` for getting the display width (in columns) of respectively a single utf8 character, or a utf8 string - Feat: helpers; `termbackup`, `termrestore`, `autotermrestore`, and `termwrap` for managing the many terminal settings on all platforms. ### Version 0.3.0, released 15-Dec-2023 - Feat: on Windows `sleep` now has a precision parameter - Feat: `setenv` added to set environment variables. - Feat: `getenvs` added to list environment variables. - Feat: `getenv` added to get environment variable previously set (Windows). - Feat: `random` added to return high-quality random bytes - Feat: `isatty` added to check if a file-handle is a tty ### Version 0.2.1, released 02-Oct-2016 ### Version 0.2.0, released 08-May-2016 ### Version 0.1.1, released 10-Apr-2016 ### Version 0.1.0, released 11-Feb-2016 - initial release luasystem-0.4.5/LICENSE.md000066400000000000000000000021551473046163300151640ustar00rootroot00000000000000# MIT License ### Copyright (c) 2016-2024, Oscar Lim ### Copyright (c) 2024, the luasystem project authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. luasystem-0.4.5/Makefile000066400000000000000000000021141473046163300152130ustar00rootroot00000000000000# busted_time makefile # # see csrc/makefile for description of how to customize the build # # Targets: # install install system independent support # install-all install for lua51 lua52 lua53 # print print the build settings ifeq ($(origin PLAT),undefined) UNAME_S:=$(shell uname -s) ifeq ($(UNAME_S),Linux) PLAT=linux endif ifeq ($(UNAME_S),Darwin) PLAT=macosx endif ifeq ($(UNAME_S),FreeBSD) PLAT=freebsd endif ifeq ($(patsubst MINGW%,MINGW,$(UNAME_S)),MINGW) PLAT=mingw endif endif PLAT?= linux PLATS= macosx linux win32 mingw freebsd all: $(PLAT) $(PLATS) none install local clean: $(MAKE) -C src $@ print: $(MAKE) -C src $@ install-all: $(MAKE) clean @cd src && $(MAKE) $(PLAT) LUA_VERSION=5.1 @cd src && $(MAKE) install LUA_VERSION=5.1 $(MAKE) clean @cd src && $(MAKE) $(PLAT) LUA_VERSION=5.2 @cd src && $(MAKE) install LUA_VERSION=5.2 $(MAKE) clean @cd src && $(MAKE) $(PLAT) LUA_VERSION=5.3 @cd src && $(MAKE) install LUA_VERSION=5.3 .PHONY: test test: busted .PHONY: lint lint: luacheck . .PHONY: docs docs: ldoc . luasystem-0.4.5/README.md000066400000000000000000000015241473046163300150360ustar00rootroot00000000000000[![Build](https://img.shields.io/github/actions/workflow/status/lunarmodules/luasystem/build.yml?branch=master&label=Unix/Win/Mac%20Build)](https://github.com/lunarmodules/luasystem/actions/workflows/build.yml) [![Lint](https://github.com/lunarmodules/luasystem/workflows/Lint/badge.svg)](https://github.com/lunarmodules/luasystem/actions/workflows/lint.yml) [![SemVer](https://img.shields.io/github/v/tag/lunarmodules/luasystem?color=brightgreen&label=SemVer&logo=semver&sort=semver)](CHANGELOG.md) # LuaSystem luasystem is a platform independent system call library for Lua. Supports Unix, Windows, MacOS, `Lua >= 5.1` and `luajit >= 2.0.0`. ## License and copyright See [LICENSE.md](LICENSE.md) ## Documentation See [online documentation](https://lunarmodules.github.io/luasystem/) ## Changelog & Versioning See [CHANGELOG.md](CHANGELOG.md) luasystem-0.4.5/config.ld000066400000000000000000000005311473046163300153420ustar00rootroot00000000000000project='Lua-System' title='Lua-System docs' description='Platform independent system calls for Lua' format='markdown' use_markdown_titles = true style="./doc_topics/" file={'./src/', './system/'} topics={'./doc_topics/', './LICENSE.md', './CHANGELOG.md'} examples = {'./examples'} dir='docs' sort=true sort_modules=true all=false merge=true luasystem-0.4.5/doc_topics/000077500000000000000000000000001473046163300157035ustar00rootroot00000000000000luasystem-0.4.5/doc_topics/01-introduction.md000066400000000000000000000010411473046163300211600ustar00rootroot00000000000000# 1. Introduction luasystem is a platform independent system call library for Lua. Supports Unix, Windows, MacOS, `Lua >= 5.1` and `luajit >= 2.0.0`. Lua is typically platform independent, but it requires adhering to very old C standards. This in turn means that many common features (according to todays standards) are not available. This module attempts to overcome some of those hurdles by providing functions that cover those common needs. This is not a kitchen sink library, but a minimalistic one with a focus on platform independence. luasystem-0.4.5/doc_topics/02-development.md000066400000000000000000000007601473046163300207710ustar00rootroot00000000000000# 2. Development Some tests cannot be run in CI becasue they test system specifics that simply do not exist in a CI environment. An example is the `isatty` function Which cannot be set to return a truthy value in CI. The tests concerned are all labelled with `#manual`. And in CI they will be skipped because `--exclude-tags=manual` is being passed to the `busted` command line. Hence if tests like these are being added, then please ensure the tests pass locally, and do not rely on CI only. luasystem-0.4.5/doc_topics/03-terminal.md000066400000000000000000000130421473046163300202600ustar00rootroot00000000000000# 3. Terminal functionality Terminals are fundamentally different on Windows and Posix. So even though `luasystem` provides primitives to manipulate both the Windows and Posix terminals, the user will still have to write platform specific code. To mitigate this a little, all functions are available on all platforms. They just will be a no-op if invoked on another platform. This means that no platform specific branching is required (but still possible) in user code. The user must simply set up both platforms to make it work. ## 3.1 Backup and Restore terminal settings Since there are a myriad of settings available; - `system.setconsoleflags` (Windows) - `system.setconsolecp` (Windows) - `system.setconsoleoutputcp` (Windows) - `system.setnonblock` (Posix) - `system.tcsetattr` (Posix) Some helper functions are available to backup and restore them all at once. See `termbackup`, `termrestore`, `autotermrestore` and `termwrap`. ## 3.1 Terminal ANSI sequences Windows is catching up with this. In Windows 10 (since 2019), the Windows Terminal application (not to be mistaken for the `cmd` console application) supports ANSI sequences. However this might not be enabled by default. ANSI processing can be set up both on the input (key sequences, reading cursor position) as well as on the output (setting colors and cursor shapes). To enable it use `system.setconsoleflags` like this: -- setup Windows console to handle ANSI processing on output sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) sys.setconsoleflags(io.stderr, sys.getconsoleflags(io.stderr) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) -- setup Windows console to handle ANSI processing on input sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT) ## 3.2 UTF-8 in/output and display width ### 3.2.1 UTF-8 in/output Where (most) Posix systems use UTF-8 by default, Windows internally uses UTF-16. More recent versions of Lua also have UTF-8 support. So `luasystem` also focusses on UTF-8. On Windows UTF-8 output can be enabled by setting the output codepage like this: -- setup Windows output codepage to UTF-8 sys.setconsoleoutputcp(sys.CODEPAGE_UTF8) Terminal input is handled by the [`_getwchar()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getchar-getwchar) function on Windows which returns UTF-16 surrogate pairs. `luasystem` will automatically convert those to UTF-8. So when using `readkey` or `readansi` to read keyboard input no additional changes are required. ### 3.2.2 UTF-8 display width Typical western characters and symbols are single width characters and will use only a single column when displayed on a terminal. However many characters from other languages/cultures or emojis require 2 columns for display. Typically the `wcwidth` function is used on Posix to check the number of columns required for display. However since Windows doesn't provide this functionality a custom implementation is included based on [the work by Markus Kuhn](http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c). 2 functions are provided, `system.utf8cwidth` for a single character, and `system.utf8swidth` for a string. When writing terminal applications the display width is relevant to positioning the cursor properly. For an example see the [`examples/readline.lua`](../examples/readline.lua.html) file. ## 3.3 reading keyboard input ### 3.3.1 Non-blocking There are 2 functions for keyboard input (actually 3, if taking `system._readkey` into account): `readkey` and `readansi`. `readkey` is a low level function and should preferably not be used, it returns a byte at a time, and hence can leave stray/invalid byte sequences in the buffer if only the start of a UTF-8 or ANSI sequence is consumed. The preferred way is to use `readansi` which will parse and return entire characters in single or multiple bytes, or a full ANSI sequence. On Windows the input is read using [`_getwchar()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getchar-getwchar) which bypasses the terminal and reads the input directly from the keyboard buffer. This means however that the character is also not being echoed to the terminal (independent of the echo settings used with `system.setconsoleflags`). On Posix the traditional file approach is used, which: - is blocking by default - echoes input to the terminal - requires enter to be pressed to pass the input (canonical mode) To use non-blocking input here's how to set it up: -- setup Windows console to disable echo and line input (not required since _getwchar is used, just for consistency) sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT) -- setup Posix by disabling echo, canonical mode, and making non-blocking local of_attr = sys.tcgetattr(io.stdin) sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, }) sys.setnonblock(io.stdin, true) Both functions require a timeout to be provided which allows for proper asynchronous code to be written. Since the underlying sleep method used is `system.sleep`, just patching that function with a coroutine based yielding one should be all that is needed to make the result work with asynchroneous coroutine schedulers. ### 3.3.2 Blocking input When using traditional input method like `io.stdin:read()` (which is blocking) the echo and newline properties should be set on Windows similar to Posix. For an example see [`examples/password_input.lua`](../examples/password_input.lua.html). luasystem-0.4.5/doc_topics/ldoc.css000066400000000000000000000132631473046163300173430ustar00rootroot00000000000000body { color: #47555c; font-size: 16px; font-family: "Open Sans", sans-serif; margin: 0; background: #eff4ff; } a:link { color: #008fee; } a:visited { color: #008fee; } a:hover { color: #22a7ff; } h1 { font-size:26px; font-weight: normal; } h2 { font-size:22px; font-weight: normal; } h3 { font-size:18px; font-weight: normal; } h4 { font-size:16px; font-weight: bold; } hr { height: 1px; background: #c1cce4; border: 0px; margin: 15px 0; } code, tt { font-family: monospace; } span.parameter { font-family: monospace; font-weight: bold; color: rgb(99, 115, 131); } span.parameter:after { content:":"; } span.types:before { content:"("; } span.types:after { content:")"; } .type { font-weight: bold; font-style:italic } p.name { font-family: "Andale Mono", monospace; } #navigation { float: left; background-color: white; border-right: 1px solid #d3dbec; border-bottom: 1px solid #d3dbec; width: 14em; vertical-align: top; overflow: visible; } #navigation br { display: none; } #navigation h1 { background-color: white; border-bottom: 1px solid #d3dbec; padding: 15px; margin-top: 0px; margin-bottom: 0px; } #navigation h2 { font-size: 18px; background-color: white; border-bottom: 1px solid #d3dbec; padding-left: 15px; padding-right: 15px; padding-top: 10px; padding-bottom: 10px; margin-top: 30px; margin-bottom: 0px; } #content h1 { background-color: #2c3e67; color: white; padding: 15px; margin: 0px; } #content h2 { background-color: #6c7ea7; color: white; padding: 15px; padding-top: 15px; padding-bottom: 15px; margin-top: 0px; } #content h2 a { background-color: #6c7ea7; color: white; text-decoration: none; } #content h2 a:hover { text-decoration: underline; } #content h3 { font-style: italic; padding-top: 15px; padding-bottom: 4px; margin-right: 15px; margin-left: 15px; margin-bottom: 5px; border-bottom: solid 1px #bcd; } #content h4 { margin-right: 15px; margin-left: 15px; border-bottom: solid 1px #bcd; } #content pre { margin: 15px; } pre { background-color: rgb(50, 55, 68); color: white; border-radius: 3px; /* border: 1px solid #C0C0C0; /* silver */ padding: 15px; overflow: auto; font-family: "Andale Mono", monospace; } #content ul pre.example { margin-left: 0px; } table.index { /* border: 1px #00007f; */ } table.index td { text-align: left; vertical-align: top; } #navigation ul { font-size:1em; list-style-type: none; margin: 1px 1px 10px 1px; padding-left: 20px; } #navigation li { text-indent: -1em; display: block; margin: 3px 0px 0px 22px; } #navigation li li a { margin: 0px 3px 0px -1em; } #content { margin-left: 14em; } #content p { padding-left: 15px; padding-right: 15px; } #content table { padding-left: 15px; padding-right: 15px; background-color: white; } #content p, #content table, #content ol, #content ul, #content dl { max-width: 900px; } #about { padding: 15px; padding-left: 16em; background-color: white; border-top: 1px solid #d3dbec; border-bottom: 1px solid #d3dbec; } table.module_list, table.function_list { border-width: 1px; border-style: solid; border-color: #cccccc; border-collapse: collapse; margin: 15px; } table.module_list td, table.function_list td { border-width: 1px; padding-left: 10px; padding-right: 10px; padding-top: 5px; padding-bottom: 5px; border: solid 1px rgb(193, 204, 228); } table.module_list td.name, table.function_list td.name { background-color: white; min-width: 200px; border-right-width: 0px; } table.module_list td.summary, table.function_list td.summary { background-color: white; width: 100%; border-left-width: 0px; } dl.function { margin-right: 15px; margin-left: 15px; border-bottom: solid 1px rgb(193, 204, 228); border-left: solid 1px rgb(193, 204, 228); border-right: solid 1px rgb(193, 204, 228); background-color: white; } dl.function dt { color: rgb(99, 123, 188); font-family: monospace; border-top: solid 1px rgb(193, 204, 228); padding: 15px; } dl.function dd { margin-left: 15px; margin-right: 15px; margin-top: 5px; margin-bottom: 15px; } #content dl.function dd h3 { margin-top: 0px; margin-left: 0px; padding-left: 0px; font-size: 16px; color: rgb(128, 128, 128); border-bottom: solid 1px #def; } #content dl.function dd ul, #content dl.function dd ol { padding: 0px; padding-left: 15px; list-style-type: none; } ul.nowrap { overflow:auto; white-space:nowrap; } .section-description { padding-left: 15px; padding-right: 15px; } /* stop sublists from having initial vertical space */ ul ul { margin-top: 0px; } ol ul { margin-top: 0px; } ol ol { margin-top: 0px; } ul ol { margin-top: 0px; } /* make the target distinct; helps when we're navigating to a function */ a:target + * { background-color: #FF9; } /* styles for prettification of source */ pre .comment { color: #bbccaa; } pre .constant { color: #a8660d; } pre .escape { color: #844631; } pre .keyword { color: #ffc090; font-weight: bold; } pre .library { color: #0e7c6b; } pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } pre .string { color: #8080ff; } pre .number { color: #f8660d; } pre .operator { color: #2239a8; font-weight: bold; } pre .preprocessor, pre .prepro { color: #a33243; } pre .global { color: #c040c0; } pre .user-keyword { color: #800080; } pre .prompt { color: #558817; } pre .url { color: #272fc2; text-decoration: underline; } luasystem-0.4.5/docs/000077500000000000000000000000001473046163300145055ustar00rootroot00000000000000luasystem-0.4.5/docs/classes/000077500000000000000000000000001473046163300161425ustar00rootroot00000000000000luasystem-0.4.5/docs/classes/bitflags.html000066400000000000000000000321201473046163300206210ustar00rootroot00000000000000 Lua-System docs

Class bitflags

Bitflags module.

The bitflag object makes it easy to manipulate flags in a bitmask.

It has metamethods that do the hard work, adding flags sets them, substracting unsets them. Comparing flags checks if all flags in the second set are also set in the first set. The has method checks if all flags in the second set are also set in the first set, but behaves slightly different.

Indexing allows checking values or setting them by bit index (eg. 0-7 for flags in the first byte).

NOTE: unavailable flags (eg. Windows flags on a Posix system) should not be omitted, but be assigned a value of 0. This is because the has method will return false if the flags are checked and the value is 0.

See system.bitflag (the constructor) for extensive examples on usage.

Bit flags

bitflag:has_all_of (subset) Checks if all the flags in the given subset are set.
bitflag:has_any_of (subset) Checks if any of the flags in the given subset are set.
bitflag:value () Retrieves the numeric value of the bitflag object.
system.bitflag ([value=0]) Creates a new bitflag object from the given value.


Bit flags

Bitflag objects can be used to easily manipulate and compare bit flags. These are primarily for use with the terminal functions, but can be used in other places as well.
bitflag:has_all_of (subset)
Checks if all the flags in the given subset are set. If the flags to check has a value 0, it will always return false. So if there are flags that are unsupported on a platform, they can be set to 0 and the has_all_of function will return false if the flags are checked.

Parameters:

  • subset bitflag the flags to check for.

Returns:

    boolean true if all the flags are set, false otherwise.

Usage:

    local sys = require 'system'
    local flags = sys.bitflag(12)     -- b1100
    local myflags = sys.bitflag(15)   -- b1111
    print(flags:has_all_of(myflags))  -- false, not all bits in myflags are set in flags
    print(myflags:has_all_of(flags))  -- true, all bits in flags are set in myflags
bitflag:has_any_of (subset)
Checks if any of the flags in the given subset are set. If the flags to check has a value 0, it will always return false. So if there are flags that are unsupported on a platform, they can be set to 0 and the has_any_of function will return false if the flags are checked.

Parameters:

  • subset bitflag the flags to check for.

Returns:

    boolean true if any of the flags are set, false otherwise.

Usage:

    local sys = require 'system'
    local flags = sys.bitflag(12)     -- b1100
    local myflags = sys.bitflag(7)    -- b0111
    print(flags:has_any_of(myflags))  -- true, some bits in myflags are set in flags
    print(myflags:has_any_of(flags))  -- true, some bits in flags are set in myflags
bitflag:value ()
Retrieves the numeric value of the bitflag object.

Returns:

    number the numeric value of the bitflags.

Usage:

    local sys = require 'system'
    local flags = sys.bitflag()     -- b0000
    flags[0] = true                 -- b0001
    flags[2] = true                 -- b0101
    print(flags:value())            -- 5
system.bitflag ([value=0])
Creates a new bitflag object from the given value.

Parameters:

  • value number the value to create the bitflag object from. (default 0)

Returns:

    bitflag bitflag object with the given values set.

Usage:

    local sys = require 'system'
    local flags = sys.bitflag(2)    -- b0010
    
    -- get state of individual bits
    print(flags[0])                 -- false
    print(flags[1])                 -- true
    
    -- set individual bits
    flags[0] = true                 -- b0011
    print(flags:value())            -- 3
    print(flags)                    -- "bitflags: 3"
    
    -- adding flags (bitwise OR)
    local flags1 = sys.bitflag(1)   -- b0001
    local flags2 = sys.bitflag(2)   -- b0010
    local flags3 = flags1 + flags2  -- b0011
    
    -- substracting flags (bitwise AND NOT)
    print(flags3:value())           -- 3
    flag3 = flag3 - flag3           -- b0000
    print(flags3:value())           -- 0
    
    -- comparing flags
    local flags4 = sys.bitflag(7)   -- b0111
    local flags5 = sys.bitflag(255) -- b11111111
    print(flags5 ~= flags4)         -- true, not the same flags
    local flags6 = sys.bitflag(7)   -- b0111
    print(flags6 == flags4)         -- true, same flags
    
    -- comparison of subsets
    local flags7 = sys.bitflag(0)    -- b0000
    local flags8 = sys.bitflag(3)    -- b0011
    local flags9 = sys.bitflag(7)    -- b0111
    print(flags9:has_all_of(flags8)) -- true, flags8 bits are all set in flags9
    print(flags8:has_any_of(flags9)) -- true, some of flags9 bits are set in flags8
    print(flags8:has_all_of(flags7)) -- false, flags7 (== 0) is not set in flags8
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/000077500000000000000000000000001473046163300163235ustar00rootroot00000000000000luasystem-0.4.5/docs/examples/compat.lua.html000066400000000000000000000125411473046163300212570ustar00rootroot00000000000000 Lua-System docs

compat.lua

-- This example shows how to remove platform differences to create a
-- cross-platform level playing field.

local sys = require "system"



if sys.windows then
  -- Windows holds multiple copies of environment variables, to ensure getenv
  -- returns what setenv sets we need to use the system.getenv instead of
  -- os.getenv.
  os.getenv = sys.getenv  -- luacheck: ignore

  -- Set console output to UTF-8 encoding.
  sys.setconsoleoutputcp(sys.CODEPAGE_UTF8)

  -- Set up the terminal to handle ANSI escape sequences on Windows.
  if sys.isatty(io.stdout) then
    sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
  end
  if sys.isatty(io.stderr) then
    sys.setconsoleflags(io.stderr, sys.getconsoleflags(io.stderr) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
  end
  if sys.isatty(io.stdin) then
    sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdout) + sys.ENABLE_VIRTUAL_TERMINAL_INPUT)
  end


else
  -- On Posix, one can set a variable to an empty string, but on Windows, this
  -- will remove the variable from the environment. To make this consistent
  -- across platforms, we will remove the variable from the environment if the
  -- value is an empty string.
  local old_setenv = sys.setenv
  function sys.setenv(name, value)
    if value == "" then value = nil end
    return old_setenv(name, value)
  end
end
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/flag_debugging.lua.html000066400000000000000000000051031473046163300227140ustar00rootroot00000000000000 Lua-System docs

flag_debugging.lua

local sys = require "system"

-- Print the Windows Console flags for stdin
sys.listconsoleflags(io.stdin)

-- Print the Posix termios flags for stdin
sys.listtermflags(io.stdin)
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/password_input.lua.html000066400000000000000000000142371473046163300230610ustar00rootroot00000000000000 Lua-System docs

password_input.lua

local sys = require "system"

print [[

This example shows how to disable the "echo" of characters read to the console,
useful for reading secrets from the user.

]]

--- Function to read from stdin without echoing the input (for secrets etc).
-- It will (in a platform agnostic way) disable echo on the terminal, read the
-- input, and then re-enable echo.
-- @param ... Arguments to pass to io.stdin:read()
-- @return the results of io.stdin:read(...)
local function read_secret(...)
  local w_oldflags, p_oldflags

  if sys.isatty(io.stdin) then
    -- backup settings, configure echo flags
    w_oldflags = sys.getconsoleflags(io.stdin)
    p_oldflags = sys.tcgetattr(io.stdin)
    -- set echo off to not show password on screen
    assert(sys.setconsoleflags(io.stdin, w_oldflags - sys.CIF_ECHO_INPUT))
    assert(sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = p_oldflags.lflag - sys.L_ECHO }))
  end

  local secret, err = io.stdin:read(...)

  -- restore settings
  if sys.isatty(io.stdin) then
    io.stdout:write("\n")  -- Add newline after reading the password
    sys.setconsoleflags(io.stdin, w_oldflags)
    sys.tcsetattr(io.stdin, sys.TCSANOW, p_oldflags)
  end

  return secret, err
end



-- Get username
io.write("Username: ")
local username = io.stdin:read("*l")

-- Get the secret
io.write("Password: ")
local password = read_secret("*l")

-- Get domainname
io.write("Domain  : ")
local domain = io.stdin:read("*l")


-- Print the results
print("")
print("Here's what we got:")
print("  username: " .. username)
print("  password: " .. password)
print("  domain  : " .. domain)
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/read.lua.html000066400000000000000000000166171473046163300207170ustar00rootroot00000000000000 Lua-System docs

read.lua

local sys = require "system"

print [[

This example shows how to do a non-blocking read from the cli.

]]

-- setup Windows console to handle ANSI processing
local of_in = sys.getconsoleflags(io.stdin)
local of_out = sys.getconsoleflags(io.stdout)
sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT)

-- setup Posix terminal to use non-blocking mode, and disable line-mode
local of_attr = sys.tcgetattr(io.stdin)
local of_block = sys.getnonblock(io.stdin)
sys.setnonblock(io.stdin, true)
sys.tcsetattr(io.stdin, sys.TCSANOW, {
  lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo
})

-- cursor sequences
local get_cursor_pos = "\27[6n"



print("Press a key, or 'A' to get cursor position, 'ESC' to exit")
while true do
  local key, keytype

  -- wait for a key
  while not key do
    key, keytype = sys.readansi(math.huge)
  end

  if key == "A" then io.write(get_cursor_pos); io.flush() end

  -- check if we got a key or ANSI sequence
  if keytype == "char" then
    -- just a key
    local b = key:byte()
    if b < 32 then
      key = "." -- replace control characters with a simple "." to not mess up the screen
    end

    print("you pressed: " .. key .. " (" .. b .. ")")
    if b == 27 then
      print("Escape pressed, exiting")
      break
    end

  elseif keytype == "ansi" then
    -- we got an ANSI sequence
    local seq = { key:byte(1, #key) }
    print("ANSI sequence received: " .. key:sub(2,-1), "(bytes: " .. table.concat(seq, ", ")..")")

  else
    print("unknown key type received: " .. tostring(keytype))
  end
end



-- Clean up afterwards
sys.setnonblock(io.stdin, false)
sys.setconsoleflags(io.stdout, of_out)
sys.setconsoleflags(io.stdin, of_in)
sys.tcsetattr(io.stdin, sys.TCSANOW, of_attr)
sys.setnonblock(io.stdin, of_block)
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/readline.lua.html000066400000000000000000001034731473046163300215640ustar00rootroot00000000000000 Lua-System docs

readline.lua

--- An example class for reading a line of input from the user in a non-blocking way.
-- It uses ANSI escape sequences to move the cursor and handle input.
-- It can be used to read a line of input from the user, with a prompt.
-- It can handle double-width UTF-8 characters.
-- It can be used asynchroneously if system.sleep is patched to yield to a coroutine scheduler.

local sys = require("system")


-- Mapping of key-sequences to key-names
local key_names = {
  ["\27[C"] = "right",
  ["\27[D"] = "left",
  ["\127"] = "backspace",
  ["\27[3~"] = "delete",
  ["\27[H"] = "home",
  ["\27[F"] = "end",
  ["\27"] = "escape",
  ["\9"] = "tab",
  ["\27[Z"] = "shift-tab",
}

if sys.windows then
  key_names["\13"] = "enter"
else
  key_names["\10"] = "enter"
end


-- Mapping of key-names to key-sequences
local key_sequences = {}
for k, v in pairs(key_names) do
  key_sequences[v] = k
end


-- bell character
local function bell()
  io.write("\7")
  io.flush()
end


-- generate string to move cursor horizontally
-- positive goes right, negative goes left
local function cursor_move_horiz(n)
  if n == 0 then
    return ""
  end
  return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "C" or "D")
end


-- -- generate string to move cursor vertically
-- -- positive goes down, negative goes up
-- local function cursor_move_vert(n)
--   if n == 0 then
--     return ""
--   end
--   return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "B" or "A")
-- end


-- -- log to the line above the current line
-- local function log(...)
--   local arg = { n = select("#", ...), ...}
--   for i = 1, arg.n do
--     arg[i] = tostring(arg[i])
--   end
--   arg = " " .. table.concat(arg, " ") .. " "

--   io.write(cursor_move_vert(-1), arg, cursor_move_vert(1), cursor_move_horiz(-#arg))
-- end


-- UTF8 character size in bytes
-- @tparam number b the byte value of the first byte of a UTF8 character
local function utf8size(b)
  return b < 128 and 1 or b < 224 and 2 or b < 240 and 3 or b < 248 and 4
end



local utf8parse do
  local utf8_value_mt = {
    __tostring = function(self)
      return table.concat(self, "")
    end,
  }

  -- Parses a UTF8 string into list of individual characters.
  -- key 'chars' gets the length in UTF8 characters, whilst # returns the length
  -- for display (to handle double-width UTF8 chars).
  -- in the list the double-width characters are followed by an empty string.
  -- @tparam string s the UTF8 string to parse
  -- @treturn table the list of characters
  function utf8parse(s)
    local t = setmetatable({ chars = 0 }, utf8_value_mt)
    local i = 1
    while i <= #s do
      local b = s:byte(i)
      local w = utf8size(b)
      local char = s:sub(i, i + w - 1)
      t[#t + 1] = char
      t.chars = t.chars + 1
      if sys.utf8cwidth(char) == 2 then
        -- double width character, add empty string to keep the length of the
        -- list the same as the character width on screen
        t[#t + 1] = ""
      end
      i = i + w
    end
    return t
  end
end



-- inline tests for utf8parse
-- do
--   local t = utf8parse("a你b好c")
--   assert(t[1] == "a")
--   assert(t[2] == "你")  -- double width
--   assert(t[3] == "")
--   assert(t[4] == "b")
--   assert(t[5] == "好")  -- double width
--   assert(t[6] == "")
--   assert(t[7] == "c")
--   assert(#t == 7)       -- size as displayed
-- end



-- readline class

local readline = {}
readline.__index = readline


--- Create a new readline object.
-- @tparam table opts the options for the readline object
-- @tparam[opt=""] string opts.prompt the prompt to display
-- @tparam[opt=80] number opts.max_length the maximum length of the input (in characters, not bytes)
-- @tparam[opt=""] string opts.value the default value
-- @tparam[opt=#value] number opts.position of the cursor in the input
-- @tparam[opt={"\10"/"\13"}] table opts.exit_keys an array of keys that will cause the readline to exit
-- @treturn readline the new readline object
function readline.new(opts)
  local value = utf8parse(opts.value or "")
  local prompt = utf8parse(opts.prompt or "")
  local pos = math.floor(opts.position or (#value + 1))
  pos = math.max(math.min(pos, (#value + 1)), 1)
  local len = math.floor(opts.max_length or 80)
  if len < 1 then
    error("max_length must be at least 1", 2)
  end

  if value.chars > len then
    error("value is longer than max_length", 2)
  end

  local exit_keys = {}
  for _, key in ipairs(opts.exit_keys or {}) do
    exit_keys[key] = true
  end
  if exit_keys[1] == nil then
    -- nothing provided, default to Enter-key
    exit_keys[1] = key_sequences.enter
  end

  local self = {
    value = value,          -- the default value
    max_length = len,       -- the maximum length of the input
    prompt = prompt,        -- the prompt to display
    position = pos,         -- the current position in the input
    drawn_before = false,   -- if the prompt has been drawn
    exit_keys = exit_keys,  -- the keys that will cause the readline to exit
  }

  setmetatable(self, readline)
  return self
end



-- draw the prompt and the input value, and position the cursor.
local function draw(self, redraw)
  if redraw or not self.drawn_before then
    -- we are at start of prompt
    self.drawn_before = true
  else
    -- we are at current cursor position, move to start of prompt
    io.write(cursor_move_horiz(-(#self.prompt + self.position)))
  end
  -- write prompt & value
  io.write(tostring(self.prompt) .. tostring(self.value))
  -- clear remainder of input size
  io.write(string.rep(" ", self.max_length - self.value.chars))
  io.write(cursor_move_horiz(-(self.max_length - self.value.chars)))
  -- move to cursor position
  io.write(cursor_move_horiz(-(#self.value + 1 - self.position)))
  io.flush()
end


local handle_key do -- keyboard input handler

  local key_handlers
  key_handlers = {
    left = function(self)
      if self.position == 1 then
        bell()
        return
      end

      local new_pos = self.position - 1
      while self.value[new_pos] == "" do -- skip empty strings; double width chars
        new_pos = new_pos - 1
      end

      io.write(cursor_move_horiz(-(self.position - new_pos)))
      io.flush()
      self.position = new_pos
    end,

    right = function(self)
      if self.position == #self.value + 1 then
        bell()
        return
      end

      local new_pos = self.position + 1
      while self.value[new_pos] == "" do -- skip empty strings; double width chars
        new_pos = new_pos + 1
      end

      io.write(cursor_move_horiz(new_pos - self.position))
      io.flush()
      self.position = new_pos
    end,

    backspace = function(self)
      if self.position == 1 then
        bell()
        return
      end

      while self.value[self.position - 1] == "" do -- remove empty strings; double width chars
        io.write(cursor_move_horiz(-1))
        self.position = self.position - 1
        table.remove(self.value, self.position)
      end
      -- remove char itself
      io.write(cursor_move_horiz(-1))
      self.position = self.position - 1
      table.remove(self.value, self.position)
      self.value.chars = self.value.chars - 1
      draw(self)
    end,

    home = function(self)
      local new_pos = 1
      io.write(cursor_move_horiz(new_pos - self.position))
      self.position = new_pos
    end,

    ["end"] = function(self)
      local new_pos = #self.value + 1
      io.write(cursor_move_horiz(new_pos - self.position))
      self.position = new_pos
    end,

    delete = function(self)
      if self.position > #self.value then
        bell()
        return
      end

      key_handlers.right(self)
      key_handlers.backspace(self)
    end,
  }


  -- handles a single input key/ansi-sequence.
  -- @tparam string key the key or ansi-sequence (from system.readansi)
  -- @tparam string keytype the type of the key, either "char" or "ansi" (from system.readansi)
  -- @treturn string status the status of the key handling, either "ok", "exit_key" or an error message
  function handle_key(self, key, keytype)
    if self.exit_keys[key] then
      -- registered exit key
      return "exit_key"
    end

    local handler = key_handlers[key_names[key] or true ]
    if handler then
      handler(self)
      return "ok"
    end

    if keytype == "ansi" then
      -- we got an ansi sequence, but dunno how to handle it, ignore
      -- print("unhandled ansi: ", key:sub(2,-1), string.byte(key, 1, -1))
      bell()
      return "ok"
    end

    -- just a single key
    if key < " " then
      -- control character
      bell()
      return "ok"
    end

    if self.value.chars >= self.max_length then
      bell()
      return "ok"
    end

    -- insert the key into the value
    if sys.utf8cwidth(key) == 2 then
      -- double width character, insert empty string after it
      table.insert(self.value, self.position, "")
      table.insert(self.value, self.position, key)
      self.position = self.position + 2
      io.write(cursor_move_horiz(2))
    else
      table.insert(self.value, self.position, key)
      self.position = self.position + 1
      io.write(cursor_move_horiz(1))
    end
    self.value.chars = self.value.chars + 1
    draw(self)
    return "ok"
  end
end



--- Get_size returns the maximum size of the input box (prompt + input).
-- The size is in rows and columns. Columns is determined by
-- the prompt and the max_length * 2 (characters can be double-width).
-- @treturn number the number of rows (always 1)
-- @treturn number the number of columns
function readline:get_size()
  return 1, #self.prompt + self.max_length * 2
end



--- Get coordinates of the cursor in the input box (prompt + input).
-- The coordinates are 1-based. They are returned as row and column, within the
-- size as reported by get_size.
-- @treturn number the row of the cursor (always 1)
-- @treturn number the column of the cursor
function readline:get_cursor()
  return 1, #self.prompt + self.position
end



--- Set the coordinates of the cursor in the input box (prompt + input).
-- The coordinates are 1-based. They are expected to be within the
-- size as reported by get_size, and beyond the prompt.
-- If the position is invalid, it will be corrected.
-- Use the results to check if the position was adjusted.
-- @tparam number row the row of the cursor (always 1)
-- @tparam number col the column of the cursor
-- @return results of get_cursor
function readline:set_cursor(row, col)
  local l_prompt = #self.prompt
  local l_value = #self.value

  if col < l_prompt + 1 then
    col = l_prompt + 1
  elseif col > l_prompt + l_value + 1 then
    col = l_prompt + l_value + 1
  end

  while self.value[col - l_prompt] == "" do
    col = col - 1 -- on an empty string, so move back to start of double-width char
  end

  local new_pos = col - l_prompt

  cursor_move_horiz(self.position - new_pos)
  io.flush()

  self.position = new_pos
  return self:get_cursor()
end



--- Read a line of input from the user.
-- It will first print the prompt and then wait for input. Ensure the cursor
-- is at the correct position before calling this function. This function will
-- do all cursor movements in a relative way.
-- Can be called again after an exit-key or timeout has occurred. Just make sure
-- the cursor is at the same position where is was when it returned the last time.
-- Alternatively the cursor can be set to the position of the prompt (the position
-- the cursor was in before the first call), and the parameter redraw can be set
-- to true.
-- @tparam[opt=math.huge] number timeout the maximum time to wait for input in seconds
-- @tparam[opt=false] boolean redraw if true the prompt will be redrawn (cursor must be at prompt position!)
-- @treturn[1] string the input string as entered the user
-- @treturn[1] string the exit-key used to exit the readline (see new)
-- @treturn[2] nil when input is incomplete
-- @treturn[2] string error message, the reason why the input is incomplete, "timeout", or an error reading a key
function readline:__call(timeout, redraw)
  draw(self, redraw)
  timeout = timeout or math.huge
  local timeout_end = sys.gettime() + timeout

  while true do
    local key, keytype = sys.readansi(timeout_end - sys.gettime())
    if not key then
      -- error or timeout
      return nil, keytype
    end

    local status = handle_key(self, key, keytype)
    if status == "exit_key" then
      return tostring(self.value), key

    elseif status ~= "ok" then
      error("unknown status received: " .. tostring(status))
    end
  end
end



-- return readline  -- normally we'd return here, but for the example we continue




local backup = sys.termbackup()

-- setup Windows console to handle ANSI processing
sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT)
-- set output to UTF-8
sys.setconsoleoutputcp(sys.CODEPAGE_UTF8)

-- setup Posix terminal to disable canonical mode and echo
sys.tcsetattr(io.stdin, sys.TCSANOW, {
  lflag = sys.tcgetattr(io.stdin).lflag - sys.L_ICANON - sys.L_ECHO,
})
-- setup stdin to non-blocking mode
sys.setnonblock(io.stdin, true)


local rl = readline.new{
  prompt = "Enter something: ",
  max_length = 60,
  value = "Hello, 你-好 World 🚀!",
  -- position = 2,
  exit_keys = {key_sequences.enter, "\27", "\t", "\27[Z"}, -- enter, escape, tab, shift-tab
}


local result, key = rl()
print("")  -- newline after input, to move cursor down from the input line
print("Result (string): '" .. result .. "'")
print("Result (bytes):", result:byte(1,-1))
print("Exit-Key (bytes):", key:byte(1,-1))


-- Clean up afterwards
sys.termrestore(backup)
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/spinner.lua.html000066400000000000000000000145701473046163300214560ustar00rootroot00000000000000 Lua-System docs

spinner.lua

local sys = require("system")

print [[

An example to display a spinner, whilst a long running task executes.

]]


-- start make backup, to auto-restore on exit
sys.autotermrestore()
-- configure console
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT)
local of = sys.tcgetattr(io.stdin)
sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = of.lflag - sys.L_ICANON - sys.L_ECHO })
sys.setnonblock(io.stdin, true)



local function hideCursor()
  io.write("\27[?25l")
  io.flush()
end

local function showCursor()
  io.write("\27[?25h")
  io.flush()
end

local function left(n)
  io.write("\27[",n or 1,"D")
  io.flush()
end



local spinner do
  local spin = [[|/-\]]
  local i = 1
  spinner = function()
    hideCursor()
    io.write(spin:sub(i, i))
    left()
    i = i + 1
    if i > #spin then i = 1 end

    if sys.readkey(0) ~= nil then
      while sys.readkey(0) ~= nil do end -- consume keys pressed
      io.write(" ");
      left()
      showCursor()
      return true
    else
      return false
    end
  end
end

io.stdout:write("press any key to stop the spinner... ")
while not spinner() do
  sys.sleep(0.1)
end

print("Done!")
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/spiral_snake.lua.html000066400000000000000000000150371473046163300224520ustar00rootroot00000000000000 Lua-System docs

spiral_snake.lua

local sys = require "system"

print [[

This example will draw a snake like spiral on the screen. Showing ANSI escape
codes for moving the cursor around.

]]

-- backup term settings with auto-restore on exit
sys.autotermrestore()

-- setup Windows console to handle ANSI processing
sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)

-- start drawing the spiral.
-- start from current pos, then right, then up, then left, then down, and again.
local x, y = 1, 1     -- current position
local dx, dy = 1, 0   -- direction after each step
local wx, wy = 30, 30 -- width and height of the room
local mx, my = 1, 1   -- margin

-- commands to move the cursor
local move_left = "\27[1D"
local move_right = "\27[1C"
local move_up = "\27[1A"
local move_down = "\27[1B"

-- create room: 30 empty lines
print(("\n"):rep(wy))
local move = move_right

while wx > 0 and wy > 0 do
  sys.sleep(0.01) -- slow down the drawing a little
  io.write("*" .. move_left .. move )
  io.flush()
  x = x + dx
  y = y + dy

  if x > wx and move == move_right then
    -- end of move right
    dx = 0
    dy = 1
    move = move_up
    wy = wy - 1
    my = my + 1
  elseif y > wy and move == move_up then
    -- end of move up
    dx = -1
    dy = 0
    move = move_left
    wx = wx - 1
    mx = mx + 1
  elseif x < mx and move == move_left then
    -- end of move left
    dx = 0
    dy = -1
    move = move_down
    wy = wy - 1
    my = my + 1
  elseif y < my and move == move_down then
    -- end of move down
    dx = 1
    dy = 0
    move = move_right
    wx = wx - 1
    mx = mx + 1
  end
end

io.write(move_down:rep(15))
print("\nDone!")
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/examples/terminalsize.lua.html000066400000000000000000000127041473046163300225030ustar00rootroot00000000000000 Lua-System docs

terminalsize.lua

local sys = require("system")

sys.autotermrestore()  -- set up auto restore of terminal settings on exit

-- setup Windows console to handle ANSI processing
sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT)

-- setup Posix to disable canonical mode and echo
local of_attr = sys.tcgetattr(io.stdin)
sys.setnonblock(io.stdin, true)
sys.tcsetattr(io.stdin, sys.TCSANOW, {
  lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo
})


-- generate string to move cursor horizontally
-- positive goes right, negative goes left
local function cursor_move_horiz(n)
  if n == 0 then
    return ""
  end
  return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "C" or "D")
end


local rows, cols
print("Change the terminal window size, press any key to exit")
while not sys.readansi(0.2) do  -- use readansi to not leave stray bytes in the input buffer
  local nrows, ncols = sys.termsize()
  if rows ~= nrows or cols ~= ncols then
    rows, cols = nrows, ncols
    local text = "Terminal size: " .. rows .. "x" .. cols .. "     "
    io.write(text .. cursor_move_horiz(-#text))
    io.flush()
  end
end
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/index.html000066400000000000000000000103321473046163300165010ustar00rootroot00000000000000 Lua-System docs

Platform independent system calls for Lua

Modules

system Platform independent system calls for Lua.

Classes

bitflags Bitflags module.

Topics

01-introduction.md
02-development.md
03-terminal.md
CHANGELOG.md
LICENSE.md

Examples

compat.lua
flag_debugging.lua
password_input.lua
read.lua
readline.lua
spinner.lua
spiral_snake.lua
terminalsize.lua
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/ldoc.css000066400000000000000000000132631473046163300161450ustar00rootroot00000000000000body { color: #47555c; font-size: 16px; font-family: "Open Sans", sans-serif; margin: 0; background: #eff4ff; } a:link { color: #008fee; } a:visited { color: #008fee; } a:hover { color: #22a7ff; } h1 { font-size:26px; font-weight: normal; } h2 { font-size:22px; font-weight: normal; } h3 { font-size:18px; font-weight: normal; } h4 { font-size:16px; font-weight: bold; } hr { height: 1px; background: #c1cce4; border: 0px; margin: 15px 0; } code, tt { font-family: monospace; } span.parameter { font-family: monospace; font-weight: bold; color: rgb(99, 115, 131); } span.parameter:after { content:":"; } span.types:before { content:"("; } span.types:after { content:")"; } .type { font-weight: bold; font-style:italic } p.name { font-family: "Andale Mono", monospace; } #navigation { float: left; background-color: white; border-right: 1px solid #d3dbec; border-bottom: 1px solid #d3dbec; width: 14em; vertical-align: top; overflow: visible; } #navigation br { display: none; } #navigation h1 { background-color: white; border-bottom: 1px solid #d3dbec; padding: 15px; margin-top: 0px; margin-bottom: 0px; } #navigation h2 { font-size: 18px; background-color: white; border-bottom: 1px solid #d3dbec; padding-left: 15px; padding-right: 15px; padding-top: 10px; padding-bottom: 10px; margin-top: 30px; margin-bottom: 0px; } #content h1 { background-color: #2c3e67; color: white; padding: 15px; margin: 0px; } #content h2 { background-color: #6c7ea7; color: white; padding: 15px; padding-top: 15px; padding-bottom: 15px; margin-top: 0px; } #content h2 a { background-color: #6c7ea7; color: white; text-decoration: none; } #content h2 a:hover { text-decoration: underline; } #content h3 { font-style: italic; padding-top: 15px; padding-bottom: 4px; margin-right: 15px; margin-left: 15px; margin-bottom: 5px; border-bottom: solid 1px #bcd; } #content h4 { margin-right: 15px; margin-left: 15px; border-bottom: solid 1px #bcd; } #content pre { margin: 15px; } pre { background-color: rgb(50, 55, 68); color: white; border-radius: 3px; /* border: 1px solid #C0C0C0; /* silver */ padding: 15px; overflow: auto; font-family: "Andale Mono", monospace; } #content ul pre.example { margin-left: 0px; } table.index { /* border: 1px #00007f; */ } table.index td { text-align: left; vertical-align: top; } #navigation ul { font-size:1em; list-style-type: none; margin: 1px 1px 10px 1px; padding-left: 20px; } #navigation li { text-indent: -1em; display: block; margin: 3px 0px 0px 22px; } #navigation li li a { margin: 0px 3px 0px -1em; } #content { margin-left: 14em; } #content p { padding-left: 15px; padding-right: 15px; } #content table { padding-left: 15px; padding-right: 15px; background-color: white; } #content p, #content table, #content ol, #content ul, #content dl { max-width: 900px; } #about { padding: 15px; padding-left: 16em; background-color: white; border-top: 1px solid #d3dbec; border-bottom: 1px solid #d3dbec; } table.module_list, table.function_list { border-width: 1px; border-style: solid; border-color: #cccccc; border-collapse: collapse; margin: 15px; } table.module_list td, table.function_list td { border-width: 1px; padding-left: 10px; padding-right: 10px; padding-top: 5px; padding-bottom: 5px; border: solid 1px rgb(193, 204, 228); } table.module_list td.name, table.function_list td.name { background-color: white; min-width: 200px; border-right-width: 0px; } table.module_list td.summary, table.function_list td.summary { background-color: white; width: 100%; border-left-width: 0px; } dl.function { margin-right: 15px; margin-left: 15px; border-bottom: solid 1px rgb(193, 204, 228); border-left: solid 1px rgb(193, 204, 228); border-right: solid 1px rgb(193, 204, 228); background-color: white; } dl.function dt { color: rgb(99, 123, 188); font-family: monospace; border-top: solid 1px rgb(193, 204, 228); padding: 15px; } dl.function dd { margin-left: 15px; margin-right: 15px; margin-top: 5px; margin-bottom: 15px; } #content dl.function dd h3 { margin-top: 0px; margin-left: 0px; padding-left: 0px; font-size: 16px; color: rgb(128, 128, 128); border-bottom: solid 1px #def; } #content dl.function dd ul, #content dl.function dd ol { padding: 0px; padding-left: 15px; list-style-type: none; } ul.nowrap { overflow:auto; white-space:nowrap; } .section-description { padding-left: 15px; padding-right: 15px; } /* stop sublists from having initial vertical space */ ul ul { margin-top: 0px; } ol ul { margin-top: 0px; } ol ol { margin-top: 0px; } ul ol { margin-top: 0px; } /* make the target distinct; helps when we're navigating to a function */ a:target + * { background-color: #FF9; } /* styles for prettification of source */ pre .comment { color: #bbccaa; } pre .constant { color: #a8660d; } pre .escape { color: #844631; } pre .keyword { color: #ffc090; font-weight: bold; } pre .library { color: #0e7c6b; } pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } pre .string { color: #8080ff; } pre .number { color: #f8660d; } pre .operator { color: #2239a8; font-weight: bold; } pre .preprocessor, pre .prepro { color: #a33243; } pre .global { color: #c040c0; } pre .user-keyword { color: #800080; } pre .prompt { color: #558817; } pre .url { color: #272fc2; text-decoration: underline; } luasystem-0.4.5/docs/modules/000077500000000000000000000000001473046163300161555ustar00rootroot00000000000000luasystem-0.4.5/docs/modules/system.html000066400000000000000000001316351473046163300204000ustar00rootroot00000000000000 Lua-System docs

Module system

Platform independent system calls for Lua.

Fields

windows Flag to identify Windows.

Environment

getenv (name) Gets the value of an environment variable.
getenvs () Returns a table with all environment variables.
setenv (name[, value]) Sets an environment variable.

Random

random ([length=1]) Generate random bytes.

Terminal

CODEPAGE_UTF8 UTF8 codepage.
_readkey () Reads a key from the console non-blocking.
autotermrestore () Backs up terminal settings and restores them on application exit.
getconsolecp () Gets the current console code page (Windows).
getconsoleflags (file) Gets console flags (Windows).
getconsoleoutputcp () Gets the current console output code page (Windows).
getnonblock (fd) Gets non-blocking mode status for a file (Posix).
isatty (file) Checks if a file-handle is a TTY.
listconsoleflags (fh) Debug function for console flags (Windows).
listtermflags (fh) Debug function for terminal flags (Posix).
readansi (timeout) Reads a single key, if it is the start of ansi escape sequence then it reads the full sequence.
readkey (timeout) Reads a single byte from the console, with a timeout.
setconsolecp (cp) Sets the current console code page (Windows).
setconsoleflags (file, bitflags) Sets the console flags (Windows).
setconsoleoutputcp (cp) Sets the current console output code page (Windows).
setnonblock (fd, make_non_block) Enables or disables non-blocking mode for a file (Posix).
tcgetattr (fd) Get termios state (Posix).
tcsetattr (fd, actions, termios) Set termios state (Posix).
termbackup () Returns a backup of terminal settings for stdin/out/err.
termrestore (backup) Restores terminal settings from a backup
termsize () Get the size of the terminal in rows and columns.
termwrap (f) Wraps a function to automatically restore terminal settings upon returning.
utf8cwidth (utf8_char) Get the width of a utf8 character for terminal display.
utf8swidth (utf8_string) Get the width of a utf8 string for terminal display.

Time

gettime () Get system time.
monotime () Get monotonic time.
sleep (seconds[, precision=16]) Sleep without a busy loop.


Fields

windows
Flag to identify Windows.
  • windows true if on Windows, false otherwise.

Environment

getenv (name)
Gets the value of an environment variable.

NOTE: Windows has multiple copies of environment variables. For this reason, the setenv function will not work with Lua's os.getenv on Windows. If you want to use setenv then consider patching os.getenv with this implementation of getenv.

Parameters:

  • name string name of the environment variable

Returns:

    string or nil value of the environment variable, or nil if the variable is not set
getenvs ()
Returns a table with all environment variables.

Returns:

    table table with all environment variables and their values
setenv (name[, value])
Sets an environment variable.

NOTE: Windows has multiple copies of environment variables. For this reason, the setenv function will not work with Lua's os.getenv on Windows. If you want to use it then consider patching os.getenv with the implementation of system.getenv.

Parameters:

  • name string name of the environment variable
  • value string value of the environment variable, if nil the variable will be deleted (on Windows, setting an empty string, will also delete the variable) (optional)

Returns:

    boolean success

Random

random ([length=1])
Generate random bytes. This uses CryptGenRandom() on Windows, and /dev/urandom on other platforms. It will return the requested number of bytes, or an error, never a partial result.

Parameters:

  • length int number of bytes to get (default 1)

Returns:

    string string of random bytes

Or

  1. nil
  2. string error message

Terminal

Unix: see https://blog.nelhage.com/2009/12/a-brief-introduction-to-termios-termios3-and-stty/

Windows: see https://learn.microsoft.com/en-us/windows/console/console-reference

CODEPAGE_UTF8
UTF8 codepage. To be used with system.setconsoleoutputcp and system.setconsolecp.
  • CODEPAGE_UTF8 The Windows CodePage for UTF8.
_readkey ()
Reads a key from the console non-blocking. This function should not be called directly, but through the system.readkey or system.readansi functions. It will return the next byte from the input stream, or nil if no key was pressed.

On Posix, io.stdin must be set to non-blocking mode using setnonblock and canonical mode must be turned off using tcsetattr, before calling this function. Otherwise it will block. No conversions are done on Posix, so the byte read is returned as-is.

On Windows this reads a wide character and converts it to UTF-8. Multi-byte sequences will be buffered internally and returned one byte at a time.

Returns:

    integer the byte read from the input stream

Or

    nil if no key was pressed

Or

  1. nil on error
  2. string error message
  3. int errnum (on posix)
autotermrestore ()
Backs up terminal settings and restores them on application exit. Calls termbackup to back up terminal settings and sets up a GC method to automatically restore them on application exit (also works on Lua 5.1).

Returns:

    boolean true

Or

  1. nil if the backup was already created
  2. string error message
getconsolecp ()
Gets the current console code page (Windows).

Returns:

    int the current code page (always 65001 on Posix systems)
getconsoleflags (file)
Gets console flags (Windows). The CIF_ and COF_ constants are available on the module table. Where CIF are the input flags (for use with io.stdin) and COF are the output flags (for use with io.stdout/io.stderr).

Note: See setconsolemode documentation for more information on the flags.

Parameters:

Returns:

    bitflags the current console flags.

Or

  1. nil
  2. string error message

Usage:

    local system = require('system')
    
    local flags = system.getconsoleflags(io.stdout)
    print("Current stdout flags:", tostring(flags))
    
    if flags:has_all_of(system.COF_VIRTUAL_TERMINAL_PROCESSING + system.COF_PROCESSED_OUTPUT) then
        print("Both flags are set")
    else
        print("At least one flag is not set")
    end
getconsoleoutputcp ()
Gets the current console output code page (Windows).

Returns:

    int the current code page (always 65001 on Posix systems)
getnonblock (fd)
Gets non-blocking mode status for a file (Posix).

Parameters:

Returns:

    bool true if set to non-blocking, false if not. Always returns false on Windows.

Or

  1. nil
  2. string error message
  3. int errnum
isatty (file)
Checks if a file-handle is a TTY.

Parameters:

Returns:

    boolean true if the file is a tty

Usage:

    local system = require('system')
    if system.isatty(io.stdin) then
        -- enable ANSI coloring etc on Windows, does nothing in Posix.
        local flags = system.getconsoleflags(io.stdout)
        system.setconsoleflags(io.stdout, flags + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
    end
listconsoleflags (fh)
Debug function for console flags (Windows). Pretty prints the current flags set for the handle.

Parameters:

Usage:

    -- Print the flags for stdin/out/err
    system.listconsoleflags(io.stdin)
    system.listconsoleflags(io.stdout)
    system.listconsoleflags(io.stderr)
listtermflags (fh)
Debug function for terminal flags (Posix). Pretty prints the current flags set for the handle.

Parameters:

Usage:

    -- Print the flags for stdin/out/err
    system.listconsoleflags(io.stdin)
    system.listconsoleflags(io.stdout)
    system.listconsoleflags(io.stderr)
readansi (timeout)
Reads a single key, if it is the start of ansi escape sequence then it reads the full sequence. The key can be a multi-byte string in case of multibyte UTF-8 character. This function uses system.readkey, and hence system.sleep to wait until either a key is available or the timeout is reached. It returns immediately if a key is available or if timeout is less than or equal to 0. In case of an ANSI sequence, it will return the full sequence as a string.

Parameters:

  • timeout number the timeout in seconds.

Returns:

  1. string the character that was received (can be multi-byte), or a complete ANSI sequence
  2. string the type of input: "char" for a single key, "ansi" for an ANSI sequence

Or

  1. nil in case of an error
  2. string error message; "timeout" if the timeout was reached.
  3. string partial result in case of an error while reading a sequence, the sequence so far.
readkey (timeout)
Reads a single byte from the console, with a timeout. This function uses system.sleep to wait until either a byte is available or the timeout is reached. The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.2 seconds. It returns immediately if a byte is available or if timeout is less than or equal to 0.

Using system.readansi is preferred over this function. Since this function can leave stray/invalid byte-sequences in the input buffer, while system.readansi reads full ANSI and UTF8 sequences.

Parameters:

  • timeout number the timeout in seconds.

Returns:

    byte the byte value that was read.

Or

  1. nil if no key was read
  2. string error message; "timeout" if the timeout was reached.
setconsolecp (cp)
Sets the current console code page (Windows).

Parameters:

Returns:

    bool true on success (always true on Posix systems)
setconsoleflags (file, bitflags)
Sets the console flags (Windows). The CIF_ and COF_ constants are available on the module table. Where CIF are the input flags (for use with io.stdin) and COF are the output flags (for use with io.stdout/io.stderr).

To see flag status and constant names check listconsoleflags.

Note: not all combinations of flags are allowed, as some are mutually exclusive or mutually required. See setconsolemode documentation

Parameters:

Returns:

    boolean true on success

Or

  1. nil
  2. string error message

Usage:

    local system = require('system')
    system.listconsoleflags(io.stdout) -- List all the available flags and their current status
    
    local flags = system.getconsoleflags(io.stdout)
    assert(system.setconsoleflags(io.stdout,
            flags + system.COF_VIRTUAL_TERMINAL_PROCESSING)
    
    system.listconsoleflags(io.stdout) -- List again to check the differences
setconsoleoutputcp (cp)
Sets the current console output code page (Windows).

Parameters:

Returns:

    bool true on success (always true on Posix systems)
setnonblock (fd, make_non_block)
Enables or disables non-blocking mode for a file (Posix).

Parameters:

  • fd file file handle to operate on, one of io.stdin, io.stdout, io.stderr
  • make_non_block boolean a truthy value will enable non-blocking mode, a falsy value will disable it.

Returns:

    bool true, if successful

Or

  1. nil
  2. string error message
  3. int errnum

See also:

Usage:

    local sys = require('system')
    
    -- set io.stdin to non-blocking mode
    local old_setting = sys.getnonblock(io.stdin)
    sys.setnonblock(io.stdin, true)
    
    -- do stuff
    
    -- restore old setting
    sys.setnonblock(io.stdin, old_setting)
tcgetattr (fd)

Get termios state (Posix). The terminal attributes is a table with the following fields:

  • iflag input flags
  • oflag output flags
  • lflag local flags
  • cflag control flags
  • ispeed input speed
  • ospeed output speed
  • cc control characters

Parameters:

Returns:

    error message if failed

Or

    termios terminal attributes, if successful. On Windows the bitflags are all 0, and the cc table is empty.

Or

  1. nil
  2. string error message
  3. int errnum

Usage:

    local system = require('system')
    
    local status = assert(tcgetattr(io.stdin))
    if status.iflag:has_all_of(system.I_IGNBRK) then
        print("Ignoring break condition")
    end
tcsetattr (fd, actions, termios)
Set termios state (Posix). This function will set the flags as given.

The I_, O_, and L_ constants are available on the module table. They are the respective flags for the iflags, oflags, and lflags bitmasks.

To see flag status and constant names check listtermflags. For their meaning check the manpage.

Note: only iflag, oflag, and lflag are supported at the moment. The other fields are ignored.

Parameters:

  • fd file file handle to operate on, one of io.stdin, io.stdout, io.stderr
  • actions integer one of TCSANOW, TCSADRAIN, TCSAFLUSH
  • termios a table with bitflag fields:
    • iflag bitflags if given will set the input flags (optional)
    • oflag bitflags if given will set the output flags (optional)
    • lflag bitflags if given will set the local flags (optional)

Returns:

    bool true, if successful. Always returns true on Windows.

Or

  1. nil
  2. string error message
  3. int errnum

Usage:

    local system = require('system')
    
    local status = assert(tcgetattr(io.stdin))
    if not status.lflag:has_all_of(system.L_ECHO) then
        -- if echo is off, turn echoing newlines on
        tcsetattr(io.stdin, system.TCSANOW, { lflag = status.lflag + system.L_ECHONL }))
    end
termbackup ()
Returns a backup of terminal settings for stdin/out/err. Handles terminal/console flags, Windows codepage, and non-block flags on the streams. Backs up terminal/console flags only if a stream is a tty.

Returns:

    table with backup of terminal settings
termrestore (backup)
Restores terminal settings from a backup

Parameters:

Returns:

    boolean true
termsize ()
Get the size of the terminal in rows and columns.

Returns:

  1. int the number of rows
  2. int the number of columns

Or

  1. nil
  2. string error message
termwrap (f)
Wraps a function to automatically restore terminal settings upon returning. Calls termbackup before calling the function and termrestore after.

Parameters:

  • f function function to wrap

Returns:

    function wrapped function
utf8cwidth (utf8_char)
Get the width of a utf8 character for terminal display.

Parameters:

  • utf8_char string the utf8 character to check, only the width of the first character will be returned

Returns:

    int the display width in columns of the first character in the string (0 for an empty string)

Or

  1. nil
  2. string error message
utf8swidth (utf8_string)
Get the width of a utf8 string for terminal display.

Parameters:

  • utf8_string string the utf8 string to check

Returns:

    int the display width of the string in columns (0 for an empty string)

Or

  1. nil
  2. string error message

Time

gettime ()
Get system time. The time is returned as the seconds since the epoch (1 January 1970 00:00:00).

Returns:

    number seconds (fractional)
monotime ()
Get monotonic time. The time is returned as the seconds since system start.

Returns:

    number seconds (fractional)
sleep (seconds[, precision=16])
Sleep without a busy loop. This function will sleep, without doing a busy-loop and wasting CPU cycles.

Parameters:

  • seconds number seconds to sleep (fractional).
  • precision integer minimum stepsize in milliseconds (Windows only, ignored elsewhere) (default 16)

Returns:

    true on success, or nil+err on failure
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/topics/000077500000000000000000000000001473046163300160065ustar00rootroot00000000000000luasystem-0.4.5/docs/topics/01-introduction.md.html000066400000000000000000000052641473046163300222410ustar00rootroot00000000000000 Lua-System docs

1. Introduction

luasystem is a platform independent system call library for Lua. Supports Unix, Windows, MacOS, Lua >= 5.1 and luajit >= 2.0.0.

Lua is typically platform independent, but it requires adhering to very old C standards. This in turn means that many common features (according to todays standards) are not available. This module attempts to overcome some of those hurdles by providing functions that cover those common needs.

This is not a kitchen sink library, but a minimalistic one with a focus on platform independence.

generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/topics/02-development.md.html000066400000000000000000000052241473046163300220370ustar00rootroot00000000000000 Lua-System docs

2. Development

Some tests cannot be run in CI becasue they test system specifics that simply do not exist in a CI environment. An example is the isatty function Which cannot be set to return a truthy value in CI.

The tests concerned are all labelled with #manual. And in CI they will be skipped because --exclude-tags=manual is being passed to the busted command line.

Hence if tests like these are being added, then please ensure the tests pass locally, and do not rely on CI only.

generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/topics/03-terminal.md.html000066400000000000000000000243161473046163300213340ustar00rootroot00000000000000 Lua-System docs

3. Terminal functionality

Terminals are fundamentally different on Windows and Posix. So even though luasystem provides primitives to manipulate both the Windows and Posix terminals, the user will still have to write platform specific code.

To mitigate this a little, all functions are available on all platforms. They just will be a no-op if invoked on another platform. This means that no platform specific branching is required (but still possible) in user code. The user must simply set up both platforms to make it work.

3.1 Backup and Restore terminal settings

Since there are a myriad of settings available;

Some helper functions are available to backup and restore them all at once. See termbackup, termrestore, autotermrestore and termwrap.

3.1 Terminal ANSI sequences

Windows is catching up with this. In Windows 10 (since 2019), the Windows Terminal application (not to be mistaken for the cmd console application) supports ANSI sequences. However this might not be enabled by default.

ANSI processing can be set up both on the input (key sequences, reading cursor position) as well as on the output (setting colors and cursor shapes).

To enable it use system.setconsoleflags like this:

-- setup Windows console to handle ANSI processing on output
sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
sys.setconsoleflags(io.stderr, sys.getconsoleflags(io.stderr) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)

-- setup Windows console to handle ANSI processing on input
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT)

3.2 UTF-8 in/output and display width

3.2.1 UTF-8 in/output

Where (most) Posix systems use UTF-8 by default, Windows internally uses UTF-16. More recent versions of Lua also have UTF-8 support. So luasystem also focusses on UTF-8.

On Windows UTF-8 output can be enabled by setting the output codepage like this:

-- setup Windows output codepage to UTF-8
sys.setconsoleoutputcp(sys.CODEPAGE_UTF8)

Terminal input is handled by the _getwchar() function on Windows which returns UTF-16 surrogate pairs. luasystem will automatically convert those to UTF-8. So when using readkey or readansi to read keyboard input no additional changes are required.

3.2.2 UTF-8 display width

Typical western characters and symbols are single width characters and will use only a single column when displayed on a terminal. However many characters from other languages/cultures or emojis require 2 columns for display.

Typically the wcwidth function is used on Posix to check the number of columns required for display. However since Windows doesn't provide this functionality a custom implementation is included based on the work by Markus Kuhn.

2 functions are provided, system.utf8cwidth for a single character, and system.utf8swidth for a string. When writing terminal applications the display width is relevant to positioning the cursor properly. For an example see the examples/readline.lua file.

3.3 reading keyboard input

3.3.1 Non-blocking

There are 2 functions for keyboard input (actually 3, if taking system._readkey into account): readkey and readansi.

readkey is a low level function and should preferably not be used, it returns a byte at a time, and hence can leave stray/invalid byte sequences in the buffer if only the start of a UTF-8 or ANSI sequence is consumed.

The preferred way is to use readansi which will parse and return entire characters in single or multiple bytes, or a full ANSI sequence.

On Windows the input is read using _getwchar() which bypasses the terminal and reads the input directly from the keyboard buffer. This means however that the character is also not being echoed to the terminal (independent of the echo settings used with system.setconsoleflags).

On Posix the traditional file approach is used, which:

  • is blocking by default
  • echoes input to the terminal
  • requires enter to be pressed to pass the input (canonical mode)

To use non-blocking input here's how to set it up:

-- setup Windows console to disable echo and line input (not required since _getwchar is used, just for consistency)
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT)

-- setup Posix by disabling echo, canonical mode, and making non-blocking
local of_attr = sys.tcgetattr(io.stdin)
sys.tcsetattr(io.stdin, sys.TCSANOW, {
  lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO,
})
sys.setnonblock(io.stdin, true)

Both functions require a timeout to be provided which allows for proper asynchronous code to be written. Since the underlying sleep method used is system.sleep, just patching that function with a coroutine based yielding one should be all that is needed to make the result work with asynchroneous coroutine schedulers.

3.3.2 Blocking input

When using traditional input method like io.stdin:read() (which is blocking) the echo and newline properties should be set on Windows similar to Posix. For an example see examples/password_input.lua.

generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/topics/CHANGELOG.md.html000066400000000000000000000154021473046163300205640ustar00rootroot00000000000000 Lua-System docs

CHANGELOG

Versioning

This library is versioned based on Semantic Versioning (SemVer).

Version scoping

The scope of what is covered by the version number excludes:

  • error messages; the text of the messages can change, unless specifically documented.

Releasing new versions

  • create a release branch
  • update the changelog below
  • update version and copyright-years in ./LICENSE.md and ./src/core.c (in module constants)
  • create a new rockspec and update the version inside the new rockspec:
    cp luasystem-scm-0.rockspec ./rockspecs/luasystem-X.Y.Z-1.rockspec
  • clean and render the docs: run ldoc .
  • commit the changes as Release vX.Y.Z
  • push the commit, and create a release PR
  • after merging tag the release commit with vX.Y.Z
  • upload to LuaRocks:
    luarocks upload ./rockspecs/luasystem-X.Y.Z-1.rockspec --api-key=ABCDEFGH
  • test the newly created rock:
    luarocks install luasystem

Version history

version 0.4.5, released 18-Dec-2024

  • Fix: suppress a warning when building with clang
  • Fix: do not rely on luaconf.h to include limits.h, fixes builds with latest LuaJIT (#38).

version 0.4.4, released 03-Sep-2024

  • Fix: include all objects in Makefile

version 0.4.3, released 28-Aug-2024

  • Chore: add compiler error on Windows if Virtual Terminal Processing is unavailable.
  • Fix: fix the freebsd build

Version 0.4.2, released 25-Jun-2024

  • Fix: include additional headers for some MinGW installations

Version 0.4.1, released 25-Jun-2024

  • Fix: when compiling with msys2 the conio.h header is required

Version 0.4.0, released 20-Jun-2024

  • Feat: getconsoleflags and setconsoleflags for getting/setting the current console configuration flags on Windows
  • Feat: getconsolecp and setconsolecp for getting/setting the console codepage on Windows
  • Feat: getconsoleoutputcp and setconsoleoutputcp for getting/setting the console output codepage on Windows
  • Feat: tcgetattr and tcsetattr for getting/setting the current console configuration flags on Posix
  • Feat: getnonblock and setnonblock for getting/setting the non-blocking flag on Posix
  • Feat: bitflags: a support feature for the above flag type controls to facilitate bit manipulation without resorting to binary operations (to also support PuC Lua 5.1)
  • Feat: readkey reads a keyboard input from stdin in a non-blocking way (utf8, also on Windows)
  • Feat: readansi reads a keyboard input from stdin in a non-blocking way, parses ansi and utf8 sequences
  • Feat: termsize gets the current terminal size in rows and columns
  • Feat: utf8cwidth and utf8swidth for getting the display width (in columns) of respectively a single utf8 character, or a utf8 string
  • Feat: helpers; termbackup, termrestore, autotermrestore, and termwrap for managing the many terminal settings on all platforms.

Version 0.3.0, released 15-Dec-2023

  • Feat: on Windows sleep now has a precision parameter
  • Feat: setenv added to set environment variables.
  • Feat: getenvs added to list environment variables.
  • Feat: getenv added to get environment variable previously set (Windows).
  • Feat: random added to return high-quality random bytes
  • Feat: isatty added to check if a file-handle is a tty

Version 0.2.1, released 02-Oct-2016

Version 0.2.0, released 08-May-2016

Version 0.1.1, released 10-Apr-2016

Version 0.1.0, released 11-Feb-2016

  • initial release
generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/docs/topics/LICENSE.md.html000066400000000000000000000063661473046163300203700ustar00rootroot00000000000000 Lua-System docs

MIT License

Copyright (c) 2016-2024, Oscar Lim

Copyright (c) 2024, the luasystem project authors.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

generated by LDoc 1.5.0 Last updated 2024-12-18 02:50:03
luasystem-0.4.5/examples/000077500000000000000000000000001473046163300153735ustar00rootroot00000000000000luasystem-0.4.5/examples/compat.lua000066400000000000000000000025571473046163300173720ustar00rootroot00000000000000-- This example shows how to remove platform differences to create a -- cross-platform level playing field. local sys = require "system" if sys.windows then -- Windows holds multiple copies of environment variables, to ensure `getenv` -- returns what `setenv` sets we need to use the `system.getenv` instead of -- `os.getenv`. os.getenv = sys.getenv -- luacheck: ignore -- Set console output to UTF-8 encoding. sys.setconsoleoutputcp(sys.CODEPAGE_UTF8) -- Set up the terminal to handle ANSI escape sequences on Windows. if sys.isatty(io.stdout) then sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) end if sys.isatty(io.stderr) then sys.setconsoleflags(io.stderr, sys.getconsoleflags(io.stderr) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) end if sys.isatty(io.stdin) then sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdout) + sys.ENABLE_VIRTUAL_TERMINAL_INPUT) end else -- On Posix, one can set a variable to an empty string, but on Windows, this -- will remove the variable from the environment. To make this consistent -- across platforms, we will remove the variable from the environment if the -- value is an empty string. local old_setenv = sys.setenv function sys.setenv(name, value) if value == "" then value = nil end return old_setenv(name, value) end end luasystem-0.4.5/examples/flag_debugging.lua000066400000000000000000000002621473046163300210220ustar00rootroot00000000000000local sys = require "system" -- Print the Windows Console flags for stdin sys.listconsoleflags(io.stdin) -- Print the Posix termios flags for stdin sys.listtermflags(io.stdin) luasystem-0.4.5/examples/password_input.lua000066400000000000000000000031011473046163300211520ustar00rootroot00000000000000local sys = require "system" print [[ This example shows how to disable the "echo" of characters read to the console, useful for reading secrets from the user. ]] --- Function to read from stdin without echoing the input (for secrets etc). -- It will (in a platform agnostic way) disable echo on the terminal, read the -- input, and then re-enable echo. -- @param ... Arguments to pass to `io.stdin:read()` -- @return the results of `io.stdin:read(...)` local function read_secret(...) local w_oldflags, p_oldflags if sys.isatty(io.stdin) then -- backup settings, configure echo flags w_oldflags = sys.getconsoleflags(io.stdin) p_oldflags = sys.tcgetattr(io.stdin) -- set echo off to not show password on screen assert(sys.setconsoleflags(io.stdin, w_oldflags - sys.CIF_ECHO_INPUT)) assert(sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = p_oldflags.lflag - sys.L_ECHO })) end local secret, err = io.stdin:read(...) -- restore settings if sys.isatty(io.stdin) then io.stdout:write("\n") -- Add newline after reading the password sys.setconsoleflags(io.stdin, w_oldflags) sys.tcsetattr(io.stdin, sys.TCSANOW, p_oldflags) end return secret, err end -- Get username io.write("Username: ") local username = io.stdin:read("*l") -- Get the secret io.write("Password: ") local password = read_secret("*l") -- Get domainname io.write("Domain : ") local domain = io.stdin:read("*l") -- Print the results print("") print("Here's what we got:") print(" username: " .. username) print(" password: " .. password) print(" domain : " .. domain) luasystem-0.4.5/examples/read.lua000066400000000000000000000036211473046163300170130ustar00rootroot00000000000000local sys = require "system" print [[ This example shows how to do a non-blocking read from the cli. ]] -- setup Windows console to handle ANSI processing local of_in = sys.getconsoleflags(io.stdin) local of_out = sys.getconsoleflags(io.stdout) sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT) -- setup Posix terminal to use non-blocking mode, and disable line-mode local of_attr = sys.tcgetattr(io.stdin) local of_block = sys.getnonblock(io.stdin) sys.setnonblock(io.stdin, true) sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo }) -- cursor sequences local get_cursor_pos = "\27[6n" print("Press a key, or 'A' to get cursor position, 'ESC' to exit") while true do local key, keytype -- wait for a key while not key do key, keytype = sys.readansi(math.huge) end if key == "A" then io.write(get_cursor_pos); io.flush() end -- check if we got a key or ANSI sequence if keytype == "char" then -- just a key local b = key:byte() if b < 32 then key = "." -- replace control characters with a simple "." to not mess up the screen end print("you pressed: " .. key .. " (" .. b .. ")") if b == 27 then print("Escape pressed, exiting") break end elseif keytype == "ansi" then -- we got an ANSI sequence local seq = { key:byte(1, #key) } print("ANSI sequence received: " .. key:sub(2,-1), "(bytes: " .. table.concat(seq, ", ")..")") else print("unknown key type received: " .. tostring(keytype)) end end -- Clean up afterwards sys.setnonblock(io.stdin, false) sys.setconsoleflags(io.stdout, of_out) sys.setconsoleflags(io.stdin, of_in) sys.tcsetattr(io.stdin, sys.TCSANOW, of_attr) sys.setnonblock(io.stdin, of_block) luasystem-0.4.5/examples/readline.lua000066400000000000000000000326731473046163300176740ustar00rootroot00000000000000--- An example class for reading a line of input from the user in a non-blocking way. -- It uses ANSI escape sequences to move the cursor and handle input. -- It can be used to read a line of input from the user, with a prompt. -- It can handle double-width UTF-8 characters. -- It can be used asynchroneously if `system.sleep` is patched to yield to a coroutine scheduler. local sys = require("system") -- Mapping of key-sequences to key-names local key_names = { ["\27[C"] = "right", ["\27[D"] = "left", ["\127"] = "backspace", ["\27[3~"] = "delete", ["\27[H"] = "home", ["\27[F"] = "end", ["\27"] = "escape", ["\9"] = "tab", ["\27[Z"] = "shift-tab", } if sys.windows then key_names["\13"] = "enter" else key_names["\10"] = "enter" end -- Mapping of key-names to key-sequences local key_sequences = {} for k, v in pairs(key_names) do key_sequences[v] = k end -- bell character local function bell() io.write("\7") io.flush() end -- generate string to move cursor horizontally -- positive goes right, negative goes left local function cursor_move_horiz(n) if n == 0 then return "" end return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "C" or "D") end -- -- generate string to move cursor vertically -- -- positive goes down, negative goes up -- local function cursor_move_vert(n) -- if n == 0 then -- return "" -- end -- return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "B" or "A") -- end -- -- log to the line above the current line -- local function log(...) -- local arg = { n = select("#", ...), ...} -- for i = 1, arg.n do -- arg[i] = tostring(arg[i]) -- end -- arg = " " .. table.concat(arg, " ") .. " " -- io.write(cursor_move_vert(-1), arg, cursor_move_vert(1), cursor_move_horiz(-#arg)) -- end -- UTF8 character size in bytes -- @tparam number b the byte value of the first byte of a UTF8 character local function utf8size(b) return b < 128 and 1 or b < 224 and 2 or b < 240 and 3 or b < 248 and 4 end local utf8parse do local utf8_value_mt = { __tostring = function(self) return table.concat(self, "") end, } -- Parses a UTF8 string into list of individual characters. -- key 'chars' gets the length in UTF8 characters, whilst # returns the length -- for display (to handle double-width UTF8 chars). -- in the list the double-width characters are followed by an empty string. -- @tparam string s the UTF8 string to parse -- @treturn table the list of characters function utf8parse(s) local t = setmetatable({ chars = 0 }, utf8_value_mt) local i = 1 while i <= #s do local b = s:byte(i) local w = utf8size(b) local char = s:sub(i, i + w - 1) t[#t + 1] = char t.chars = t.chars + 1 if sys.utf8cwidth(char) == 2 then -- double width character, add empty string to keep the length of the -- list the same as the character width on screen t[#t + 1] = "" end i = i + w end return t end end -- inline tests for utf8parse -- do -- local t = utf8parse("a你b好c") -- assert(t[1] == "a") -- assert(t[2] == "你") -- double width -- assert(t[3] == "") -- assert(t[4] == "b") -- assert(t[5] == "好") -- double width -- assert(t[6] == "") -- assert(t[7] == "c") -- assert(#t == 7) -- size as displayed -- end -- readline class local readline = {} readline.__index = readline --- Create a new readline object. -- @tparam table opts the options for the readline object -- @tparam[opt=""] string opts.prompt the prompt to display -- @tparam[opt=80] number opts.max_length the maximum length of the input (in characters, not bytes) -- @tparam[opt=""] string opts.value the default value -- @tparam[opt=`#value`] number opts.position of the cursor in the input -- @tparam[opt={"\10"/"\13"}] table opts.exit_keys an array of keys that will cause the readline to exit -- @treturn readline the new readline object function readline.new(opts) local value = utf8parse(opts.value or "") local prompt = utf8parse(opts.prompt or "") local pos = math.floor(opts.position or (#value + 1)) pos = math.max(math.min(pos, (#value + 1)), 1) local len = math.floor(opts.max_length or 80) if len < 1 then error("max_length must be at least 1", 2) end if value.chars > len then error("value is longer than max_length", 2) end local exit_keys = {} for _, key in ipairs(opts.exit_keys or {}) do exit_keys[key] = true end if exit_keys[1] == nil then -- nothing provided, default to Enter-key exit_keys[1] = key_sequences.enter end local self = { value = value, -- the default value max_length = len, -- the maximum length of the input prompt = prompt, -- the prompt to display position = pos, -- the current position in the input drawn_before = false, -- if the prompt has been drawn exit_keys = exit_keys, -- the keys that will cause the readline to exit } setmetatable(self, readline) return self end -- draw the prompt and the input value, and position the cursor. local function draw(self, redraw) if redraw or not self.drawn_before then -- we are at start of prompt self.drawn_before = true else -- we are at current cursor position, move to start of prompt io.write(cursor_move_horiz(-(#self.prompt + self.position))) end -- write prompt & value io.write(tostring(self.prompt) .. tostring(self.value)) -- clear remainder of input size io.write(string.rep(" ", self.max_length - self.value.chars)) io.write(cursor_move_horiz(-(self.max_length - self.value.chars))) -- move to cursor position io.write(cursor_move_horiz(-(#self.value + 1 - self.position))) io.flush() end local handle_key do -- keyboard input handler local key_handlers key_handlers = { left = function(self) if self.position == 1 then bell() return end local new_pos = self.position - 1 while self.value[new_pos] == "" do -- skip empty strings; double width chars new_pos = new_pos - 1 end io.write(cursor_move_horiz(-(self.position - new_pos))) io.flush() self.position = new_pos end, right = function(self) if self.position == #self.value + 1 then bell() return end local new_pos = self.position + 1 while self.value[new_pos] == "" do -- skip empty strings; double width chars new_pos = new_pos + 1 end io.write(cursor_move_horiz(new_pos - self.position)) io.flush() self.position = new_pos end, backspace = function(self) if self.position == 1 then bell() return end while self.value[self.position - 1] == "" do -- remove empty strings; double width chars io.write(cursor_move_horiz(-1)) self.position = self.position - 1 table.remove(self.value, self.position) end -- remove char itself io.write(cursor_move_horiz(-1)) self.position = self.position - 1 table.remove(self.value, self.position) self.value.chars = self.value.chars - 1 draw(self) end, home = function(self) local new_pos = 1 io.write(cursor_move_horiz(new_pos - self.position)) self.position = new_pos end, ["end"] = function(self) local new_pos = #self.value + 1 io.write(cursor_move_horiz(new_pos - self.position)) self.position = new_pos end, delete = function(self) if self.position > #self.value then bell() return end key_handlers.right(self) key_handlers.backspace(self) end, } -- handles a single input key/ansi-sequence. -- @tparam string key the key or ansi-sequence (from `system.readansi`) -- @tparam string keytype the type of the key, either "char" or "ansi" (from `system.readansi`) -- @treturn string status the status of the key handling, either "ok", "exit_key" or an error message function handle_key(self, key, keytype) if self.exit_keys[key] then -- registered exit key return "exit_key" end local handler = key_handlers[key_names[key] or true ] if handler then handler(self) return "ok" end if keytype == "ansi" then -- we got an ansi sequence, but dunno how to handle it, ignore -- print("unhandled ansi: ", key:sub(2,-1), string.byte(key, 1, -1)) bell() return "ok" end -- just a single key if key < " " then -- control character bell() return "ok" end if self.value.chars >= self.max_length then bell() return "ok" end -- insert the key into the value if sys.utf8cwidth(key) == 2 then -- double width character, insert empty string after it table.insert(self.value, self.position, "") table.insert(self.value, self.position, key) self.position = self.position + 2 io.write(cursor_move_horiz(2)) else table.insert(self.value, self.position, key) self.position = self.position + 1 io.write(cursor_move_horiz(1)) end self.value.chars = self.value.chars + 1 draw(self) return "ok" end end --- Get_size returns the maximum size of the input box (prompt + input). -- The size is in rows and columns. Columns is determined by -- the prompt and the `max_length * 2` (characters can be double-width). -- @treturn number the number of rows (always 1) -- @treturn number the number of columns function readline:get_size() return 1, #self.prompt + self.max_length * 2 end --- Get coordinates of the cursor in the input box (prompt + input). -- The coordinates are 1-based. They are returned as row and column, within the -- size as reported by `get_size`. -- @treturn number the row of the cursor (always 1) -- @treturn number the column of the cursor function readline:get_cursor() return 1, #self.prompt + self.position end --- Set the coordinates of the cursor in the input box (prompt + input). -- The coordinates are 1-based. They are expected to be within the -- size as reported by `get_size`, and beyond the prompt. -- If the position is invalid, it will be corrected. -- Use the results to check if the position was adjusted. -- @tparam number row the row of the cursor (always 1) -- @tparam number col the column of the cursor -- @return results of get_cursor function readline:set_cursor(row, col) local l_prompt = #self.prompt local l_value = #self.value if col < l_prompt + 1 then col = l_prompt + 1 elseif col > l_prompt + l_value + 1 then col = l_prompt + l_value + 1 end while self.value[col - l_prompt] == "" do col = col - 1 -- on an empty string, so move back to start of double-width char end local new_pos = col - l_prompt cursor_move_horiz(self.position - new_pos) io.flush() self.position = new_pos return self:get_cursor() end --- Read a line of input from the user. -- It will first print the `prompt` and then wait for input. Ensure the cursor -- is at the correct position before calling this function. This function will -- do all cursor movements in a relative way. -- Can be called again after an exit-key or timeout has occurred. Just make sure -- the cursor is at the same position where is was when it returned the last time. -- Alternatively the cursor can be set to the position of the prompt (the position -- the cursor was in before the first call), and the parameter `redraw` can be set -- to `true`. -- @tparam[opt=math.huge] number timeout the maximum time to wait for input in seconds -- @tparam[opt=false] boolean redraw if `true` the prompt will be redrawn (cursor must be at prompt position!) -- @treturn[1] string the input string as entered the user -- @treturn[1] string the exit-key used to exit the readline (see `new`) -- @treturn[2] nil when input is incomplete -- @treturn[2] string error message, the reason why the input is incomplete, `"timeout"`, or an error reading a key function readline:__call(timeout, redraw) draw(self, redraw) timeout = timeout or math.huge local timeout_end = sys.gettime() + timeout while true do local key, keytype = sys.readansi(timeout_end - sys.gettime()) if not key then -- error or timeout return nil, keytype end local status = handle_key(self, key, keytype) if status == "exit_key" then return tostring(self.value), key elseif status ~= "ok" then error("unknown status received: " .. tostring(status)) end end end -- return readline -- normally we'd return here, but for the example we continue local backup = sys.termbackup() -- setup Windows console to handle ANSI processing sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT) -- set output to UTF-8 sys.setconsoleoutputcp(sys.CODEPAGE_UTF8) -- setup Posix terminal to disable canonical mode and echo sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = sys.tcgetattr(io.stdin).lflag - sys.L_ICANON - sys.L_ECHO, }) -- setup stdin to non-blocking mode sys.setnonblock(io.stdin, true) local rl = readline.new{ prompt = "Enter something: ", max_length = 60, value = "Hello, 你-好 World 🚀!", -- position = 2, exit_keys = {key_sequences.enter, "\27", "\t", "\27[Z"}, -- enter, escape, tab, shift-tab } local result, key = rl() print("") -- newline after input, to move cursor down from the input line print("Result (string): '" .. result .. "'") print("Result (bytes):", result:byte(1,-1)) print("Exit-Key (bytes):", key:byte(1,-1)) -- Clean up afterwards sys.termrestore(backup) luasystem-0.4.5/examples/spinner.lua000066400000000000000000000022511473046163300175540ustar00rootroot00000000000000local sys = require("system") print [[ An example to display a spinner, whilst a long running task executes. ]] -- start make backup, to auto-restore on exit sys.autotermrestore() -- configure console sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT) local of = sys.tcgetattr(io.stdin) sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = of.lflag - sys.L_ICANON - sys.L_ECHO }) sys.setnonblock(io.stdin, true) local function hideCursor() io.write("\27[?25l") io.flush() end local function showCursor() io.write("\27[?25h") io.flush() end local function left(n) io.write("\27[",n or 1,"D") io.flush() end local spinner do local spin = [[|/-\]] local i = 1 spinner = function() hideCursor() io.write(spin:sub(i, i)) left() i = i + 1 if i > #spin then i = 1 end if sys.readkey(0) ~= nil then while sys.readkey(0) ~= nil do end -- consume keys pressed io.write(" "); left() showCursor() return true else return false end end end io.stdout:write("press any key to stop the spinner... ") while not spinner() do sys.sleep(0.1) end print("Done!") luasystem-0.4.5/examples/spiral_snake.lua000066400000000000000000000032041473046163300205500ustar00rootroot00000000000000local sys = require "system" print [[ This example will draw a snake like spiral on the screen. Showing ANSI escape codes for moving the cursor around. ]] -- backup term settings with auto-restore on exit sys.autotermrestore() -- setup Windows console to handle ANSI processing sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) -- start drawing the spiral. -- start from current pos, then right, then up, then left, then down, and again. local x, y = 1, 1 -- current position local dx, dy = 1, 0 -- direction after each step local wx, wy = 30, 30 -- width and height of the room local mx, my = 1, 1 -- margin -- commands to move the cursor local move_left = "\27[1D" local move_right = "\27[1C" local move_up = "\27[1A" local move_down = "\27[1B" -- create room: 30 empty lines print(("\n"):rep(wy)) local move = move_right while wx > 0 and wy > 0 do sys.sleep(0.01) -- slow down the drawing a little io.write("*" .. move_left .. move ) io.flush() x = x + dx y = y + dy if x > wx and move == move_right then -- end of move right dx = 0 dy = 1 move = move_up wy = wy - 1 my = my + 1 elseif y > wy and move == move_up then -- end of move up dx = -1 dy = 0 move = move_left wx = wx - 1 mx = mx + 1 elseif x < mx and move == move_left then -- end of move left dx = 0 dy = -1 move = move_down wy = wy - 1 my = my + 1 elseif y < my and move == move_down then -- end of move down dx = 1 dy = 0 move = move_right wx = wx - 1 mx = mx + 1 end end io.write(move_down:rep(15)) print("\nDone!") luasystem-0.4.5/examples/terminalsize.lua000066400000000000000000000023631473046163300206100ustar00rootroot00000000000000local sys = require("system") sys.autotermrestore() -- set up auto restore of terminal settings on exit -- setup Windows console to handle ANSI processing sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT) -- setup Posix to disable canonical mode and echo local of_attr = sys.tcgetattr(io.stdin) sys.setnonblock(io.stdin, true) sys.tcsetattr(io.stdin, sys.TCSANOW, { lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo }) -- generate string to move cursor horizontally -- positive goes right, negative goes left local function cursor_move_horiz(n) if n == 0 then return "" end return "\27[" .. (n > 0 and n or -n) .. (n > 0 and "C" or "D") end local rows, cols print("Change the terminal window size, press any key to exit") while not sys.readansi(0.2) do -- use readansi to not leave stray bytes in the input buffer local nrows, ncols = sys.termsize() if rows ~= nrows or cols ~= ncols then rows, cols = nrows, ncols local text = "Terminal size: " .. rows .. "x" .. cols .. " " io.write(text .. cursor_move_horiz(-#text)) io.flush() end end luasystem-0.4.5/luasystem-scm-0.rockspec000066400000000000000000000037251473046163300202620ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "scm" local rockspec_revision = "0" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/000077500000000000000000000000001473046163300155515ustar00rootroot00000000000000luasystem-0.4.5/rockspecs/luasystem-0.2.1-0.rockspec000066400000000000000000000024331473046163300221250ustar00rootroot00000000000000package = 'luasystem' version = '0.2.1-0' source = { url = "https://github.com/o-lim/luasystem/archive/v0.2.1.tar.gz", dir = "luasystem-0.2.1" } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], homepage = 'http://olivinelabs.com/luasystem/', license = 'MIT ' } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { }, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', }, defines = defines[plat], libraries = libraries[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.2.1-1.rockspec000066400000000000000000000032051473046163300221240ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.2.1" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { }, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', }, defines = defines[plat], libraries = libraries[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.3.0-1.rockspec000066400000000000000000000034201473046163300221230ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.3.0" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', }, defines = defines[plat], libraries = libraries[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.3.0-2.rockspec000066400000000000000000000036401473046163300221300ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.3.0" local rockspec_revision = "2" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.0-1.rockspec000066400000000000000000000037271473046163300221360ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.0" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.1-1.rockspec000066400000000000000000000037271473046163300221370ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.1" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.2-1.rockspec000066400000000000000000000037271473046163300221400ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.2" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.3-1.rockspec000066400000000000000000000037271473046163300221410ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.3" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.4-1.rockspec000066400000000000000000000037271473046163300221420ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.4" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/rockspecs/luasystem-0.4.5-1.rockspec000066400000000000000000000037271473046163300221430ustar00rootroot00000000000000local package_name = "luasystem" local package_version = "0.4.5" local rockspec_revision = "1" local github_account_name = "lunarmodules" local github_repo_name = "luasystem" package = package_name version = package_version.."-"..rockspec_revision source = { url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", branch = (package_version == "scm") and "master" or nil, tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { summary = 'Platform independent system calls for Lua.', detailed = [[ Adds a Lua API for making platform independent system calls. ]], license = 'MIT ', homepage = "https://github.com/"..github_account_name.."/"..github_repo_name, } dependencies = { 'lua >= 5.1', } local function make_platform(plat) local defines = { linux = { }, unix = { }, macosx = { }, win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" }, } local libraries = { linux = { "rt" }, unix = { }, macosx = { }, win32 = { "advapi32", "winmm" }, mingw32 = { }, } local libdirs = { linux = nil, unix = nil, macosx = nil, win32 = nil, mingw32 = { }, } return { modules = { ['system.core'] = { sources = { 'src/core.c', 'src/compat.c', 'src/time.c', 'src/environment.c', 'src/random.c', 'src/term.c', 'src/bitflags.c', 'src/wcwidth.c', }, defines = defines[plat], libraries = libraries[plat], libdirs = libdirs[plat], }, }, } end build = { type = 'builtin', platforms = { linux = make_platform('linux'), unix = make_platform('unix'), macosx = make_platform('macosx'), win32 = make_platform('win32'), mingw32 = make_platform('mingw32'), }, modules = { ['system.init'] = 'system/init.lua', }, } luasystem-0.4.5/spec/000077500000000000000000000000001473046163300145075ustar00rootroot00000000000000luasystem-0.4.5/spec/01-time_spec.lua000066400000000000000000000040141473046163300173770ustar00rootroot00000000000000local system = require 'system.core' describe('Test time functions', function() -- returns the new second, on the new second local function wait_for_second_rollover() local start_time = math.floor(os.time()) local end_time = math.floor(os.time()) while end_time == start_time do end_time = math.floor(os.time()) end return end_time end describe("time()", function() it('returns current time', function() local expected_time = wait_for_second_rollover() local received_time = system.gettime() assert.is.near(expected_time, received_time, 0.02) wait_for_second_rollover() assert.is.near(1, system.gettime() - received_time, 0.02) end) end) describe("monotime()", function() it('returns monotonically increasing time', function() local starttime = system.monotime() local endtime = system.monotime() local delta = endtime - starttime assert.is_true(starttime > 0) assert.is_true(delta >= 0) assert.is_true(system.monotime() - endtime >= 0) end) end) describe("sleep()", function() it("should sleep for the specified time", function() local start_time = system.gettime() system.sleep(1, 1) local end_time = system.gettime() local elapsed_time = end_time - start_time assert.is.near(elapsed_time, 1, 0.2) -- large marging of error due to CI priorities end) it("should sleep for the specified time; fractional", function() local start_time = system.gettime() system.sleep(0.5, 1) local end_time = system.gettime() local elapsed_time = end_time - start_time assert.is.near(0.5, elapsed_time, 0.2) -- large marging of error due to CI priorities end) it("should return immediately for a non-positive sleep time", function() local start_time = system.gettime() system.sleep(-1) local end_time = system.gettime() local elapsed_time = end_time - start_time assert.is.near(elapsed_time, 0, 0.01) end) end) end) luasystem-0.4.5/spec/02-random_spec.lua000066400000000000000000000024171473046163300177270ustar00rootroot00000000000000local system = require("system") describe("Random:", function() describe("random()", function() it("should return random bytes for a valid number of bytes", function() local num_bytes = 1 local result, err_msg = system.random(num_bytes) assert.is_nil(err_msg) assert.is.string(result) assert.is_equal(num_bytes, #result) end) it("should return an empty string for 0 bytes", function() local num_bytes = 0 local result, err_msg = system.random(num_bytes) assert.is_nil(err_msg) assert.are.equal("", result) end) it("should return an error message for an invalid number of bytes", function() local num_bytes = -1 local result, err_msg = system.random(num_bytes) assert.is.falsy(result) assert.are.equal("invalid number of bytes, must not be less than 0", err_msg) end) it("should not return duplicate results", function() local num_bytes = 1025 local result1, err_msg = system.random(num_bytes) assert.is_nil(err_msg) assert.is.string(result1) local result2, err_msg = system.random(num_bytes) assert.is_nil(err_msg) assert.is.string(result2) assert.is_not.equal(result1, result2) end) end) end) luasystem-0.4.5/spec/03-environment_spec.lua000066400000000000000000000046311473046163300210140ustar00rootroot00000000000000-- Import the library that contains the environment-related functions local system = require("system") require("spec.helpers") describe("Environment Variables:", function() describe("setenv()", function() it("should set an environment variable", function() assert.is_true(system.setenv("TEST_VAR", "test_value")) assert.is_equal("test_value", system.getenv("TEST_VAR")) end) -- Windows will unset a variable if set as an empty string nix_it("should set an empty environment variable value", function() assert.is_true(system.setenv("TEST_VAR", "")) assert.is_equal("", system.getenv("TEST_VAR")) end) it("should unset an environment variable on nil", function() assert.is_true(system.setenv("TEST_VAR", "test_value")) assert.is_equal("test_value", system.getenv("TEST_VAR")) assert.is_true(system.setenv("TEST_VAR", nil)) assert.is_nil(system.getenv("TEST_VAR")) end) it("should error on input bad type", function() assert.has_error(function() system.setenv("TEST_VAR", {}) end) assert.has_error(function() system.setenv({}, "test_value") end) end) it("should return success on deleting a variable that doesn't exist", function() if system.getenv("TEST_VAR") ~= nil then -- clear if it was already set assert.is_true(system.setenv("TEST_VAR", nil)) end assert.is_true(system.setenv("TEST_VAR", nil)) -- clear again shouldn't fail end) end) describe("getenvs()", function() it("should list environment variables", function() assert.is_true(system.setenv("TEST_VAR1", nil)) assert.is_true(system.setenv("TEST_VAR2", nil)) assert.is_true(system.setenv("TEST_VAR3", nil)) local envVars1 = system.getenvs() assert.is_true(system.setenv("TEST_VAR1", "test_value1")) assert.is_true(system.setenv("TEST_VAR2", "test_value2")) assert.is_true(system.setenv("TEST_VAR3", "test_value3")) local envVars2 = system.getenvs() assert.is_true(system.setenv("TEST_VAR1", nil)) assert.is_true(system.setenv("TEST_VAR2", nil)) assert.is_true(system.setenv("TEST_VAR3", nil)) for k,v in pairs(envVars1) do envVars2[k] = nil end assert.are.same({ TEST_VAR1 = "test_value1", TEST_VAR2 = "test_value2", TEST_VAR3 = "test_value3", }, envVars2) end) end) end) luasystem-0.4.5/spec/04-term_helper.lua000066400000000000000000000007041473046163300177420ustar00rootroot00000000000000-- sub-script executed for isatty test local writefile = require("pl.utils").writefile local isatty = require("system").isatty assert(arg[1] == "--", "missing -- argument") local tempfile = assert(arg[2], "missing tempfile argument") -- print("my temp file: ", tempfile) assert(writefile(tempfile, [[{ stdin = ]]..tostring(isatty(io.stdin))..[[, stdout = ]]..tostring(isatty(io.stdout))..[[, stderr = ]]..tostring(isatty(io.stderr))..[[, }]])) luasystem-0.4.5/spec/04-term_spec.lua000066400000000000000000000622631473046163300174250ustar00rootroot00000000000000local system = require("system") require("spec.helpers") describe("Terminal:", function() local wincodepage setup(function() wincodepage = system.getconsoleoutputcp() assert(system.setconsoleoutputcp(system.CODEPAGE_UTF8)) -- set to UTF8 end) teardown(function() assert(system.setconsoleoutputcp(wincodepage)) end) describe("isatty()", function() local newtmpfile = require("pl.path").tmpname -- set each param to true to make it a tty, to false for a stream local function getttyresults(sin, sout, serr) assert(type(sin) == "boolean", "sin must be a boolean") assert(type(sout) == "boolean", "sout must be a boolean") assert(type(serr) == "boolean", "serr must be a boolean") local tmpfile = "./spec/04-term_helper.output" local lua_bin = system.getenv("LUA") or "lua" local execcmd = lua_bin .. " ./spec/04-term_helper.lua -- " .. tmpfile sin = sin and "" or 'echo "hello" | ' if system.windows then sout = sout and "" or (" > " .. newtmpfile()) serr = serr and "" or (" 2> " .. newtmpfile()) else sout = sout and "" or (" > " .. newtmpfile()) serr = serr and "" or (" 2> " .. newtmpfile()) end local cmd = sin .. execcmd .. sout .. serr -- print("cmd: ", cmd) os.remove(tmpfile) assert(os.execute(cmd)) local result = assert(require("pl.utils").readfile(tmpfile)) os.remove(tmpfile) -- print("result: ", result) return assert(require("pl.compat").load("return " .. result))() end it("returns true for all if a terminal #manual", function() assert.are.same( { stdin = true, stdout = true, stderr = true, }, getttyresults(true, true, true) ) end) it("returns false for stdin if not a terminal #manual", function() assert.are.same( { stdin = false, stdout = true, stderr = true, }, getttyresults(false, true, true) ) end) it("returns false for stdout if not a terminal #manual", function() assert.are.same( { stdin = true, stdout = false, stderr = true, }, getttyresults(true, false, true) ) end) it("returns false for stderr if not a terminal #manual", function() assert.are.same( { stdin = true, stdout = true, stderr = false, }, getttyresults(true, true, false) ) end) end) describe("getconsoleflags()", function() win_it("returns the consoleflags #manual", function() local flags, err = system.getconsoleflags(io.stdout) assert.is_nil(err) assert.is_userdata(flags) assert.equals("bitflags:", tostring(flags):sub(1,9)) end) nix_it("returns the consoleflags, always value 0", function() local flags, err = system.getconsoleflags(io.stdout) assert.is_nil(err) assert.is_userdata(flags) assert.equals("bitflags:", tostring(flags):sub(1,9)) assert.equals(0, flags:value()) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.getconsoleflags("invalid") end, "bad argument #1 to 'getconsoleflags' (FILE* expected, got string)") end) end) describe("setconsoleflags()", function() win_it("sets the consoleflags #manual", function() local old_flags = assert(system.getconsoleflags(io.stdout)) finally(function() system.setconsoleflags(io.stdout, old_flags) -- ensure we restore the original ones end) local new_flags if old_flags:has_all_of(system.COF_VIRTUAL_TERMINAL_PROCESSING) then new_flags = old_flags - system.COF_VIRTUAL_TERMINAL_PROCESSING else new_flags = old_flags + system.COF_VIRTUAL_TERMINAL_PROCESSING end local success, err = system.setconsoleflags(io.stdout, new_flags) assert.is_nil(err) assert.is_true(success) local updated_flags = assert(system.getconsoleflags(io.stdout)) assert.equals(new_flags:value(), updated_flags:value()) end) nix_it("sets the consoleflags, always succeeds", function() assert(system.setconsoleflags(io.stdout, system.getconsoleflags(io.stdout))) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.setconsoleflags("invalid") end, "bad argument #1 to 'setconsoleflags' (FILE* expected, got string)") end) end) describe("tcgetattr()", function() nix_it("gets the terminal flags #manual", function() local flags, err = system.tcgetattr(io.stdin) assert.is_nil(err) assert.is_table(flags) assert.equals("bitflags:", tostring(flags.iflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.oflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.lflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.cflag):sub(1,9)) assert.not_equal(0, flags.iflag:value()) assert.not_equal(0, flags.oflag:value()) assert.not_equal(0, flags.lflag:value()) assert.not_equal(0, flags.cflag:value()) assert.is.table(flags.cc) end) win_it("gets the terminal flags, always 0", function() local flags, err = system.tcgetattr(io.stdin) assert.is_nil(err) assert.is_table(flags) assert.equals("bitflags:", tostring(flags.iflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.oflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.lflag):sub(1,9)) assert.equals("bitflags:", tostring(flags.cflag):sub(1,9)) assert.equals(0, flags.iflag:value()) assert.equals(0, flags.oflag:value()) assert.equals(0, flags.lflag:value()) assert.equals(0, flags.cflag:value()) assert.same({}, flags.cc) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.tcgetattr("invalid") end, "bad argument #1 to 'tcgetattr' (FILE* expected, got string)") end) end) describe("tcsetattr()", function() nix_it("sets the terminal flags, if called with flags #manual", function() -- system.listtermflags(io.stdin) local old_flags = assert(system.tcgetattr(io.stdin)) finally(function() system.tcsetattr(io.stdin, system.TCSANOW, old_flags) -- ensure we restore the original ones end) local new_flags = assert(system.tcgetattr(io.stdin)) -- just get them again, and then update -- change iflags local flag_to_change = system.I_IGNCR if new_flags.iflag:has_all_of(flag_to_change) then new_flags.iflag = new_flags.iflag - flag_to_change else new_flags.iflag = new_flags.iflag + flag_to_change end -- change oflags flag_to_change = system.O_OPOST if new_flags.oflag:has_all_of(flag_to_change) then new_flags.oflag = new_flags.oflag - flag_to_change else new_flags.oflag = new_flags.oflag + flag_to_change end -- change lflags flag_to_change = system.L_ECHO if new_flags.lflag:has_all_of(flag_to_change) then new_flags.lflag = new_flags.lflag - flag_to_change else new_flags.lflag = new_flags.lflag + flag_to_change end assert(system.tcsetattr(io.stdin, system.TCSANOW, new_flags)) local updated_flags = assert(system.tcgetattr(io.stdin)) assert.equals(new_flags.iflag:value(), updated_flags.iflag:value()) assert.equals(new_flags.oflag:value(), updated_flags.oflag:value()) assert.equals(new_flags.lflag:value(), updated_flags.lflag:value()) end) win_it("sets the terminal flags, if called with flags, always succeeds", function() local success, err = system.tcsetattr(io.stdin, system.TCSANOW, system.tcgetattr(io.stdin)) assert.is_nil(err) assert.is_true(success) end) it("returns an error if called with an invalid first argument", function() assert.has.error(function() system.tcsetattr("invalid", system.TCSANOW, {}) end, "bad argument #1 to 'tcsetattr' (FILE* expected, got string)") end) it("returns an error if called with an invalid second argument", function() assert.has.error(function() system.tcsetattr(io.stdin, "invalid", {}) end, "bad argument #2 to 'tcsetattr' (number expected, got string)") end) it("returns an error if called with an invalid third argument #manual", function() assert.has.error(function() system.tcsetattr(io.stdin, system.TCSANOW, "invalid") end, "bad argument #3 to 'tcsetattr' (table expected, got string)") end) it("returns an error if iflag is not a bitflags object #manual", function() local flags = assert(system.tcgetattr(io.stdin)) flags.iflag = 0 assert.has.error(function() system.tcsetattr(io.stdin, system.TCSANOW, flags) end, "bad argument #3, field 'iflag' must be a bitflag object") end) it("returns an error if oflag is not a bitflags object #manual", function() local flags = assert(system.tcgetattr(io.stdin)) flags.oflag = 0 assert.has.error(function() system.tcsetattr(io.stdin, system.TCSANOW, flags) end, "bad argument #3, field 'oflag' must be a bitflag object") end) it("returns an error if lflag is not a bitflags object #manual", function() local flags = assert(system.tcgetattr(io.stdin)) flags.lflag = 0 assert.has.error(function() system.tcsetattr(io.stdin, system.TCSANOW, flags) end, "bad argument #3, field 'lflag' must be a bitflag object") end) end) describe("getconsolecp()", function() win_it("gets the console codepage", function() local cp, err = system.getconsolecp() assert.is_nil(err) assert.is_number(cp) end) nix_it("gets the console codepage, always 65001 (utf8)", function() local cp, err = system.getconsolecp() assert.is_nil(err) assert.equals(65001, cp) end) end) describe("setconsolecp()", function() win_it("sets the console codepage", function() local old_cp = assert(system.getconsolecp()) finally(function() system.setconsolecp(old_cp) -- ensure we restore the original one end) local new_cp if old_cp ~= system.CODEPAGE_UTF8 then new_cp = system.CODEPAGE_UTF8 -- set to UTF8 else new_cp = 850 -- another common one end local success, err = system.setconsolecp(new_cp) assert.is_nil(err) assert.is_true(success) local updated_cp = assert(system.getconsolecp()) assert.equals(new_cp, updated_cp) end) nix_it("sets the console codepage, always succeeds", function() assert(system.setconsolecp(850)) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.setconsolecp("invalid") end, "bad argument #1 to 'setconsolecp' (number expected, got string)") end) end) describe("getconsoleoutputcp()", function() win_it("gets the console output codepage", function() local cp, err = system.getconsoleoutputcp() assert.is_nil(err) assert.is_number(cp) end) nix_it("gets the console output codepage, always 65001 (utf8)", function() local cp, err = system.getconsoleoutputcp() assert.is_nil(err) assert.equals(65001, cp) end) end) describe("setconsoleoutputcp()", function() win_it("sets the console output codepage", function() local old_cp = assert(system.getconsoleoutputcp()) finally(function() system.setconsoleoutputcp(old_cp) -- ensure we restore the original one end) local new_cp if old_cp ~= system.CODEPAGE_UTF8 then new_cp = system.CODEPAGE_UTF8 -- set to UTF8 else new_cp = 850 -- another common one end local success, err = system.setconsoleoutputcp(new_cp) assert.is_nil(err) assert.is_true(success) local updated_cp = assert(system.getconsoleoutputcp()) assert.equals(new_cp, updated_cp) end) nix_it("sets the console output codepage, always succeeds", function() assert(system.setconsoleoutputcp(850)) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.setconsoleoutputcp("invalid") end, "bad argument #1 to 'setconsoleoutputcp' (number expected, got string)") end) end) describe("getnonblock()", function() nix_it("gets the non-blocking flag", function() local nb, err = system.getnonblock(io.stdin) assert.is_nil(err) assert.is_boolean(nb) end) win_it("gets the non-blocking flag, always false", function() local nb, err = system.getnonblock(io.stdin) assert.is_nil(err) assert.is_false(nb) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.getnonblock("invalid") end, "bad argument #1 to 'getnonblock' (FILE* expected, got string)") end) end) describe("setnonblock()", function() nix_it("sets the non-blocking flag", function() local old_nb = system.getnonblock(io.stdin) assert.is.boolean(old_nb) finally(function() system.setnonblock(io.stdin, old_nb) -- ensure we restore the original one end) local new_nb = not old_nb local success, err = system.setnonblock(io.stdin, new_nb) assert.is_nil(err) assert.is_true(success) local updated_nb = assert(system.getnonblock(io.stdin)) assert.equals(new_nb, updated_nb) end) win_it("sets the non-blocking flag, always succeeds", function() local success, err = system.setnonblock(io.stdin, true) assert.is_nil(err) assert.is_true(success) end) it("returns an error if called with an invalid argument", function() assert.has.error(function() system.setnonblock("invalid") end, "bad argument #1 to 'setnonblock' (FILE* expected, got string)") end) end) describe("termsize() #manual", function() it("gets the terminal size", function() local rows, columns = system.termsize() assert.is_number(rows) assert.is_number(columns) end) end) describe("utf8cwidth()", function() local ch1 = string.char(226, 130, 172) -- "€" single local ch2 = string.char(240, 159, 154, 128) -- "🚀" double local ch3 = string.char(228, 189, 160) -- "你" double local ch4 = string.char(229, 165, 189) -- "好" double it("handles zero width characters", function() assert.same({0}, {system.utf8cwidth("")}) -- empty string returns 0-size assert.same({nil, 'Character width determination failed'}, {system.utf8cwidth("\a")}) -- bell character assert.same({nil, 'Character width determination failed'}, {system.utf8cwidth("\27")}) -- escape character end) it("handles single width characters", function() assert.same({1}, {system.utf8cwidth("a")}) assert.same({1}, {system.utf8cwidth(ch1)}) end) it("handles double width characters", function() assert.same({2}, {system.utf8cwidth(ch2)}) assert.same({2}, {system.utf8cwidth(ch3)}) assert.same({2}, {system.utf8cwidth(ch4)}) end) it("returns the width of the first character in the string", function() assert.same({nil, 'Character width determination failed'}, {system.utf8cwidth("\a" .. ch1)}) -- bell character + EURO assert.same({1}, {system.utf8cwidth(ch1 .. ch2)}) assert.same({2}, {system.utf8cwidth(ch2 .. ch3 .. ch4)}) end) end) describe("utf8swidth()", function() local ch1 = string.char(226, 130, 172) -- "€" single local ch2 = string.char(240, 159, 154, 128) -- "🚀" double local ch3 = string.char(228, 189, 160) -- "你" double local ch4 = string.char(229, 165, 189) -- "好" double it("handles zero width characters", function() assert.same({0}, {system.utf8swidth("")}) -- empty string returns 0-size assert.same({nil, 'Character width determination failed'}, {system.utf8swidth("\a")}) -- bell character assert.same({nil, 'Character width determination failed'}, {system.utf8swidth("\27")}) -- escape character end) it("handles multi-character UTF8 strings", function() assert.same({15}, {system.utf8swidth("hello " .. ch1 .. ch2 .. " world")}) assert.same({16}, {system.utf8swidth("hello " .. ch3 .. ch4 .. " world")}) end) end) describe("termbackup() & termrestore()", function() -- this is all Lua code, so testing one platform should be good enough win_it("creates and restores a backup", function() local backup = system.termbackup() local old_cp = assert(system.getconsoleoutputcp()) finally(function() system.setconsoleoutputcp(old_cp) -- ensure we restore the original one end) -- get the console page... local new_cp if old_cp ~= system.CODEPAGE_UTF8 then new_cp = system.CODEPAGE_UTF8 -- set to UTF8 else new_cp = 850 -- another common one end -- change the console page... local success, err = system.setconsoleoutputcp(new_cp) assert.is_nil(err) assert.is_true(success) -- ... and check it local updated_cp = assert(system.getconsoleoutputcp()) assert.equals(new_cp, updated_cp) -- restore the console page system.termrestore(backup) local restored_cp = assert(system.getconsoleoutputcp()) assert.equals(old_cp, restored_cp) end) it("termrestore() fails on bad input", function() assert.has.error(function() system.termrestore("invalid") end, "arg #1 to termrestore, expected backup table, got string") end) end) describe("termwrap()", function() local old_backup local old_restore local result setup(function() old_backup = system.termbackup old_restore = system.termrestore system.termbackup = function() table.insert(result,"backup") end system.termrestore = function() table.insert(result,"restore") end end) before_each(function() result = {} end) teardown(function() system.termbackup = old_backup system.termrestore = old_restore end) it("calls both backup and restore", function() system.termwrap(function() table.insert(result,"wrapped") end)() assert.are.same({"backup", "wrapped", "restore"}, result) end) it("passes all args", function() system.termwrap(function(...) table.insert(result,{...}) end)(1, 2, 3) assert.are.same({"backup", {1,2,3}, "restore"}, result) end) it("returns all results", function() local a, b, c = system.termwrap(function(...) return 1, nil, 3 -- ensure nil is passed as well end)() assert.are.same({"backup", "restore"}, result) assert.equals(1, a) assert.is_nil(b) assert.equals(3, c) end) end) describe("autotermrestore()", function() local old_backup local old_restore local result before_each(function() _G._TEST = true package.loaded["system"] = nil system = require("system") old_backup = system.termbackup old_restore = system.termrestore system.termbackup = function(...) table.insert(result,"backup") return old_backup(...) end system.termrestore = function(...) table.insert(result,"restore") return old_restore(...) end result = {} end) after_each(function() -- system.termbackup = old_backup -- system.termrestore = old_restore _G._TEST = false package.loaded["system"] = nil system = require("system") end) it("calls backup", function() local ok, err = system.autotermrestore() assert.is_nil(err) assert.is_true(ok) assert.are.same({"backup"}, result) end) it("returns an error on the second call", function() local ok, err = system.autotermrestore() assert.is_nil(err) assert.is_true(ok) local ok, err = system.autotermrestore() assert.are.equal("global terminal backup was already set up", err) assert.is_nil(ok) end) it("calls restore upon being garbage collected", function() local ok, err = system.autotermrestore() assert.is_nil(err) assert.is_true(ok) -- ensure tables from previous tests are GC'ed collectgarbage() collectgarbage() -- clear references result = {} system._reset_global_backup() collectgarbage() collectgarbage() assert.are.same({"restore"}, result) end) end) describe("keyboard input", function() local old_readkey = system._readkey local current_buffer local function setbuffer(str) assert(type(str) == "string", "setbuffer() expects a string") if str == "" then current_buffer = nil else current_buffer = str end end setup(function() system._readkey = function() if not current_buffer then return nil end local ch = current_buffer:byte(1, 1) if #current_buffer == 1 then current_buffer = nil else current_buffer = current_buffer:sub(2, -1) end return ch end end) teardown(function() system._readkey = old_readkey end) describe("readkey()", function() it("fails without a timeout", function() assert.has.error(function() system.readkey() end, "arg #1 to readkey, expected timeout in seconds, got nil") end) it("reads a single byte for single-byte characters", function() setbuffer("abc") assert.equals(string.byte("a"), system.readkey(0)) assert.equals(string.byte("b"), system.readkey(0)) assert.equals(string.byte("c"), system.readkey(0)) end) it("reads a single byte for multi-byte chars", function() setbuffer(string.char(226, 130, 172) .. -- "€" single string.char(240, 159, 154, 128)) -- "🚀" double assert.equals(226, system.readkey(0)) assert.equals(130, system.readkey(0)) assert.equals(172, system.readkey(0)) assert.equals(240, system.readkey(0)) assert.equals(159, system.readkey(0)) assert.equals(154, system.readkey(0)) assert.equals(128, system.readkey(0)) end) it("times out", function() setbuffer("") local timing = system.gettime() assert.same({ nil, "timeout" }, { system.readkey(0.5) }) timing = system.gettime() - timing -- assert.is.near(0.5, timing, 0.1) -- this works in CI for Unix and Windows, but not MacOS (locally it passes) assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI end) end) describe("readansi()", function() it("fails without a timeout", function() assert.has.error(function() system.readansi() end, "arg #1 to readansi, expected timeout in seconds, got nil") end) it("reads a single byte for single-byte characters", function() setbuffer("abc") assert.are.same({"a", "char"}, {system.readansi(0)}) assert.are.same({"b", "char"}, {system.readansi(0)}) assert.are.same({"c", "char"}, {system.readansi(0)}) end) it("reads a multi-byte characters one at a time", function() setbuffer(string.char(226, 130, 172) .. -- "€" single string.char(240, 159, 154, 128)) -- "🚀" double assert.are.same({"€", "char"}, {system.readansi(0)}) assert.are.same({"🚀", "char"}, {system.readansi(0)}) end) it("reads ANSI escape sequences, marked by '['", function() setbuffer("\27[A\27[B\27[C\27[D") assert.are.same({"\27[A", "ansi"}, {system.readansi(0)}) assert.are.same({"\27[B", "ansi"}, {system.readansi(0)}) assert.are.same({"\27[C", "ansi"}, {system.readansi(0)}) assert.are.same({"\27[D", "ansi"}, {system.readansi(0)}) end) it("reads ANSI escape sequences, marked by 'O'", function() setbuffer("\27OA\27OB\27OC\27OD") assert.are.same({"\27OA", "ansi"}, {system.readansi(0)}) assert.are.same({"\27OB", "ansi"}, {system.readansi(0)}) assert.are.same({"\27OC", "ansi"}, {system.readansi(0)}) assert.are.same({"\27OD", "ansi"}, {system.readansi(0)}) end) it("returns a single character if no sequence is found", function() setbuffer("\27\27[A") assert.are.same({"\27", "char"}, {system.readansi(0)}) assert.are.same({"\27[A", "ansi"}, {system.readansi(0)}) end) it("times out", function() setbuffer("") local timing = system.gettime() assert.same({ nil, "timeout" }, { system.readansi(0.5) }) timing = system.gettime() - timing -- assert.is.near(0.5, timing, 0.1) -- this works in CI for Unix and Windows, but not MacOS (locally it passes) assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI end) end) end) end) luasystem-0.4.5/spec/05-bitflags_spec.lua000066400000000000000000000077361473046163300202560ustar00rootroot00000000000000describe("BitFlags library", function() local sys = require("system") it("creates new flag objects", function() local bf = sys.bitflag(255) assert.is_not_nil(bf) assert.are.equal(255, bf:value()) assert.is.userdata(bf) end) it("converts to a hex string", function() local bf = sys.bitflag(255) assert.are.equal("bitflags: 255", tostring(bf)) end) it("handles OR/ADD operations", function() -- one at a time local bf1 = sys.bitflag(1) -- b0001 local bf2 = sys.bitflag(2) -- b0010 local bf3 = bf1 + bf2 -- b0011 assert.are.equal(3, bf3:value()) -- multiple at once local bf4 = sys.bitflag(4+8) -- b1100 local bf5 = bf3 + bf4 -- b1111 assert.are.equal(15, bf5:value()) -- multiple that were already set local bf6 = sys.bitflag(15) -- b1111 local bf7 = sys.bitflag(8+2) -- b1010 local bf8 = bf6 + bf7 -- b1111 assert.are.equal(15, bf8:value()) end) it("handles AND-NOT/SUBSTRACT operations", function() -- one at a time local bf1 = sys.bitflag(3) -- b0011 local bf2 = sys.bitflag(1) -- b0001 local bf3 = bf1 - bf2 -- b0010 assert.are.equal(2, bf3:value()) -- multiple at once local bf4 = sys.bitflag(15) -- b1111 local bf5 = sys.bitflag(8+2) -- b1010 local bf6 = bf4 - bf5 -- b0101 assert.are.equal(5, bf6:value()) -- multiple that were not set local bf7 = sys.bitflag(3) -- b0011 local bf8 = sys.bitflag(15) -- b1111 local bf9 = bf7 - bf8 -- b0000 assert.are.equal(0, bf9:value()) end) it("checks for equality", function() local bf1 = sys.bitflag(4) local bf2 = sys.bitflag(4) local bf3 = sys.bitflag(5) assert.is.True(bf1 == bf2) assert.is.False(bf1 == bf3) end) it("indexes bits correctly", function() local bf = sys.bitflag(4) -- b100 assert.is_true(bf[2]) assert.is_false(bf[1]) end) it("errors on reading invalid bit indexes", function() local bf = sys.bitflag(4) assert.has_error(function() return bf[-10] end, "index out of range") assert.has_error(function() return bf[10000] end, "index out of range") assert.has_no_error(function() return bf.not_a_number end) end) it("sets and clears bits correctly", function() local bf = sys.bitflag(8) -- b1000 bf[1] = true assert.is_true(bf[1]) -- b1010 assert.equals(10, bf:value()) bf[1] = false assert.is_false(bf[1]) -- b1000 assert.equals(8, bf:value()) end) it("errors on setting invalid bit indexes", function() local bf = sys.bitflag(0) assert.has_error(function() bf[-10] = true end, "index out of range") assert.has_error(function() bf[10000] = true end, "index out of range") assert.has_error(function() bf.not_a_number = true end, "index must be a number") end) it("checks for a subset using 'has_all_of'", function() local bf1 = sys.bitflag(3) -- b0011 local bf2 = sys.bitflag(3) -- b0011 local bf3 = sys.bitflag(15) -- b1111 local bf0 = sys.bitflag(0) -- b0000 assert.is_true(bf1:has_all_of(bf2)) -- equal assert.is_true(bf3:has_all_of(bf1)) -- is a subset, and has more flags assert.is_false(bf1:has_all_of(bf3)) -- not a subset, bf3 has more flags assert.is_false(bf1:has_all_of(bf0)) -- bf0 is unset, always returns false end) it("checks for a subset using 'has_any_of'", function() local bf1 = sys.bitflag(3) -- b0011 local bf2 = sys.bitflag(3) -- b0011 local bf3 = sys.bitflag(7) -- b0111 local bf4 = sys.bitflag(8) -- b1000 local bf0 = sys.bitflag(0) -- b0000 assert.is_true(bf1:has_any_of(bf2)) -- equal assert.is_true(bf3:has_any_of(bf1)) -- is a subset, and has more flags assert.is_false(bf3:has_any_of(bf4)) -- no overlap in flags assert.is_true(bf1:has_any_of(bf3)) -- not a subset, bf3 has more flags but still some overlap assert.is_false(bf1:has_all_of(bf0)) -- bf0 is unset, always returns false end) end) luasystem-0.4.5/spec/helpers.lua000066400000000000000000000007771473046163300166670ustar00rootroot00000000000000local busted = require("busted") local function is_windows() return package.config:sub(1,1) == "\\" end local function postfixer(postfix) return function(description, ...) return busted.pending(description.." ["..postfix.."]", ...) end end -- win_it only executes on Windows, and is "pending" otherwise win_it = is_windows() and busted.it or postfixer("Windows only") -- nix_it only executes on Unix/Mac, and is "pending" otherwise nix_it = is_windows() and postfixer("Unix/Mac only") or busted.it luasystem-0.4.5/src/000077500000000000000000000000001473046163300143445ustar00rootroot00000000000000luasystem-0.4.5/src/Makefile000066400000000000000000000144331473046163300160110ustar00rootroot00000000000000# luasystem # # Definitions in this section can be overriden on the command line or in the # environment. # # PLAT: linux macosx win32 mingw freebsd # platform to build for ifeq ($(origin PLAT),undefined) UNAME_S:=$(shell uname -s) ifeq ($(UNAME_S),Linux) PLAT=linux endif ifeq ($(UNAME_S),Darwin) PLAT=macosx endif ifeq ($(UNAME_S),FreeBSD) PLAT=freebsd endif ifeq ($(patsubst MINGW%,MINGW,$(UNAME_S)),MINGW) PLAT=mingw endif endif PLAT?=linux # LUA_VERSION: 5.1 5.2 5.3 # lua version to build against LUA_VERSION?=$(basename $(word 2,$(shell lua -v))) # MYCFLAGS: to be set by user if needed MYCFLAGS= # MYLDFLAGS: to be set by user if needed MYLDFLAGS= # where lua headers are found for macosx builds # LUAINC_macosx: # /opt/local/include LUAINC_macosx_base?=/opt/local/include LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUA_VERSION) # FIXME default should this default to fink or to macports? # What happens when more than one Lua version is installed? LUAPREFIX_macosx?=/opt/local CDIR_macosx?=lib/lua/$(LUA_VERSION) LDIR_macosx?=share/lua/$(LUA_VERSION) # LUAINC_linux: # /usr/include/lua$(LUA_VERSION) # /usr/local/include # /usr/local/include/lua$(LUA_VERSION) # where lua headers are found for linux builds LUAINC_linux_base?=/usr/include LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUA_VERSION) LUAPREFIX_linux?=/usr/local CDIR_linux?=lib/lua/$(LUA_VERSION) LDIR_linux?=share/lua/$(LUA_VERSION) # LUAINC_freebsd: # /usr/local/include/lua$(LUA_VERSION) # where lua headers are found for linux builds LUAINC_freebsd_base?=/usr/local/include/ LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua$(LUA_VERSION) LUAPREFIX_freebsd?=/usr/local/ CDIR_freebsd?=lib/lua/$(LUA_VERSION) LDIR_freebsd?=share/lua/$(LUA_VERSION) # where lua headers are found for mingw builds # LUAINC_mingw: # /opt/local/include LUAINC_mingw_base?=/usr/include LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUA_VERSION) LUALIB_mingw_base?=/usr/bin LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUA_VERSION)/lua$(subst .,,$(LUA_VERSION)).dll LUAPREFIX_mingw?=/usr CDIR_mingw?=lua/$(LUA_VERSION) LDIR_mingw?=lua/$(LUA_VERSION)/lua # LUAINC_win32: # LUALIB_win32: # where lua headers and libraries are found for win32 builds LUAPREFIX_win32?= LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUA_VERSION) PLATFORM_win32?=Release CDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32) LDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32)/lua LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUA_VERSION)/$(PLATFORM_win32) LUALIBNAME_win32?=lua$(subst .,,$(LUA_VERSION)).lib # prefix: /usr/local /usr /opt/local /sw # the top of the default install tree prefix?=$(LUAPREFIX_$(PLAT)) CDIR?=$(CDIR_$(PLAT)) LDIR?=$(LDIR_$(PLAT)) # DESTDIR: (no default) # used by package managers to install into a temporary destination DESTDIR= #------ # Definitions below can be overridden on the make command line, but # shouldn't have to be. #------ # Install directories # INSTALL_DIR=install -d INSTALL_DATA=install -m644 INSTALL_EXEC=install INSTALL_TOP=$(DESTDIR)$(prefix) INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) INSTALL_LDIR=$(INSTALL_TOP_LDIR)/system INSTALL_CDIR=$(INSTALL_TOP_CDIR)/system #------ # Supported platforms # PLATS= macosx linux win32 mingw #------ # Compiler and linker settings # for Mac OS X SO_macosx=so O_macosx=o CC_macosx=gcc DEF_macosx= CFLAGS_macosx= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \ -fvisibility=hidden LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc #------ # Compiler and linker settings # for Linux SO_linux=so O_linux=o CC_linux=gcc DEF_linux= CFLAGS_linux= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden LDFLAGS_linux=-lrt -O -shared -fpic -o LD_linux=gcc #------ # Compiler and linker settings # for FreeBSD SO_freebsd=so O_freebsd=o CC_freebsd=gcc DEF_freebsd= CFLAGS_freebsd= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden LDFLAGS_freebsd=-O -shared -fpic -o LD_freebsd=gcc #------ # Compiler and linker settings # for MingW SO_mingw=dll O_mingw=o CC_mingw=gcc DEF_mingw=-DWINVER=0x0600 -D_WIN32_WINNT=0x0600 CFLAGS_mingw= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \ -fvisibility=hidden LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -o LD_mingw=gcc #------ # Compiler and linker settings # for Win32 SO_win32=dll O_win32=obj CC_win32=cl DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ //D "_CRT_SECURE_NO_WARNINGS" //D "_WINDLL" \ //D"WINVER=0x0600" //D"_WIN32_WINNT=0x0600" CFLAGS_win32=//I "$(LUAINC)" $(DEF) //O2 //Ot //MD //W3 //nologo LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ //MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ //MACHINE:X86 /LIBPATH:"$(shell cmd //c echo $(LUALIB))" \ $(LUALIBNAME_win32) //OUT: LD_win32=cl .SUFFIXES: .obj .c.obj: $(CC) $(CFLAGS) //Fo"$@" //c $< #------ # Output file names # SO=$(SO_$(PLAT)) O=$(O_$(PLAT)) SOLIB=core.$(SO) #------ # Settings selected for platform # CC=$(CC_$(PLAT)) DEF=$(DEF_$(PLAT)) CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) LD=$(LD_$(PLAT)) LUAINC= $(LUAINC_$(PLAT)) LUALIB= $(LUALIB_$(PLAT)) #------ # Objects # OBJS=bitflags.$(O) compat.$(O) core.$(O) environment.$(O) random.$(O) term.$(O) time.$(O) wcwidth.$(O) #------ # Targets # default: $(PLAT) freebsd: $(MAKE) all PLAT=freebsd macosx: $(MAKE) all PLAT=macosx win32: $(MAKE) all PLAT=win32 linux: $(MAKE) all PLAT=linux mingw: $(MAKE) all PLAT=mingw none: @echo "Please run" @echo " make PLATFORM" @echo "where PLATFORM is one of these:" @echo " $(PLATS)" all: $(SOLIB) $(SOLIB): $(OBJS) $(LD) $(OBJS) $(LDFLAGS)$@ install: all $(INSTALL_DIR) $(INSTALL_TOP_LDIR) $(INSTALL_DIR) $(INSTALL_CDIR) $(INSTALL_EXEC) $(SOLIB) $(INSTALL_CDIR)/$(SOLIB) local: $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. clean: rm -f $(SOLIB) $(OBJS) ../system/core.so print: @echo PLAT=$(PLAT) @echo LUA_VERSION=$(LUA_VERSION) @echo prefix=$(prefix) @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) .PHONY: all $(PLATS) default clean none luasystem-0.4.5/src/bitflags.c000066400000000000000000000215071473046163300163100ustar00rootroot00000000000000/// Bitflags module. // The bitflag object makes it easy to manipulate flags in a bitmask. // // It has metamethods that do the hard work, adding flags sets them, substracting // unsets them. Comparing flags checks if all flags in the second set are also set // in the first set. The `has` method checks if all flags in the second set are // also set in the first set, but behaves slightly different. // // Indexing allows checking values or setting them by bit index (eg. 0-7 for flags // in the first byte). // // _NOTE_: unavailable flags (eg. Windows flags on a Posix system) should not be // omitted, but be assigned a value of 0. This is because the `has` method will // return `false` if the flags are checked and the value is 0. // // See `system.bitflag` (the constructor) for extensive examples on usage. // @classmod bitflags #include "bitflags.h" #define BITFLAGS_MT_NAME "LuaSystem.BitFlags" typedef struct { LSBF_BITFLAG flags; } LS_BitFlags; /// Bit flags. // Bitflag objects can be used to easily manipulate and compare bit flags. // These are primarily for use with the terminal functions, but can be used // in other places as well. // @section bitflags // pushes a new LS_BitFlags object with the given value onto the stack void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value) { LS_BitFlags *obj = (LS_BitFlags *)lua_newuserdata(L, sizeof(LS_BitFlags)); if (!obj) luaL_error(L, "Memory allocation failed"); luaL_getmetatable(L, BITFLAGS_MT_NAME); lua_setmetatable(L, -2); obj->flags = value; } // gets the LS_BitFlags value at the given index. Returns a Lua error if it is not // a LS_BitFlags object. LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index) { LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, index, BITFLAGS_MT_NAME); return obj->flags; } // Validates that the given index is a table containing a field 'fieldname' // which is a bitflag object and returns its value. // If the index is not a table or the field is not a bitflag object, a Lua // error is raised. If the bitflag is not present, the default value is returned. // The stack remains unchanged. LSBF_BITFLAG lsbf_checkbitflagsfield(lua_State *L, int index, const char *fieldname, LSBF_BITFLAG default_value) { luaL_checktype(L, index, LUA_TTABLE); lua_getfield(L, index, fieldname); // if null, return default value if (lua_isnil(L, -1)) { lua_pop(L, 1); return default_value; } // check to bitflags LS_BitFlags *obj = luaL_testudata(L, -1, BITFLAGS_MT_NAME); if (obj == NULL) { lua_pop(L, 1); return luaL_error(L, "bad argument #%d, field '%s' must be a bitflag object", index, fieldname); } LSBF_BITFLAG value = obj->flags; lua_pop(L, 1); return value; } /*** Creates a new bitflag object from the given value. @function system.bitflag @tparam[opt=0] number value the value to create the bitflag object from. @treturn bitflag bitflag object with the given values set. @usage local sys = require 'system' local flags = sys.bitflag(2) -- b0010 -- get state of individual bits print(flags[0]) -- false print(flags[1]) -- true -- set individual bits flags[0] = true -- b0011 print(flags:value()) -- 3 print(flags) -- "bitflags: 3" -- adding flags (bitwise OR) local flags1 = sys.bitflag(1) -- b0001 local flags2 = sys.bitflag(2) -- b0010 local flags3 = flags1 + flags2 -- b0011 -- substracting flags (bitwise AND NOT) print(flags3:value()) -- 3 flag3 = flag3 - flag3 -- b0000 print(flags3:value()) -- 0 -- comparing flags local flags4 = sys.bitflag(7) -- b0111 local flags5 = sys.bitflag(255) -- b11111111 print(flags5 ~= flags4) -- true, not the same flags local flags6 = sys.bitflag(7) -- b0111 print(flags6 == flags4) -- true, same flags -- comparison of subsets local flags7 = sys.bitflag(0) -- b0000 local flags8 = sys.bitflag(3) -- b0011 local flags9 = sys.bitflag(7) -- b0111 print(flags9:has_all_of(flags8)) -- true, flags8 bits are all set in flags9 print(flags8:has_any_of(flags9)) -- true, some of flags9 bits are set in flags8 print(flags8:has_all_of(flags7)) -- false, flags7 (== 0) is not set in flags8 */ static int lsbf_new(lua_State *L) { LSBF_BITFLAG flags = 0; if (lua_gettop(L) > 0) { flags = luaL_checkinteger(L, 1); } lsbf_pushbitflags(L, flags); return 1; } /*** Retrieves the numeric value of the bitflag object. @function bitflag:value @treturn number the numeric value of the bitflags. @usage local sys = require 'system' local flags = sys.bitflag() -- b0000 flags[0] = true -- b0001 flags[2] = true -- b0101 print(flags:value()) -- 5 */ static int lsbf_value(lua_State *L) { lua_pushinteger(L, lsbf_checkbitflags(L, 1)); return 1; } static int lsbf_tostring(lua_State *L) { lua_pushfstring(L, "bitflags: %d", lsbf_checkbitflags(L, 1)); return 1; } static int lsbf_add(lua_State *L) { lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) | lsbf_checkbitflags(L, 2)); return 1; } static int lsbf_sub(lua_State *L) { lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) & ~lsbf_checkbitflags(L, 2)); return 1; } static int lsbf_eq(lua_State *L) { lua_pushboolean(L, lsbf_checkbitflags(L, 1) == lsbf_checkbitflags(L, 2)); return 1; } /*** Checks if all the flags in the given subset are set. If the flags to check has a value `0`, it will always return `false`. So if there are flags that are unsupported on a platform, they can be set to 0 and the `has_all_of` function will return `false` if the flags are checked. @function bitflag:has_all_of @tparam bitflag subset the flags to check for. @treturn boolean true if all the flags are set, false otherwise. @usage local sys = require 'system' local flags = sys.bitflag(12) -- b1100 local myflags = sys.bitflag(15) -- b1111 print(flags:has_all_of(myflags)) -- false, not all bits in myflags are set in flags print(myflags:has_all_of(flags)) -- true, all bits in flags are set in myflags */ static int lsbf_has_all_of(lua_State *L) { LSBF_BITFLAG a = lsbf_checkbitflags(L, 1); LSBF_BITFLAG b = lsbf_checkbitflags(L, 2); // Check if all bits in b are also set in a, and b is not 0 lua_pushboolean(L, (a & b) == b && b != 0); return 1; } /*** Checks if any of the flags in the given subset are set. If the flags to check has a value `0`, it will always return `false`. So if there are flags that are unsupported on a platform, they can be set to 0 and the `has_any_of` function will return `false` if the flags are checked. @function bitflag:has_any_of @tparam bitflag subset the flags to check for. @treturn boolean true if any of the flags are set, false otherwise. @usage local sys = require 'system' local flags = sys.bitflag(12) -- b1100 local myflags = sys.bitflag(7) -- b0111 print(flags:has_any_of(myflags)) -- true, some bits in myflags are set in flags print(myflags:has_any_of(flags)) -- true, some bits in flags are set in myflags */ static int lsbf_has_any_of(lua_State *L) { LSBF_BITFLAG a = lsbf_checkbitflags(L, 1); LSBF_BITFLAG b = lsbf_checkbitflags(L, 2); // Check if any bits in b are set in a lua_pushboolean(L, (a & b) != 0); return 1; } static int lsbf_index(lua_State *L) { if (!lua_isnumber(L, 2)) { // the parameter isn't a number, just lookup the key in the metatable lua_getmetatable(L, 1); lua_pushvalue(L, 2); lua_gettable(L, -2); return 1; } int index = luaL_checkinteger(L, 2); if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) { return luaL_error(L, "index out of range"); } lua_pushboolean(L, (lsbf_checkbitflags(L, 1) & (1 << index)) != 0); return 1; } static int lsbf_newindex(lua_State *L) { LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, 1, BITFLAGS_MT_NAME); if (!lua_isnumber(L, 2)) { return luaL_error(L, "index must be a number"); } int index = luaL_checkinteger(L, 2); if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) { return luaL_error(L, "index out of range"); } luaL_checkany(L, 3); if (lua_toboolean(L, 3)) { obj->flags |= (1 << index); } else { obj->flags &= ~(1 << index); } return 0; } static const struct luaL_Reg lsbf_funcs[] = { {"bitflag", lsbf_new}, {NULL, NULL} }; static const struct luaL_Reg lsbf_methods[] = { {"value", lsbf_value}, {"has_all_of", lsbf_has_all_of}, {"has_any_of", lsbf_has_any_of}, {"__tostring", lsbf_tostring}, {"__add", lsbf_add}, {"__sub", lsbf_sub}, {"__eq", lsbf_eq}, {"__index", lsbf_index}, {"__newindex", lsbf_newindex}, {NULL, NULL} }; void bitflags_open(lua_State *L) { luaL_newmetatable(L, BITFLAGS_MT_NAME); luaL_setfuncs(L, lsbf_methods, 0); lua_pop(L, 1); luaL_setfuncs(L, lsbf_funcs, 0); } luasystem-0.4.5/src/bitflags.h000066400000000000000000000020361473046163300163110ustar00rootroot00000000000000#ifndef LSBITFLAGS_H #define LSBITFLAGS_H #include #include "compat.h" #include #include // type used to store the bitflags #define LSBF_BITFLAG lua_Integer // Validates that the given index is a bitflag object and returns its value. // If the index is not a bitflag object, a Lua error is raised. // The value will be left on the stack. LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index); // Validates that the given index is a table containing a field 'fieldname' // which is a bitflag object and returns its value. // If the index is not a table or the field is not a bitflag object, a Lua // error is raised. If the bitflag is not present, the default value is returned. // The stack remains unchanged. LSBF_BITFLAG lsbf_checkbitflagsfield(lua_State *L, int index, const char *fieldname, LSBF_BITFLAG default_value); // Pushes a new bitflag object with the given value onto the stack. // Might raise a Lua error if memory allocation fails. void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value); #endif luasystem-0.4.5/src/compat.c000066400000000000000000000022761473046163300160020ustar00rootroot00000000000000#include #include #if LUA_VERSION_NUM == 501 void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup+1, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; lua_pushstring(L, l->name); for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -(nup+1)); lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ lua_settable(L, -(nup + 3)); } lua_pop(L, nup); /* remove upvalues */ } void *luaL_testudata(lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) { /* Check for userdata */ if (lua_getmetatable(L, ud)) { /* Does it have a metatable? */ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* Get metatable we're looking for */ if (lua_rawequal(L, -1, -2)) { /* Compare metatables */ lua_pop(L, 2); /* Remove metatables from stack */ return p; } lua_pop(L, 2); /* Remove metatables from stack */ } } return NULL; /* Return NULL if check fails */ } #endif luasystem-0.4.5/src/compat.h000066400000000000000000000024671473046163300160110ustar00rootroot00000000000000#ifndef COMPAT_H #define COMPAT_H #include #include #if LUA_VERSION_NUM == 501 void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup); void *luaL_testudata(lua_State *L, int ud, const char *tname); #endif #ifdef __MINGW32__ #include #endif // Windows compatibility; define DWORD and TRUE/FALSE on non-Windows #ifndef _WIN32 #ifndef DWORD #define DWORD unsigned long #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #endif #ifdef _MSC_VER // MSVC Windows doesn't have ssize_t, so we define it here #if SIZE_MAX == UINT_MAX typedef int ssize_t; /* common 32 bit case */ #define SSIZE_MIN INT_MIN #define SSIZE_MAX INT_MAX #elif SIZE_MAX == ULONG_MAX typedef long ssize_t; /* linux 64 bits */ #define SSIZE_MIN LONG_MIN #define SSIZE_MAX LONG_MAX #elif SIZE_MAX == ULLONG_MAX typedef long long ssize_t; /* windows 64 bits */ #define SSIZE_MIN LLONG_MIN #define SSIZE_MAX LLONG_MAX #elif SIZE_MAX == USHRT_MAX typedef short ssize_t; /* is this even possible? */ #define SSIZE_MIN SHRT_MIN #define SSIZE_MAX SHRT_MAX #elif SIZE_MAX == UINTMAX_MAX typedef intmax_t ssize_t; /* last resort, chux suggestion */ #define SSIZE_MIN INTMAX_MIN #define SSIZE_MAX INTMAX_MAX #else #error platform has exotic SIZE_MAX #endif #endif // _MSC_VER #endif // COMPAT_H luasystem-0.4.5/src/core.c000066400000000000000000000022361473046163300154430ustar00rootroot00000000000000/// Platform independent system calls for Lua. // @module system #include #include #define LUASYSTEM_VERSION "LuaSystem 0.4.5" #ifdef _WIN32 #define LUAEXPORT __declspec(dllexport) #else #define LUAEXPORT __attribute__((visibility("default"))) #endif void time_open(lua_State *L); void environment_open(lua_State *L); void random_open(lua_State *L); void term_open(lua_State *L); void bitflags_open(lua_State *L); /*------------------------------------------------------------------------- * Initializes all library modules. *-------------------------------------------------------------------------*/ LUAEXPORT int luaopen_system_core(lua_State *L) { lua_newtable(L); lua_pushstring(L, "_VERSION"); lua_pushstring(L, LUASYSTEM_VERSION); lua_rawset(L, -3); /// Flag to identify Windows. // @field windows `true` if on Windows, `false` otherwise. lua_pushstring(L, "windows"); #ifdef _WIN32 lua_pushboolean(L, 1); #else lua_pushboolean(L, 0); #endif lua_rawset(L, -3); bitflags_open(L); // must be first, used by others time_open(L); random_open(L); term_open(L); environment_open(L); return 1; } luasystem-0.4.5/src/environment.c000066400000000000000000000122431473046163300170560ustar00rootroot00000000000000/// @module system /// Environment. // @section environment #include #include #include "compat.h" #include #include #ifdef _WIN32 #include "windows.h" #endif /*** Gets the value of an environment variable. __NOTE__: Windows has multiple copies of environment variables. For this reason, the `setenv` function will not work with Lua's `os.getenv` on Windows. If you want to use `setenv` then consider patching `os.getenv` with this implementation of `getenv`. @function getenv @tparam string name name of the environment variable @treturn string|nil value of the environment variable, or nil if the variable is not set */ static int lua_get_environment_variable(lua_State* L) { const char* variableName = luaL_checkstring(L, 1); #ifdef _WIN32 // On Windows, use GetEnvironmentVariable to retrieve the value DWORD bufferSize = GetEnvironmentVariable(variableName, NULL, 0); if (bufferSize > 0) { char* buffer = (char*)malloc(bufferSize); if (GetEnvironmentVariable(variableName, buffer, bufferSize) > 0) { lua_pushstring(L, buffer); free(buffer); return 1; } free(buffer); } #else // On non-Windows platforms, use getenv to retrieve the value const char* variableValue = getenv(variableName); if (variableValue != NULL) { lua_pushstring(L, variableValue); return 1; } #endif // If the variable is not set or an error occurs, push nil lua_pushnil(L); return 1; } /*** Returns a table with all environment variables. @function getenvs @treturn table table with all environment variables and their values */ static int lua_list_environment_variables(lua_State* L) { lua_newtable(L); #ifdef _WIN32 char* envStrings = GetEnvironmentStrings(); char* envString = envStrings; if (envStrings == NULL) { lua_pushnil(L); return 1; } while (*envString != '\0') { const char* envVar = envString; // Split the environment variable into key and value char* equals = strchr(envVar, '='); if (equals != NULL) { lua_pushlstring(L, envVar, equals - envVar); // Push the key lua_pushstring(L, equals + 1); // Push the value lua_settable(L, -3); // Set the key-value pair in the table } envString += strlen(envString) + 1; } FreeEnvironmentStrings(envStrings); #else extern char** environ; if (environ != NULL) { for (char** envVar = environ; *envVar != NULL; envVar++) { const char* envVarStr = *envVar; // Split the environment variable into key and value char* equals = strchr(envVarStr, '='); if (equals != NULL) { lua_pushlstring(L, envVarStr, equals - envVarStr); // Push the key lua_pushstring(L, equals + 1); // Push the value lua_settable(L, -3); // Set the key-value pair in the table } } } #endif return 1; } /*** Sets an environment variable. __NOTE__: Windows has multiple copies of environment variables. For this reason, the `setenv` function will not work with Lua's `os.getenv` on Windows. If you want to use it then consider patching `os.getenv` with the implementation of `system.getenv`. @function setenv @tparam string name name of the environment variable @tparam[opt] string value value of the environment variable, if `nil` the variable will be deleted (on Windows, setting an empty string, will also delete the variable) @treturn boolean success */ static int lua_set_environment_variable(lua_State* L) { const char* variableName = luaL_checkstring(L, 1); const char* variableValue = luaL_optstring(L, 2, NULL); #ifdef _WIN32 // if (variableValue == NULL) { // // If the value is nil, delete the environment variable // if (SetEnvironmentVariable(variableName, NULL)) { // lua_pushboolean(L, 1); // } else { // lua_pushboolean(L, 0); // } // } else { // Set the environment variable with the provided value if (SetEnvironmentVariable(variableName, variableValue)) { lua_pushboolean(L, 1); } else { lua_pushboolean(L, 0); } // } #else if (variableValue == NULL) { // If the value is nil, delete the environment variable if (unsetenv(variableName) == 0) { lua_pushboolean(L, 1); } else { lua_pushboolean(L, 0); } } else { // Set the environment variable with the provided value if (setenv(variableName, variableValue, 1) == 0) { lua_pushboolean(L, 1); } else { lua_pushboolean(L, 0); } } #endif return 1; } static luaL_Reg func[] = { { "getenv", lua_get_environment_variable }, { "setenv", lua_set_environment_variable }, { "getenvs", lua_list_environment_variables }, { NULL, NULL } }; /*------------------------------------------------------------------------- * Initializes module *-------------------------------------------------------------------------*/ void environment_open(lua_State *L) { luaL_setfuncs(L, func, 0); } luasystem-0.4.5/src/random.c000066400000000000000000000057131473046163300157760ustar00rootroot00000000000000/// @module system /// Random. // @section random #include #include #include "compat.h" #include #ifdef _WIN32 #include "windows.h" #include "wincrypt.h" #else #include #include #include #endif /*** Generate random bytes. This uses `CryptGenRandom()` on Windows, and `/dev/urandom` on other platforms. It will return the requested number of bytes, or an error, never a partial result. @function random @tparam[opt=1] int length number of bytes to get @treturn[1] string string of random bytes @treturn[2] nil @treturn[2] string error message */ static int lua_get_random_bytes(lua_State* L) { int num_bytes = luaL_optinteger(L, 1, 1); // Number of bytes, default to 1 if not provided if (num_bytes <= 0) { if (num_bytes == 0) { lua_pushliteral(L, ""); return 1; } lua_pushnil(L); lua_pushstring(L, "invalid number of bytes, must not be less than 0"); return 2; } unsigned char* buffer = (unsigned char*)lua_newuserdata(L, num_bytes); if (buffer == NULL) { lua_pushnil(L); lua_pushstring(L, "failed to allocate memory for random buffer"); return 2; } ssize_t n; ssize_t total_read = 0; #ifdef _WIN32 HCRYPTPROV hCryptProv; if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { DWORD error = GetLastError(); lua_pushnil(L); lua_pushfstring(L, "failed to acquire cryptographic context: %lu", error); return 2; } if (!CryptGenRandom(hCryptProv, num_bytes, buffer)) { DWORD error = GetLastError(); lua_pushnil(L); lua_pushfstring(L, "failed to get random data: %lu", error); CryptReleaseContext(hCryptProv, 0); return 2; } CryptReleaseContext(hCryptProv, 0); #else // for macOS/unixes use /dev/urandom for non-blocking int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (fd < 0) { lua_pushnil(L); lua_pushstring(L, "failed opening /dev/urandom"); return 2; } while (total_read < num_bytes) { n = read(fd, buffer + total_read, num_bytes - total_read); if (n < 0) { if (errno == EINTR) { continue; // Interrupted, retry } else { lua_pushnil(L); lua_pushfstring(L, "failed reading /dev/urandom: %s", strerror(errno)); close(fd); return 2; } } total_read += n; } close(fd); #endif lua_pushlstring(L, (const char*)buffer, num_bytes); return 1; } static luaL_Reg func[] = { { "random", lua_get_random_bytes }, { NULL, NULL } }; /*------------------------------------------------------------------------- * Initializes module *-------------------------------------------------------------------------*/ void random_open(lua_State *L) { luaL_setfuncs(L, func, 0); } luasystem-0.4.5/src/term.c000066400000000000000000001062451473046163300154670ustar00rootroot00000000000000/// @module system /// Terminal. // Unix: see https://blog.nelhage.com/2009/12/a-brief-introduction-to-termios-termios3-and-stty/ // // Windows: see https://learn.microsoft.com/en-us/windows/console/console-reference // @section terminal #include #include #include #include "compat.h" #include "bitflags.h" #ifdef _WIN32 # include # include # ifndef _MSC_VER # include # include # include # endif #else # include # include # include # include # include # include # include #endif #include // Windows does not have a wcwidth function, so we use compatibilty code from // http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c by Markus Kuhn #include "wcwidth.h" #ifdef _WIN32 // after an error is returned, GetLastError() result can be passed to this function to get a string // representation of the error on the stack. // result will be nil+error on the stack, always 2 results. static void termFormatError(lua_State *L, DWORD errorCode, const char* prefix) { //static void FormatErrorAndReturn(lua_State *L, DWORD errorCode, const char* prefix) { LPSTR messageBuffer = NULL; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); lua_pushnil(L); if (messageBuffer) { if (prefix) { lua_pushfstring(L, "%s: %s", prefix, messageBuffer); } else { lua_pushstring(L, messageBuffer); } LocalFree(messageBuffer); } else { lua_pushfstring(L, "%sError code %d", prefix ? prefix : "", errorCode); } } #else static int pusherror(lua_State *L, const char *info) { lua_pushnil(L); if (info==NULL) lua_pushstring(L, strerror(errno)); else lua_pushfstring(L, "%s: %s", info, strerror(errno)); lua_pushinteger(L, errno); return 3; } #endif /*** Checks if a file-handle is a TTY. @function isatty @tparam file file the file-handle to check, one of `io.stdin`, `io.stdout`, `io.stderr`. @treturn boolean true if the file is a tty @usage local system = require('system') if system.isatty(io.stdin) then -- enable ANSI coloring etc on Windows, does nothing in Posix. local flags = system.getconsoleflags(io.stdout) system.setconsoleflags(io.stdout, flags + sys.COF_VIRTUAL_TERMINAL_PROCESSING) end */ static int lst_isatty(lua_State* L) { FILE **fh = (FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); lua_pushboolean(L, isatty(fileno(*fh))); return 1; } /*------------------------------------------------------------------------- * Windows Get/SetConsoleMode functions *-------------------------------------------------------------------------*/ typedef struct ls_RegConst { const char *name; DWORD value; } ls_RegConst; // Define a macro to check if a constant is defined and set it to 0 if not. // This is needed because some flags are not defined on all platforms. So we // still export the constants, but they will be all 0, and hence not do anything. #ifdef _WIN32 // check compatibility: Windows virtual terminal processing was added in 2015, // some older compiler suites don't have the proper headers. #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT #error Virtual terminal macros are undefined (eg. ENABLE_VIRTUAL_TERMINAL_INPUT). Update the toolchain or revert to Luasystem < 0.4 #endif #define CHECK_WIN_FLAG_OR_ZERO(flag) flag #define CHECK_NIX_FLAG_OR_ZERO(flag) 0 #else #define CHECK_WIN_FLAG_OR_ZERO(flag) 0 #define CHECK_NIX_FLAG_OR_ZERO(flag) flag #endif // Export Windows constants to Lua static const struct ls_RegConst win_console_in_flags[] = { // Console Input Flags {"CIF_ECHO_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_ECHO_INPUT)}, {"CIF_INSERT_MODE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_INSERT_MODE)}, {"CIF_LINE_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_LINE_INPUT)}, {"CIF_MOUSE_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_MOUSE_INPUT)}, {"CIF_PROCESSED_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_PROCESSED_INPUT)}, {"CIF_QUICK_EDIT_MODE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_QUICK_EDIT_MODE)}, {"CIF_WINDOW_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_WINDOW_INPUT)}, {"CIF_VIRTUAL_TERMINAL_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_VIRTUAL_TERMINAL_INPUT)}, {"CIF_EXTENDED_FLAGS", CHECK_WIN_FLAG_OR_ZERO(ENABLE_EXTENDED_FLAGS)}, {"CIF_AUTO_POSITION", CHECK_WIN_FLAG_OR_ZERO(ENABLE_AUTO_POSITION)}, {NULL, 0} }; static const struct ls_RegConst win_console_out_flags[] = { // Console Output Flags {"COF_PROCESSED_OUTPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_PROCESSED_OUTPUT)}, {"COF_WRAP_AT_EOL_OUTPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_WRAP_AT_EOL_OUTPUT)}, {"COF_VIRTUAL_TERMINAL_PROCESSING", CHECK_WIN_FLAG_OR_ZERO(ENABLE_VIRTUAL_TERMINAL_PROCESSING)}, {"COF_DISABLE_NEWLINE_AUTO_RETURN", CHECK_WIN_FLAG_OR_ZERO(DISABLE_NEWLINE_AUTO_RETURN)}, {"COF_ENABLE_LVB_GRID_WORLDWIDE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_LVB_GRID_WORLDWIDE)}, {NULL, 0} }; // Export Unix constants to Lua static const struct ls_RegConst nix_tcsetattr_actions[] = { // The optional actions for tcsetattr {"TCSANOW", CHECK_NIX_FLAG_OR_ZERO(TCSANOW)}, {"TCSADRAIN", CHECK_NIX_FLAG_OR_ZERO(TCSADRAIN)}, {"TCSAFLUSH", CHECK_NIX_FLAG_OR_ZERO(TCSAFLUSH)}, {NULL, 0} }; static const struct ls_RegConst nix_console_i_flags[] = { // Input flags (c_iflag) {"I_IGNBRK", CHECK_NIX_FLAG_OR_ZERO(IGNBRK)}, {"I_BRKINT", CHECK_NIX_FLAG_OR_ZERO(BRKINT)}, {"I_IGNPAR", CHECK_NIX_FLAG_OR_ZERO(IGNPAR)}, {"I_PARMRK", CHECK_NIX_FLAG_OR_ZERO(PARMRK)}, {"I_INPCK", CHECK_NIX_FLAG_OR_ZERO(INPCK)}, {"I_ISTRIP", CHECK_NIX_FLAG_OR_ZERO(ISTRIP)}, {"I_INLCR", CHECK_NIX_FLAG_OR_ZERO(INLCR)}, {"I_IGNCR", CHECK_NIX_FLAG_OR_ZERO(IGNCR)}, {"I_ICRNL", CHECK_NIX_FLAG_OR_ZERO(ICRNL)}, #if !defined(__APPLE__) && !defined(__FreeBSD__) {"I_IUCLC", CHECK_NIX_FLAG_OR_ZERO(IUCLC)}, // Might not be available on all systems #else {"I_IUCLC", 0}, #endif {"I_IXON", CHECK_NIX_FLAG_OR_ZERO(IXON)}, {"I_IXANY", CHECK_NIX_FLAG_OR_ZERO(IXANY)}, {"I_IXOFF", CHECK_NIX_FLAG_OR_ZERO(IXOFF)}, {"I_IMAXBEL", CHECK_NIX_FLAG_OR_ZERO(IMAXBEL)}, {NULL, 0} }; static const struct ls_RegConst nix_console_o_flags[] = { // Output flags (c_oflag) {"O_OPOST", CHECK_NIX_FLAG_OR_ZERO(OPOST)}, #if !defined(__APPLE__) && !defined(__FreeBSD__) {"O_OLCUC", CHECK_NIX_FLAG_OR_ZERO(OLCUC)}, // Might not be available on all systems #else {"O_OLCUC", 0}, #endif {"O_ONLCR", CHECK_NIX_FLAG_OR_ZERO(ONLCR)}, {"O_OCRNL", CHECK_NIX_FLAG_OR_ZERO(OCRNL)}, {"O_ONOCR", CHECK_NIX_FLAG_OR_ZERO(ONOCR)}, {"O_ONLRET", CHECK_NIX_FLAG_OR_ZERO(ONLRET)}, #ifndef __FreeBSD__ {"O_OFILL", CHECK_NIX_FLAG_OR_ZERO(OFILL)}, {"O_OFDEL", CHECK_NIX_FLAG_OR_ZERO(OFDEL)}, {"O_NLDLY", CHECK_NIX_FLAG_OR_ZERO(NLDLY)}, {"O_CRDLY", CHECK_NIX_FLAG_OR_ZERO(CRDLY)}, #else {"O_OFILL", 0}, {"O_OFDEL", 0}, {"O_NLDLY", 0}, {"O_CRDLY", 0}, #endif {"O_TABDLY", CHECK_NIX_FLAG_OR_ZERO(TABDLY)}, #ifndef __FreeBSD__ {"O_BSDLY", CHECK_NIX_FLAG_OR_ZERO(BSDLY)}, {"O_VTDLY", CHECK_NIX_FLAG_OR_ZERO(VTDLY)}, {"O_FFDLY", CHECK_NIX_FLAG_OR_ZERO(FFDLY)}, #else {"O_BSDLY", 0}, {"O_VTDLY", 0}, {"O_FFDLY", 0}, #endif {NULL, 0} }; static const struct ls_RegConst nix_console_l_flags[] = { // Local flags (c_lflag) {"L_ISIG", CHECK_NIX_FLAG_OR_ZERO(ISIG)}, {"L_ICANON", CHECK_NIX_FLAG_OR_ZERO(ICANON)}, #if !defined(__APPLE__) && !defined(__FreeBSD__) {"L_XCASE", CHECK_NIX_FLAG_OR_ZERO(XCASE)}, // Might not be available on all systems #else {"L_XCASE", 0}, #endif {"L_ECHO", CHECK_NIX_FLAG_OR_ZERO(ECHO)}, {"L_ECHOE", CHECK_NIX_FLAG_OR_ZERO(ECHOE)}, {"L_ECHOK", CHECK_NIX_FLAG_OR_ZERO(ECHOK)}, {"L_ECHONL", CHECK_NIX_FLAG_OR_ZERO(ECHONL)}, {"L_NOFLSH", CHECK_NIX_FLAG_OR_ZERO(NOFLSH)}, {"L_TOSTOP", CHECK_NIX_FLAG_OR_ZERO(TOSTOP)}, {"L_ECHOCTL", CHECK_NIX_FLAG_OR_ZERO(ECHOCTL)}, // Might not be available on all systems {"L_ECHOPRT", CHECK_NIX_FLAG_OR_ZERO(ECHOPRT)}, // Might not be available on all systems {"L_ECHOKE", CHECK_NIX_FLAG_OR_ZERO(ECHOKE)}, // Might not be available on all systems {"L_FLUSHO", CHECK_NIX_FLAG_OR_ZERO(FLUSHO)}, {"L_PENDIN", CHECK_NIX_FLAG_OR_ZERO(PENDIN)}, {"L_IEXTEN", CHECK_NIX_FLAG_OR_ZERO(IEXTEN)}, {NULL, 0} }; static DWORD win_valid_in_flags = 0; static DWORD win_valid_out_flags = 0; static DWORD nix_valid_i_flags = 0; static DWORD nix_valid_o_flags = 0; static DWORD nix_valid_l_flags = 0; static void initialize_valid_flags() { win_valid_in_flags = 0; for (int i = 0; win_console_in_flags[i].name != NULL; i++) { win_valid_in_flags |= win_console_in_flags[i].value; } win_valid_out_flags = 0; for (int i = 0; win_console_out_flags[i].name != NULL; i++) { win_valid_out_flags |= win_console_out_flags[i].value; } nix_valid_i_flags = 0; for (int i = 0; nix_console_i_flags[i].name != NULL; i++) { nix_valid_i_flags |= nix_console_i_flags[i].value; } nix_valid_o_flags = 0; for (int i = 0; nix_console_o_flags[i].name != NULL; i++) { nix_valid_o_flags |= nix_console_o_flags[i].value; } nix_valid_l_flags = 0; for (int i = 0; nix_console_l_flags[i].name != NULL; i++) { nix_valid_l_flags |= nix_console_l_flags[i].value; } } #ifdef _WIN32 // first item on the stack should be io.stdin, io.stderr, or io.stdout, second item // should be the flags to validate. // If it returns NULL, then it leaves nil+err on the stack static HANDLE get_console_handle(lua_State *L, int flags_optional) { if (lua_gettop(L) < 1) { luaL_argerror(L, 1, "expected file handle"); } HANDLE handle; DWORD valid; FILE *file = *(FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE); if (file == stdin && file != NULL) { handle = GetStdHandle(STD_INPUT_HANDLE); valid = win_valid_in_flags; } else if (file == stdout && file != NULL) { handle = GetStdHandle(STD_OUTPUT_HANDLE); valid = win_valid_out_flags; } else if (file == stderr && file != NULL) { handle = GetStdHandle(STD_ERROR_HANDLE); valid = win_valid_out_flags; } else { luaL_argerror(L, 1, "invalid file handle"); // does not return } if (handle == INVALID_HANDLE_VALUE) { termFormatError(L, GetLastError(), "failed to retrieve std handle"); lua_error(L); // does not return } if (handle == NULL) { lua_pushnil(L); lua_pushliteral(L, "failed to get console handle"); return NULL; } if (flags_optional && lua_gettop(L) < 2) { return handle; } if (lua_gettop(L) < 2) { luaL_argerror(L, 2, "expected flags"); } LSBF_BITFLAG flags = lsbf_checkbitflags(L, 2); if ((flags & ~valid) != 0) { luaL_argerror(L, 2, "invalid flags"); } return handle; } #else // first item on the stack should be io.stdin, io.stderr, or io.stdout. Throws a // Lua error if the file is not one of these. static int get_console_handle(lua_State *L) { FILE **file = (FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE); if (file == NULL || *file == NULL) { return luaL_argerror(L, 1, "expected file handle"); // call doesn't return } // Check if the file is stdin, stdout, or stderr if (*file == stdin || *file == stdout || *file == stderr) { // Push the file descriptor onto the Lua stack return fileno(*file); } return luaL_argerror(L, 1, "invalid file handle"); // does not return } #endif /*** Sets the console flags (Windows). The `CIF_` and `COF_` constants are available on the module table. Where `CIF` are the input flags (for use with `io.stdin`) and `COF` are the output flags (for use with `io.stdout`/`io.stderr`). To see flag status and constant names check `listconsoleflags`. Note: not all combinations of flags are allowed, as some are mutually exclusive or mutually required. See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode) @function setconsoleflags @tparam file file file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` @tparam bitflags bitflags the flags to set/unset @treturn[1] boolean `true` on success @treturn[2] nil @treturn[2] string error message @usage local system = require('system') system.listconsoleflags(io.stdout) -- List all the available flags and their current status local flags = system.getconsoleflags(io.stdout) assert(system.setconsoleflags(io.stdout, flags + system.COF_VIRTUAL_TERMINAL_PROCESSING) system.listconsoleflags(io.stdout) -- List again to check the differences */ static int lst_setconsoleflags(lua_State *L) { #ifdef _WIN32 HANDLE console_handle = get_console_handle(L, 0); if (console_handle == NULL) { return 2; // error message is already on the stack } LSBF_BITFLAG new_console_mode = lsbf_checkbitflags(L, 2); if (!SetConsoleMode(console_handle, new_console_mode)) { termFormatError(L, GetLastError(), "failed to set console mode"); return 2; } #else get_console_handle(L); // to validate args #endif lua_pushboolean(L, 1); return 1; } /*** Gets console flags (Windows). The `CIF_` and `COF_` constants are available on the module table. Where `CIF` are the input flags (for use with `io.stdin`) and `COF` are the output flags (for use with `io.stdout`/`io.stderr`). _Note_: See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode) for more information on the flags. @function getconsoleflags @tparam file file file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` @treturn[1] bitflags the current console flags. @treturn[2] nil @treturn[2] string error message @usage local system = require('system') local flags = system.getconsoleflags(io.stdout) print("Current stdout flags:", tostring(flags)) if flags:has_all_of(system.COF_VIRTUAL_TERMINAL_PROCESSING + system.COF_PROCESSED_OUTPUT) then print("Both flags are set") else print("At least one flag is not set") end */ static int lst_getconsoleflags(lua_State *L) { DWORD console_mode = 0; #ifdef _WIN32 HANDLE console_handle = get_console_handle(L, 1); if (console_handle == NULL) { return 2; // error message is already on the stack } if (GetConsoleMode(console_handle, &console_mode) == 0) { lua_pushnil(L); lua_pushliteral(L, "failed to get console mode"); return 2; } #else get_console_handle(L); // to validate args #endif lsbf_pushbitflags(L, console_mode); return 1; } /*------------------------------------------------------------------------- * Unix tcgetattr/tcsetattr functions *-------------------------------------------------------------------------*/ // Code modified from the LuaPosix library by Gary V. Vaughan // see https://github.com/luaposix/luaposix /*** Get termios state (Posix). The terminal attributes is a table with the following fields: - `iflag` input flags - `oflag` output flags - `lflag` local flags - `cflag` control flags - `ispeed` input speed - `ospeed` output speed - `cc` control characters @function tcgetattr @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` @treturn[1] termios terminal attributes, if successful. On Windows the bitflags are all 0, and the `cc` table is empty. @treturn[2] nil @treturn[2] string error message @treturn[2] int errnum @return error message if failed @usage local system = require('system') local status = assert(tcgetattr(io.stdin)) if status.iflag:has_all_of(system.I_IGNBRK) then print("Ignoring break condition") end */ static int lst_tcgetattr(lua_State *L) { #ifndef _WIN32 int r, i; struct termios t; int fd = get_console_handle(L); r = tcgetattr(fd, &t); if (r == -1) return pusherror(L, NULL); lua_newtable(L); lsbf_pushbitflags(L, t.c_iflag); lua_setfield(L, -2, "iflag"); lsbf_pushbitflags(L, t.c_oflag); lua_setfield(L, -2, "oflag"); lsbf_pushbitflags(L, t.c_lflag); lua_setfield(L, -2, "lflag"); lsbf_pushbitflags(L, t.c_cflag); lua_setfield(L, -2, "cflag"); lua_pushinteger(L, cfgetispeed(&t)); lua_setfield(L, -2, "ispeed"); lua_pushinteger(L, cfgetospeed(&t)); lua_setfield(L, -2, "ospeed"); lua_newtable(L); for (i=0; i 1) { lua_settop(L, 1); // use one argument, because the second boolean will fail as get_console_flags expects bitflags } HANDLE console_handle = get_console_handle(L, 1); if (console_handle == NULL) { return 2; // error message is already on the stack } #endif lua_pushboolean(L, 1); return 1; } /*** Gets non-blocking mode status for a file (Posix). @function getnonblock @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` @treturn[1] bool `true` if set to non-blocking, `false` if not. Always returns `false` on Windows. @treturn[2] nil @treturn[2] string error message @treturn[2] int errnum */ static int lst_getnonblock(lua_State *L) { #ifndef _WIN32 int fd = get_console_handle(L); // Set O_NONBLOCK int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { return pusherror(L, "Error getting handle flags: "); } if (flags & O_NONBLOCK) { lua_pushboolean(L, 1); } else { lua_pushboolean(L, 0); } #else if (lua_gettop(L) > 1) { lua_settop(L, 1); // use one argument, because the second boolean will fail as get_console_flags expects bitflags } HANDLE console_handle = get_console_handle(L, 1); if (console_handle == NULL) { return 2; // error message is already on the stack } lua_pushboolean(L, 0); #endif return 1; } /*------------------------------------------------------------------------- * Reading keyboard input *-------------------------------------------------------------------------*/ #ifdef _WIN32 // Define a static buffer for UTF-8 characters static char utf8_buffer[4]; static int utf8_buffer_len = 0; static int utf8_buffer_index = 0; #endif /*** Reads a key from the console non-blocking. This function should not be called directly, but through the `system.readkey` or `system.readansi` functions. It will return the next byte from the input stream, or `nil` if no key was pressed. On Posix, `io.stdin` must be set to non-blocking mode using `setnonblock` and canonical mode must be turned off using `tcsetattr`, before calling this function. Otherwise it will block. No conversions are done on Posix, so the byte read is returned as-is. On Windows this reads a wide character and converts it to UTF-8. Multi-byte sequences will be buffered internally and returned one byte at a time. @function _readkey @treturn[1] integer the byte read from the input stream @treturn[2] nil if no key was pressed @treturn[3] nil on error @treturn[3] string error message @treturn[3] int errnum (on posix) */ static int lst_readkey(lua_State *L) { #ifdef _WIN32 if (utf8_buffer_len > 0) { // Buffer not empty, return the next byte lua_pushinteger(L, (unsigned char)utf8_buffer[utf8_buffer_index]); utf8_buffer_index++; utf8_buffer_len--; // printf("returning from buffer: %d\n", luaL_checkinteger(L, -1)); if (utf8_buffer_len == 0) { utf8_buffer_index = 0; } return 1; } if (!_kbhit()) { return 0; } wchar_t wc = _getwch(); // printf("----\nread wchar_t: %x\n", wc); if (wc == WEOF) { lua_pushnil(L); lua_pushliteral(L, "read error"); return 2; } if (sizeof(wchar_t) == 2) { // printf("2-byte wchar_t\n"); // only 2 bytes wide, not 4 if (wc >= 0xD800 && wc <= 0xDBFF) { // printf("2-byte wchar_t, received high, getting low...\n"); // we got a high surrogate, so we need to read the next one as the low surrogate if (!_kbhit()) { lua_pushnil(L); lua_pushliteral(L, "incomplete surrogate pair"); return 2; } wchar_t wc2 = _getwch(); // printf("read wchar_t 2: %x\n", wc2); if (wc2 == WEOF) { lua_pushnil(L); lua_pushliteral(L, "read error"); return 2; } if (wc2 < 0xDC00 || wc2 > 0xDFFF) { lua_pushnil(L); lua_pushliteral(L, "invalid surrogate pair"); return 2; } // printf("2-byte pair complete now\n"); wchar_t wch_pair[2] = { wc, wc2 }; utf8_buffer_len = WideCharToMultiByte(CP_UTF8, 0, wch_pair, 2, utf8_buffer, sizeof(utf8_buffer), NULL, NULL); } else { // printf("2-byte wchar_t, no surrogate pair\n"); // not a high surrogate, so we can handle just the 2 bytes directly utf8_buffer_len = WideCharToMultiByte(CP_UTF8, 0, &wc, 1, utf8_buffer, sizeof(utf8_buffer), NULL, NULL); } } else { // printf("4-byte wchar_t\n"); // 4 bytes wide, so handle as UTF-32 directly utf8_buffer_len = WideCharToMultiByte(CP_UTF8, 0, &wc, 1, utf8_buffer, sizeof(utf8_buffer), NULL, NULL); } // printf("utf8_buffer_len: %d\n", utf8_buffer_len); utf8_buffer_index = 0; if (utf8_buffer_len <= 0) { lua_pushnil(L); lua_pushliteral(L, "UTF-8 conversion error"); return 2; } lua_pushinteger(L, (unsigned char)utf8_buffer[utf8_buffer_index]); utf8_buffer_index++; utf8_buffer_len--; // printf("returning from buffer: %x\n", luaL_checkinteger(L, -1)); return 1; #else // Posix implementation char ch; ssize_t bytes_read = read(STDIN_FILENO, &ch, 1); if (bytes_read > 0) { lua_pushinteger(L, (unsigned char)ch); return 1; } else if (bytes_read == 0) { return 0; // End of file or stream closed } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { // Resource temporarily unavailable, no data available to read return 0; } else { return pusherror(L, "read error"); } } #endif } /*------------------------------------------------------------------------- * Retrieve terminal size *-------------------------------------------------------------------------*/ /*** Get the size of the terminal in rows and columns. @function termsize @treturn[1] int the number of rows @treturn[1] int the number of columns @treturn[2] nil @treturn[2] string error message */ static int lst_termsize(lua_State *L) { int columns, rows; #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { termFormatError(L, GetLastError(), "Failed to get terminal size."); return 2; } columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; #else struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1) { return pusherror(L, "Failed to get terminal size."); } columns = ws.ws_col; rows = ws.ws_row; #endif lua_pushinteger(L, rows); lua_pushinteger(L, columns); return 2; } /*------------------------------------------------------------------------- * utf8 conversion and support *-------------------------------------------------------------------------*/ // Function to convert a single UTF-8 character to a Unicode code point (uint32_t) // To prevent having to do codepage/locale changes, we use a custom implementation int utf8_to_wchar(const char *utf8, size_t len, mk_wchar_t *codepoint) { if (len == 0) { return -1; // No input provided } unsigned char c = (unsigned char)utf8[0]; if (c <= 0x7F) { *codepoint = c; return 1; } else if ((c & 0xE0) == 0xC0) { if (len < 2) return -1; // Not enough bytes *codepoint = ((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F); return 2; } else if ((c & 0xF0) == 0xE0) { if (len < 3) return -1; // Not enough bytes *codepoint = ((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F); return 3; } else if ((c & 0xF8) == 0xF0) { if (len < 4) return -1; // Not enough bytes *codepoint = ((utf8[0] & 0x07) << 18) | ((utf8[1] & 0x3F) << 12) | ((utf8[2] & 0x3F) << 6) | (utf8[3] & 0x3F); return 4; } else { // Invalid UTF-8 character return -1; } } /*** Get the width of a utf8 character for terminal display. @function utf8cwidth @tparam string utf8_char the utf8 character to check, only the width of the first character will be returned @treturn[1] int the display width in columns of the first character in the string (0 for an empty string) @treturn[2] nil @treturn[2] string error message */ int lst_utf8cwidth(lua_State *L) { const char *utf8_char; size_t utf8_len; utf8_char = luaL_checklstring(L, 1, &utf8_len); int width = 0; mk_wchar_t wc; if (utf8_len == 0) { lua_pushinteger(L, 0); return 1; } // Convert the UTF-8 string to a wide character int bytes_processed = utf8_to_wchar(utf8_char, utf8_len, &wc); if (bytes_processed == -1) { lua_pushnil(L); lua_pushstring(L, "Invalid UTF-8 character"); return 2; } // Get the width of the wide character width = mk_wcwidth(wc); if (width == -1) { lua_pushnil(L); lua_pushstring(L, "Character width determination failed"); return 2; } lua_pushinteger(L, width); return 1; } /*** Get the width of a utf8 string for terminal display. @function utf8swidth @tparam string utf8_string the utf8 string to check @treturn[1] int the display width of the string in columns (0 for an empty string) @treturn[2] nil @treturn[2] string error message */ int lst_utf8swidth(lua_State *L) { const char *utf8_str; size_t utf8_len; utf8_str = luaL_checklstring(L, 1, &utf8_len); int total_width = 0; if (utf8_len == 0) { lua_pushinteger(L, 0); return 1; } int bytes_processed = 0; size_t i = 0; mk_wchar_t wc; while (i < utf8_len) { bytes_processed = utf8_to_wchar(utf8_str + i, utf8_len - i, &wc); if (bytes_processed == -1) { lua_pushnil(L); lua_pushstring(L, "Invalid UTF-8 character"); return 2; } int width = mk_wcwidth(wc); if (width == -1) { lua_pushnil(L); lua_pushstring(L, "Character width determination failed"); return 2; } total_width += width; i += bytes_processed; } lua_pushinteger(L, total_width); return 1; } /*------------------------------------------------------------------------- * Windows codepage functions *-------------------------------------------------------------------------*/ /*** Gets the current console code page (Windows). @function getconsolecp @treturn[1] int the current code page (always 65001 on Posix systems) */ static int lst_getconsolecp(lua_State *L) { unsigned int cp = 65001; #ifdef _WIN32 cp = GetConsoleCP(); #endif lua_pushinteger(L, cp); return 1; } /*** Sets the current console code page (Windows). @function setconsolecp @tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8 @treturn[1] bool `true` on success (always `true` on Posix systems) */ static int lst_setconsolecp(lua_State *L) { unsigned int cp = (unsigned int)luaL_checkinteger(L, 1); int success = TRUE; #ifdef _WIN32 SetConsoleCP(cp); #endif lua_pushboolean(L, success); return 1; } /*** Gets the current console output code page (Windows). @function getconsoleoutputcp @treturn[1] int the current code page (always 65001 on Posix systems) */ static int lst_getconsoleoutputcp(lua_State *L) { unsigned int cp = 65001; #ifdef _WIN32 cp = GetConsoleOutputCP(); #endif lua_pushinteger(L, cp); return 1; } /*** Sets the current console output code page (Windows). @function setconsoleoutputcp @tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8 @treturn[1] bool `true` on success (always `true` on Posix systems) */ static int lst_setconsoleoutputcp(lua_State *L) { unsigned int cp = (unsigned int)luaL_checkinteger(L, 1); int success = TRUE; #ifdef _WIN32 SetConsoleOutputCP(cp); #endif lua_pushboolean(L, success); return 1; } /*------------------------------------------------------------------------- * Initializes module *-------------------------------------------------------------------------*/ static luaL_Reg func[] = { { "isatty", lst_isatty }, { "getconsoleflags", lst_getconsoleflags }, { "setconsoleflags", lst_setconsoleflags }, { "tcgetattr", lst_tcgetattr }, { "tcsetattr", lst_tcsetattr }, { "getnonblock", lst_getnonblock }, { "setnonblock", lst_setnonblock }, { "_readkey", lst_readkey }, { "termsize", lst_termsize }, { "utf8cwidth", lst_utf8cwidth }, { "utf8swidth", lst_utf8swidth }, { "getconsolecp", lst_getconsolecp }, { "setconsolecp", lst_setconsolecp }, { "getconsoleoutputcp", lst_getconsoleoutputcp }, { "setconsoleoutputcp", lst_setconsoleoutputcp }, { NULL, NULL } }; void term_open(lua_State *L) { // set up constants and export the constants in module table initialize_valid_flags(); // Windows flags for (int i = 0; win_console_in_flags[i].name != NULL; i++) { lsbf_pushbitflags(L, win_console_in_flags[i].value); lua_setfield(L, -2, win_console_in_flags[i].name); } for (int i = 0; win_console_out_flags[i].name != NULL; i++) { lsbf_pushbitflags(L, win_console_out_flags[i].value); lua_setfield(L, -2, win_console_out_flags[i].name); } // Unix flags for (int i = 0; nix_console_i_flags[i].name != NULL; i++) { lsbf_pushbitflags(L, nix_console_i_flags[i].value); lua_setfield(L, -2, nix_console_i_flags[i].name); } for (int i = 0; nix_console_o_flags[i].name != NULL; i++) { lsbf_pushbitflags(L, nix_console_o_flags[i].value); lua_setfield(L, -2, nix_console_o_flags[i].name); } for (int i = 0; nix_console_l_flags[i].name != NULL; i++) { lsbf_pushbitflags(L, nix_console_l_flags[i].value); lua_setfield(L, -2, nix_console_l_flags[i].name); } // Unix tcsetattr actions for (int i = 0; nix_tcsetattr_actions[i].name != NULL; i++) { lua_pushinteger(L, nix_tcsetattr_actions[i].value); lua_setfield(L, -2, nix_tcsetattr_actions[i].name); } // export functions luaL_setfuncs(L, func, 0); } luasystem-0.4.5/src/time.c000066400000000000000000000077341473046163300154610ustar00rootroot00000000000000/// @module system /// Time. // @section time #include #include #include #ifdef _WIN32 #include #include #else #include #include #endif #ifdef __APPLE__ #include #ifndef MAC_OS_X_VERSION_10_12 #define MAC_OS_X_VERSION_10_12 101200 #endif #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 || defined(CLOCK_MONOTONIC)) #define HAVE_CLOCK_GETTIME 1 #else #define HAVE_CLOCK_GETTIME 0 #endif #if !(HAVE_CLOCK_GETTIME) #include "time_osx.h" #endif #endif #include "compat.h" /*------------------------------------------------------------------------- * Gets time in s, relative to January 1, 1970 (UTC) * Returns * time in s. *-------------------------------------------------------------------------*/ #ifdef _WIN32 static double time_gettime(void) { FILETIME ft; double t; GetSystemTimeAsFileTime(&ft); /* Windows file time (time since January 1, 1601 (UTC)) */ t = ft.dwLowDateTime*1.0e-7 + ft.dwHighDateTime*(4294967296.0*1.0e-7); /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ return (t - 11644473600.0); } #else static double time_gettime(void) { struct timeval v; gettimeofday(&v, (struct timezone *) NULL); /* Unix Epoch time (time since January 1, 1970 (UTC)) */ return v.tv_sec + v.tv_usec*1.0e-6; } #endif #ifdef _WIN32 WINBASEAPI ULONGLONG WINAPI GetTickCount64(VOID); static double time_monotime(void) { ULONGLONG ms = GetTickCount64(); return ms*1.0e-3; } #else static double time_monotime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec*1.0e-9; } #endif /*** Get system time. The time is returned as the seconds since the epoch (1 January 1970 00:00:00). @function gettime @treturn number seconds (fractional) */ static int time_lua_gettime(lua_State *L) { lua_pushnumber(L, time_gettime()); return 1; } /*** Get monotonic time. The time is returned as the seconds since system start. @function monotime @treturn number seconds (fractional) */ static int time_lua_monotime(lua_State *L) { lua_pushnumber(L, time_monotime()); return 1; } /*** Sleep without a busy loop. This function will sleep, without doing a busy-loop and wasting CPU cycles. @function sleep @tparam number seconds seconds to sleep (fractional). @tparam[opt=16] integer precision minimum stepsize in milliseconds (Windows only, ignored elsewhere) @return `true` on success, or `nil+err` on failure */ #ifdef _WIN32 static int time_lua_sleep(lua_State *L) { double n = luaL_checknumber(L, 1); int precision = luaL_optinteger(L, 2, 16); if (precision < 0 || precision > 16) precision = 16; if (n > 0.0) { if (n < DBL_MAX/1000.0) n *= 1000.0; if (n > INT_MAX) n = INT_MAX; if (timeBeginPeriod(precision) != TIMERR_NOERROR) { lua_pushnil(L); lua_pushstring(L, "failed to set timer precision"); return 2; }; Sleep((int)n); timeEndPeriod(precision); } lua_pushboolean(L, 1); return 1; } #else static int time_lua_sleep(lua_State *L) { double n = luaL_checknumber(L, 1); struct timespec t, r; if (n > 0.0) { if (n > INT_MAX) n = INT_MAX; t.tv_sec = (int) n; n -= t.tv_sec; t.tv_nsec = (int) (n * 1000000000); if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; while (nanosleep(&t, &r) != 0) { t.tv_sec = r.tv_sec; t.tv_nsec = r.tv_nsec; } } lua_pushboolean(L, 1); return 1; } #endif static luaL_Reg func[] = { { "gettime", time_lua_gettime }, { "monotime", time_lua_monotime }, { "sleep", time_lua_sleep }, { NULL, NULL } }; /*------------------------------------------------------------------------- * Initializes module *-------------------------------------------------------------------------*/ void time_open(lua_State *L) { luaL_setfuncs(L, func, 0); } luasystem-0.4.5/src/time_osx.h000066400000000000000000000033051473046163300163450ustar00rootroot00000000000000#ifndef TIME_OSX_H #define TIME_OSX_H /* * clock_gettime() * * OS X doesn't implement the clock_gettime() POSIX interface, but does * provide a monotonic clock through mach_absolute_time(). On i386 and * x86_64 architectures this clock is in nanosecond units, but not so on * other devices. mach_timebase_info() provides the conversion parameters. * */ #include /* struct timespec */ #include /* errno EINVAL */ #include /* TIMEVAL_TO_TIMESPEC struct timeval gettimeofday(3) */ #include /* mach_timebase_info_data_t mach_timebase_info() mach_absolute_time() */ #define CLOCK_REALTIME 0 #define CLOCK_VIRTUAL 1 #define CLOCK_PROF 2 #define CLOCK_MONOTONIC 3 static mach_timebase_info_data_t clock_timebase = { .numer = 1, .denom = 1, }; /* clock_timebase */ void clock_gettime_init(void) __attribute__((constructor)); void clock_gettime_init(void) { if (mach_timebase_info(&clock_timebase) != KERN_SUCCESS) __builtin_abort(); } /* clock_gettime_init() */ static int clock_gettime(int clockid, struct timespec *ts) { switch (clockid) { case CLOCK_REALTIME: { struct timeval tv; if (0 != gettimeofday(&tv, 0)) return -1; TIMEVAL_TO_TIMESPEC(&tv, ts); return 0; } case CLOCK_MONOTONIC: { unsigned long long abt; abt = mach_absolute_time(); abt = abt * clock_timebase.numer / clock_timebase.denom; ts->tv_sec = abt / 1000000000UL; ts->tv_nsec = abt % 1000000000UL; return 0; } default: errno = EINVAL; return -1; } /* switch() */ } /* clock_gettime() */ #endif /* TIME_OSX_H */ luasystem-0.4.5/src/wcwidth.c000066400000000000000000000335761473046163300161770ustar00rootroot00000000000000// This file was modified from the original versions, check "modified:" comments for details // Character range updates (both the table and the +1 check) were generated using ChatGPT. /* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ #include "wcwidth.h" // modified: used to define mk_wchar_t struct interval { int first; int last; }; /* auxiliary function for binary search in interval table */ static int bisearch(mk_wchar_t ucs, const struct interval *table, int max) { // modified: use mk_wchar_t int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that mk_wchar_t characters are encoded * in ISO 10646. */ int mk_wcwidth(mk_wchar_t ucs) // modified: use mk_wchar_t { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { // modified: added new ranges to the list { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0605 }, { 0x0610, 0x061A }, { 0x061C, 0x061C }, { 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06DC }, { 0x06DF, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x07FD, 0x07FD }, { 0x0816, 0x0819 }, { 0x081B, 0x0823 }, { 0x0825, 0x0827 }, { 0x0829, 0x082D }, { 0x0859, 0x085B }, { 0x08D3, 0x08E1 }, { 0x08E3, 0x0903 }, { 0x093A, 0x093C }, { 0x093E, 0x094F }, { 0x0951, 0x0957 }, { 0x0962, 0x0963 }, { 0x0981, 0x0983 }, { 0x09BC, 0x09BC }, { 0x09BE, 0x09C4 }, { 0x09C7, 0x09C8 }, { 0x09CB, 0x09CD }, { 0x09D7, 0x09D7 }, { 0x09E2, 0x09E3 }, { 0x09FE, 0x09FE }, { 0x0A01, 0x0A03 }, { 0x0A3C, 0x0A3C }, { 0x0A3E, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 }, { 0x0A75, 0x0A75 }, { 0x0A81, 0x0A83 }, { 0x0ABC, 0x0ABC }, { 0x0ABE, 0x0AC5 }, { 0x0AC7, 0x0AC9 }, { 0x0ACB, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0AFA, 0x0AFF }, { 0x0B01, 0x0B03 }, { 0x0B3C, 0x0B3C }, { 0x0B3E, 0x0B44 }, { 0x0B47, 0x0B48 }, { 0x0B4B, 0x0B4D }, { 0x0B55, 0x0B57 }, { 0x0B62, 0x0B63 }, { 0x0B82, 0x0B82 }, { 0x0BBE, 0x0BC2 }, { 0x0BC6, 0x0BC8 }, { 0x0BCA, 0x0BCD }, { 0x0BD7, 0x0BD7 }, { 0x0C00, 0x0C04 }, { 0x0C3E, 0x0C44 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0C62, 0x0C63 }, { 0x0C81, 0x0C83 }, { 0x0CBC, 0x0CBC }, { 0x0CBE, 0x0CC4 }, { 0x0CC6, 0x0CC8 }, { 0x0CCA, 0x0CCD }, { 0x0CD5, 0x0CD6 }, { 0x0CE2, 0x0CE3 }, { 0x0D00, 0x0D03 }, { 0x0D3B, 0x0D3C }, { 0x0D3E, 0x0D44 }, { 0x0D46, 0x0D48 }, { 0x0D4A, 0x0D4D }, { 0x0D57, 0x0D57 }, { 0x0D62, 0x0D63 }, { 0x0D82, 0x0D83 }, { 0x0DCF, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0DD8, 0x0DDF }, { 0x0DF2, 0x0DF3 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1037 }, { 0x1039, 0x103A }, { 0x103D, 0x103E }, { 0x1058, 0x1059 }, { 0x105E, 0x1060 }, { 0x1071, 0x1074 }, { 0x1082, 0x1082 }, { 0x1085, 0x1086 }, { 0x108D, 0x108D }, { 0x109D, 0x109D }, { 0x135D, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180E }, { 0x1885, 0x1886 }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1A1B, 0x1A1B }, { 0x1A56, 0x1A56 }, { 0x1A58, 0x1A5E }, { 0x1A60, 0x1A60 }, { 0x1A62, 0x1A62 }, { 0x1A65, 0x1A6C }, { 0x1A73, 0x1A7C }, { 0x1A7F, 0x1A7F }, { 0x1AB0, 0x1ACE }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1B80, 0x1B82 }, { 0x1BA1, 0x1BA1 }, { 0x1BA6, 0x1BA7 }, { 0x1BAA, 0x1BAA }, { 0x1BAB, 0x1BAD }, { 0x1BE6, 0x1BE6 }, { 0x1BE8, 0x1BE9 }, { 0x1BED, 0x1BED }, { 0x1BEF, 0x1BF1 }, { 0x1C2C, 0x1C33 }, { 0x1C36, 0x1C37 }, { 0x1CD0, 0x1CD2 }, { 0x1CD4, 0x1CE8 }, { 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, { 0x1CF8, 0x1CF9 }, { 0x1DC0, 0x1DF9 }, { 0x1DFB, 0x1DFF }, { 0x20D0, 0x20DC }, { 0x20E1, 0x20E1 }, { 0x20E5, 0x20F0 }, { 0x2CEF, 0x2CF1 }, { 0x2D7F, 0x2D7F }, { 0x2DE0, 0x2DFF }, { 0x302A, 0x302D }, { 0x3099, 0x309A }, { 0xA66F, 0xA672 }, { 0xA674, 0xA67D }, { 0xA69E, 0xA69F }, { 0xA6F0, 0xA6F1 }, { 0xA802, 0xA802 }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xA82C, 0xA82C }, { 0xA8C4, 0xA8C5 }, { 0xA8E0, 0xA8F1 }, { 0xA8FF, 0xA8FF }, { 0xA926, 0xA92D }, { 0xA947, 0xA951 }, { 0xA980, 0xA982 }, { 0xA9B3, 0xA9B3 }, { 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BD }, { 0xA9E5, 0xA9E5 }, { 0xAA29, 0xAA2E }, { 0xAA31, 0xAA32 }, { 0xAA35, 0xAA36 }, { 0xAA43, 0xAA43 }, { 0xAA4C, 0xAA4C }, { 0xAA7C, 0xAA7C }, { 0xAAB0, 0xAAB0 }, { 0xAAB2, 0xAAB4 }, { 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF }, { 0xAAC1, 0xAAC1 }, { 0xAAEB, 0xAAEB }, { 0xAAEE, 0xAAEF }, { 0xAAF5, 0xAAF6 }, { 0xABE3, 0xABE4 }, { 0xABE6, 0xABE7 }, { 0xABE9, 0xABEA }, { 0xABEC, 0xABED }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE2F }, { 0x101FD, 0x101FD }, { 0x102E0, 0x102E0 }, { 0x10376, 0x1037A }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x10AE5, 0x10AE6 }, { 0x10D24, 0x10D27 }, { 0x10EAB, 0x10EAC }, { 0x10F46, 0x10F50 }, { 0x10F82, 0x10F85 }, { 0x11000, 0x11002 }, { 0x11038, 0x11046 }, { 0x1107F, 0x11082 }, { 0x110B0, 0x110BA }, { 0x11100, 0x11102 }, { 0x11127, 0x11134 }, { 0x11145, 0x11146 }, { 0x11173, 0x11173 }, { 0x11180, 0x11182 }, { 0x111B3, 0x111C0 }, { 0x111C9, 0x111CC }, { 0x1122C, 0x11237 }, { 0x1123E, 0x1123E }, { 0x112DF, 0x112EA }, { 0x11300, 0x11303 }, { 0x1133B, 0x1133C }, { 0x1133E, 0x11344 }, { 0x11347, 0x11348 }, { 0x1134B, 0x1134D }, { 0x11357, 0x11357 }, { 0x11362, 0x11363 }, { 0x11435, 0x11446 }, { 0x1145E, 0x1145E }, { 0x114B0, 0x114C3 }, { 0x115AF, 0x115B5 }, { 0x115B8, 0x115C0 }, { 0x115DC, 0x115DD }, { 0x11630, 0x11640 }, { 0x116AB, 0x116B7 }, { 0x1171D, 0x1172B }, { 0x1182C, 0x1183A }, { 0x11930, 0x11935 }, { 0x11937, 0x11938 }, { 0x1193B, 0x1193E }, { 0x11940, 0x11940 }, { 0x11942, 0x11942 }, { 0x119D1, 0x119D7 }, { 0x119DA, 0x119E0 }, { 0x11A01, 0x11A0A }, { 0x11A33, 0x11A39 }, { 0x11A3B, 0x11A3E }, { 0x11A47, 0x11A47 }, { 0x11A51, 0x11A5B }, { 0x11A8A, 0x11A96 }, { 0x11A98, 0x11A99 }, { 0x11C30, 0x11C36 }, { 0x11C38, 0x11C3D }, { 0x11C3F, 0x11C3F }, { 0x11C92, 0x11CA7 }, { 0x11CAA, 0x11CB0 }, { 0x11CB2, 0x11CB3 }, { 0x11CB5, 0x11CB6 }, { 0x11D31, 0x11D36 }, { 0x11D3A, 0x11D3A }, { 0x11D3C, 0x11D3D }, { 0x11D3F, 0x11D45 }, { 0x11D47, 0x11D47 }, { 0x11D90, 0x11D91 }, { 0x11D95, 0x11D95 }, { 0x11D97, 0x11D97 }, { 0x11EF3, 0x11EF4 }, { 0x13430, 0x13438 }, { 0x16AF0, 0x16AF4 }, { 0x16B30, 0x16B36 }, { 0x16F4F, 0x16F4F }, { 0x16F8F, 0x16F92 }, { 0x1BC9D, 0x1BC9E }, { 0x1BCA0, 0x1BCA3 }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0x1DA00, 0x1DA36 }, { 0x1DA3B, 0x1DA6C }, { 0x1DA75, 0x1DA75 }, { 0x1DA84, 0x1DA84 }, { 0x1DA9B, 0x1DA9F }, { 0x1DAA1, 0x1DAAF }, { 0x1E000, 0x1E006 }, { 0x1E008, 0x1E018 }, { 0x1E01B, 0x1E021 }, { 0x1E023, 0x1E024 }, { 0x1E026, 0x1E02A }, { 0x1E130, 0x1E136 }, { 0x1E2AE, 0x1E2AE }, { 0x1E2EC, 0x1E2EF }, { 0x1E4EC, 0x1E4EF }, { 0x1E8D0, 0x1E8D6 }, { 0x1E944, 0x1E94A }, { 0x1E947, 0x1E94A }, { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x1f300 && ucs <= 0x1f64f) || /* modified: added Emoticons */ (ucs >= 0x1f680 && ucs <= 0x1f6ff) || /* modified: added Transport and Map Symbols */ (ucs >= 0x1f900 && ucs <= 0x1f9ff) || /* modified: added Supplemental Symbols and Pictographs */ (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } int mk_wcswidth(const mk_wchar_t *pwcs, size_t n) // modified: use mk_wchar_t { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth(*pwcs)) < 0) return -1; else width += w; return width; } luasystem-0.4.5/src/wcwidth.h000066400000000000000000000010411473046163300161620ustar00rootroot00000000000000// wcwidth.h // Windows does not have a wcwidth function, so we use compatibilty code from // http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c by Markus Kuhn #ifndef MK_WCWIDTH_H #define MK_WCWIDTH_H #ifdef _WIN32 #include #include typedef uint32_t mk_wchar_t; // Windows wchar_t can be 16-bit, we need 32-bit #else #include typedef wchar_t mk_wchar_t; // Posix wchar_t is 32-bit so just use that #endif int mk_wcwidth(mk_wchar_t ucs); int mk_wcswidth(const mk_wchar_t *pwcs, size_t n); #endif // MK_WCWIDTH_H luasystem-0.4.5/system/000077500000000000000000000000001473046163300151015ustar00rootroot00000000000000luasystem-0.4.5/system/init.lua000066400000000000000000000306161473046163300165550ustar00rootroot00000000000000--- Lua System Library. -- @module system --- Terminal -- @section terminal local system = require 'system.core' --- UTF8 codepage. -- To be used with `system.setconsoleoutputcp` and `system.setconsolecp`. -- @field CODEPAGE_UTF8 The Windows CodePage for UTF8. system.CODEPAGE_UTF8 = 65001 do local backup_mt = {} --- Returns a backup of terminal settings for stdin/out/err. -- Handles terminal/console flags, Windows codepage, and non-block flags on the streams. -- Backs up terminal/console flags only if a stream is a tty. -- @return table with backup of terminal settings function system.termbackup() local backup = setmetatable({}, backup_mt) if system.isatty(io.stdin) then backup.console_in = system.getconsoleflags(io.stdin) backup.term_in = system.tcgetattr(io.stdin) end if system.isatty(io.stdout) then backup.console_out = system.getconsoleflags(io.stdout) backup.term_out = system.tcgetattr(io.stdout) end if system.isatty(io.stderr) then backup.console_err = system.getconsoleflags(io.stderr) backup.term_err = system.tcgetattr(io.stderr) end backup.block_in = system.getnonblock(io.stdin) backup.block_out = system.getnonblock(io.stdout) backup.block_err = system.getnonblock(io.stderr) backup.consoleoutcodepage = system.getconsoleoutputcp() backup.consolecp = system.getconsolecp() return backup end --- Restores terminal settings from a backup -- @tparam table backup the backup of terminal settings, see `termbackup`. -- @treturn boolean true function system.termrestore(backup) if getmetatable(backup) ~= backup_mt then error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2) end if backup.console_in then system.setconsoleflags(io.stdin, backup.console_in) end if backup.term_in then system.tcsetattr(io.stdin, system.TCSANOW, backup.term_in) end if backup.console_out then system.setconsoleflags(io.stdout, backup.console_out) end if backup.term_out then system.tcsetattr(io.stdout, system.TCSANOW, backup.term_out) end if backup.console_err then system.setconsoleflags(io.stderr, backup.console_err) end if backup.term_err then system.tcsetattr(io.stderr, system.TCSANOW, backup.term_err) end if backup.block_in ~= nil then system.setnonblock(io.stdin, backup.block_in) end if backup.block_out ~= nil then system.setnonblock(io.stdout, backup.block_out) end if backup.block_err ~= nil then system.setnonblock(io.stderr, backup.block_err) end if backup.consoleoutcodepage then system.setconsoleoutputcp(backup.consoleoutcodepage) end if backup.consolecp then system.setconsolecp(backup.consolecp) end return true end end do -- autotermrestore local global_backup -- global backup for terminal settings local add_gc_method do -- __gc meta-method is not available in all Lua versions local has_gc = not newproxy or false -- `__gc` was added when `newproxy` was removed if has_gc then -- use default GC mechanism since it is available function add_gc_method(t, f) setmetatable(t, { __gc = f }) end else -- create workaround using a proxy userdata, typical for Lua 5.1 function add_gc_method(t, f) local proxy = newproxy(true) getmetatable(proxy).__gc = function() t["__gc_proxy"] = nil f(t) end t["__gc_proxy"] = proxy end end end --- Backs up terminal settings and restores them on application exit. -- Calls `termbackup` to back up terminal settings and sets up a GC method to -- automatically restore them on application exit (also works on Lua 5.1). -- @treturn[1] boolean true -- @treturn[2] nil if the backup was already created -- @treturn[2] string error message function system.autotermrestore() if global_backup then return nil, "global terminal backup was already set up" end global_backup = system.termbackup() add_gc_method(global_backup, function(self) pcall(system.termrestore, self) end) return true end -- export a reset function only upon testing if _G._TEST then function system._reset_global_backup() global_backup = nil end end end do local oldunpack = unpack or table.unpack local pack = function(...) return { n = select("#", ...), ... } end local unpack = function(t) return oldunpack(t, 1, t.n) end --- Wraps a function to automatically restore terminal settings upon returning. -- Calls `termbackup` before calling the function and `termrestore` after. -- @tparam function f function to wrap -- @treturn function wrapped function function system.termwrap(f) if type(f) ~= "function" then error("arg #1 to wrap, expected function, got " .. type(f), 2) end return function(...) local bu = system.termbackup() local results = pack(f(...)) system.termrestore(bu) return unpack(results) end end end --- Debug function for console flags (Windows). -- Pretty prints the current flags set for the handle. -- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) -- @usage -- Print the flags for stdin/out/err -- system.listconsoleflags(io.stdin) -- system.listconsoleflags(io.stdout) -- system.listconsoleflags(io.stderr) function system.listconsoleflags(fh) local flagtype if fh == io.stdin then print "------ STDIN FLAGS WINDOWS ------" flagtype = "CIF_" elseif fh == io.stdout then print "------ STDOUT FLAGS WINDOWS ------" flagtype = "COF_" elseif fh == io.stderr then print "------ STDERR FLAGS WINDOWS ------" flagtype = "COF_" end local flags = assert(system.getconsoleflags(fh)) local out = {} for k,v in pairs(system) do if type(k) == "string" and k:sub(1,4) == flagtype then if flags:has_all_of(v) then out[#out+1] = string.format("%10d [x] %s",v:value(),k) else out[#out+1] = string.format("%10d [ ] %s",v:value(),k) end end end table.sort(out) for k,v in pairs(out) do print(v) end end --- Debug function for terminal flags (Posix). -- Pretty prints the current flags set for the handle. -- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) -- @usage -- Print the flags for stdin/out/err -- system.listconsoleflags(io.stdin) -- system.listconsoleflags(io.stdout) -- system.listconsoleflags(io.stderr) function system.listtermflags(fh) if fh == io.stdin then print "------ STDIN FLAGS POSIX ------" elseif fh == io.stdout then print "------ STDOUT FLAGS POSIX ------" elseif fh == io.stderr then print "------ STDERR FLAGS POSIX ------" end local flags = assert(system.tcgetattr(fh)) for _, flagtype in ipairs { "iflag", "oflag", "lflag" } do local prefix = flagtype:sub(1,1):upper() .. "_" -- I_, O_, or L_, the constant prefixes local out = {} for k,v in pairs(system) do if type(k) == "string" and k:sub(1,2) == prefix then if flags[flagtype]:has_all_of(v) then out[#out+1] = string.format("%10d [x] %s",v:value(),k) else out[#out+1] = string.format("%10d [ ] %s",v:value(),k) end end end table.sort(out) for k,v in pairs(out) do print(v) end end end do --- Reads a single byte from the console, with a timeout. -- This function uses `system.sleep` to wait until either a byte is available or the timeout is reached. -- The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.2 seconds. -- It returns immediately if a byte is available or if `timeout` is less than or equal to `0`. -- -- Using `system.readansi` is preferred over this function. Since this function can leave stray/invalid -- byte-sequences in the input buffer, while `system.readansi` reads full ANSI and UTF8 sequences. -- @tparam number timeout the timeout in seconds. -- @treturn[1] byte the byte value that was read. -- @treturn[2] nil if no key was read -- @treturn[2] string error message; `"timeout"` if the timeout was reached. function system.readkey(timeout) if type(timeout) ~= "number" then error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2) end local interval = 0.0125 local key = system._readkey() while key == nil and timeout > 0 do system.sleep(math.min(interval, timeout)) timeout = timeout - interval interval = math.min(0.2, interval * 2) key = system._readkey() end if key then return key end return nil, "timeout" end end do local left_over_key local sequence -- table to store the sequence in progress local utf8_length -- length of utf8 sequence currently being processed local unpack = unpack or table.unpack --- Reads a single key, if it is the start of ansi escape sequence then it reads -- the full sequence. The key can be a multi-byte string in case of multibyte UTF-8 character. -- This function uses `system.readkey`, and hence `system.sleep` to wait until either a key is -- available or the timeout is reached. -- It returns immediately if a key is available or if `timeout` is less than or equal to `0`. -- In case of an ANSI sequence, it will return the full sequence as a string. -- @tparam number timeout the timeout in seconds. -- @treturn[1] string the character that was received (can be multi-byte), or a complete ANSI sequence -- @treturn[1] string the type of input: `"char"` for a single key, `"ansi"` for an ANSI sequence -- @treturn[2] nil in case of an error -- @treturn[2] string error message; `"timeout"` if the timeout was reached. -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. function system.readansi(timeout) if type(timeout) ~= "number" then error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) end local key if not sequence then -- no sequence in progress, read a key if left_over_key then -- we still have a cached key from the last call key = left_over_key left_over_key = nil else -- read a new key local err key, err = system.readkey(timeout) if key == nil then -- timeout or error return nil, err end end if key == 27 then -- looks like an ansi escape sequence, immediately read next char -- as an heuristic against manually typing escape sequences local key2 = system.readkey(0) if key2 ~= 91 and key2 ~= 79 then -- we expect either "[" or "O" for an ANSI sequence -- not the expected [ or O character, so we return the key as is -- and store the extra key read for the next call left_over_key = key2 return string.char(key), "char" end -- escape sequence detected sequence = { key, key2 } else -- check UTF8 length utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 if utf8_length == 1 then -- single byte character utf8_length = nil return string.char(key), "char" else -- UTF8 sequence detected sequence = { key } end end end local err if utf8_length then -- read remainder of UTF8 sequence local timeout_end = system.gettime() + timeout while true do key, err = system.readkey(timeout_end - system.gettime()) if err then break end table.insert(sequence, key) if #sequence == utf8_length then -- end of sequence, return the full sequence local result = string.char(unpack(sequence)) sequence = nil utf8_length = nil return result, "char" end end else -- read remainder of ANSI sequence local timeout_end = system.gettime() + timeout while true do key, err = system.readkey(timeout_end - system.gettime()) if err then break end table.insert(sequence, key) if (key >= 65 and key <= 90) or (key >= 97 and key <= 126) then -- end of sequence, return the full sequence local result = string.char(unpack(sequence)) sequence = nil return result, "ansi" end end end -- error, or timeout reached, return the sequence so far local partial = string.char(unpack(sequence)) return nil, err, partial end end return system