pax_global_header 0000666 0000000 0000000 00000000064 14730461633 0014521 g ustar 00root root 0000000 0000000 52 comment=9e0790c5060c5346039f92f4c9d97a2dbc7316ed
luasystem-0.4.5/ 0000775 0000000 0000000 00000000000 14730461633 0013555 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/.busted 0000664 0000000 0000000 00000000136 14730461633 0015044 0 ustar 00root root 0000000 0000000 return {
default = {
verbose = true,
coverage = false,
output = "gtest",
},
}
luasystem-0.4.5/.editorconfig 0000664 0000000 0000000 00000000533 14730461633 0016233 0 ustar 00root root 0000000 0000000 root = 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/ 0000775 0000000 0000000 00000000000 14730461633 0015115 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/.github/workflows/ 0000775 0000000 0000000 00000000000 14730461633 0017152 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/.github/workflows/build.yml 0000664 0000000 0000000 00000006540 14730461633 0021001 0 ustar 00root root 0000000 0000000 name: "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.yml 0000664 0000000 0000000 00000002332 14730461633 0021171 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000000331 14730461633 0021451 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000644 14730461633 0015551 0 ustar 00root root 0000000 0000000 # 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/.luacheckrc 0000664 0000000 0000000 00000000702 14730461633 0015661 0 ustar 00root root 0000000 0000000 unused_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.md 0000664 0000000 0000000 00000006655 14730461633 0015402 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000002155 14730461633 0015164 0 ustar 00root root 0000000 0000000 # 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/Makefile 0000664 0000000 0000000 00000002114 14730461633 0015213 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000001524 14730461633 0015036 0 ustar 00root root 0000000 0000000 [](https://github.com/lunarmodules/luasystem/actions/workflows/build.yml)
[](https://github.com/lunarmodules/luasystem/actions/workflows/lint.yml)
[](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.ld 0000664 0000000 0000000 00000000531 14730461633 0015342 0 ustar 00root root 0000000 0000000 project='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/ 0000775 0000000 0000000 00000000000 14730461633 0015703 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/doc_topics/01-introduction.md 0000664 0000000 0000000 00000001041 14730461633 0021160 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000000760 14730461633 0020771 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000013042 14730461633 0020260 0 ustar 00root root 0000000 0000000 # 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.css 0000664 0000000 0000000 00000013263 14730461633 0017343 0 ustar 00root root 0000000 0000000 body {
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/ 0000775 0000000 0000000 00000000000 14730461633 0014505 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/docs/classes/ 0000775 0000000 0000000 00000000000 14730461633 0016142 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/docs/classes/bitflags.html 0000664 0000000 0000000 00000032120 14730461633 0020621 0 ustar 00root root 0000000 0000000
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.
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.
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.
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.
-- 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)
endif sys.isatty(io.stderr) then
sys.setconsoleflags(io.stderr, sys.getconsoleflags(io.stderr) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
endif sys.isatty(io.stdin) then
sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdout) + sys.ENABLE_VIRTUAL_TERMINAL_INPUT)
endelse-- 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 = nilendreturnold_setenv(name, value)
endend
generated by LDoc 1.5.0Last updated 2024-12-18 02:50:03
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.0Last updated 2024-12-18 02:50:03
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(...)localfunctionread_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 }))
endlocal secret, err = io.stdin:read(...)
-- restore settings
if sys.isatty(io.stdin) thenio.stdout:write("\n") -- Add newline after reading the password
sys.setconsoleflags(io.stdin, w_oldflags)
sys.tcsetattr(io.stdin, sys.TCSANOW, p_oldflags)
endreturn 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.0Last updated 2024-12-18 02:50:03
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")
whiletruedolocal key, keytype
-- wait for a key
whilenot key do
key, keytype = sys.readansi(math.huge)
endif key == "A"thenio.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 < 32then
key = "."-- replace control characters with a simple "." to not mess up the screen
endprint("you pressed: " .. key .. " (" .. b .. ")")
if b == 27thenprint("Escape pressed, exiting")
breakendelseif 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, ", ")..")")
elseprint("unknown key type received: " .. tostring(keytype))
endend-- 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.0Last updated 2024-12-18 02:50:03
--- 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 inpairs(key_names) do
key_sequences[v] = k
end-- bell character
localfunctionbell()
io.write("\7")
io.flush()
end-- generate string to move cursor horizontally
-- positive goes right, negative goes left
localfunctioncursor_move_horiz(n)
if n == 0thenreturn""endreturn"\27[" .. (n > 0and n or -n) .. (n > 0and"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
localfunctionutf8size(b)
return b < 128and1or b < 224and2or b < 240and3or b < 248and4endlocal utf8parse dolocal utf8_value_mt = {
__tostring = function(self)
returntable.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
functionutf8parse(s)
local t = setmetatable({ chars = 0 }, utf8_value_mt)
local i = 1while i <= #s dolocal b = s:byte(i)
local w = utf8size(b)
local char = s:sub(i, i + w - 1)
t[#t + 1] = char
t.chars = t.chars + 1if sys.utf8cwidth(char) == 2then-- 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
endreturn t
endend-- 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 or80)
if len < 1thenerror("max_length must be at least 1", 2)
endif value.chars > len thenerror("value is longer than max_length", 2)
endlocal exit_keys = {}
for _, key inipairs(opts.exit_keys or {}) do
exit_keys[key] = trueendif exit_keys[1] == nilthen-- nothing provided, default to Enter-key
exit_keys[1] = key_sequences.enter
endlocal 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.
localfunctiondraw(self, redraw)
if redraw ornot self.drawn_before then-- we are at start of prompt
self.drawn_before = trueelse-- 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()
endlocal handle_key do-- keyboard input handler
local key_handlers
key_handlers = {
left = function(self)
if self.position == 1thenbell()
returnendlocal new_pos = self.position - 1while self.value[new_pos] == ""do-- skip empty strings; double width chars
new_pos = new_pos - 1endio.write(cursor_move_horiz(-(self.position - new_pos)))
io.flush()
self.position = new_pos
end,
right = function(self)
if self.position == #self.value + 1thenbell()
returnendlocal new_pos = self.position + 1while self.value[new_pos] == ""do-- skip empty strings; double width chars
new_pos = new_pos + 1endio.write(cursor_move_horiz(new_pos - self.position))
io.flush()
self.position = new_pos
end,
backspace = function(self)
if self.position == 1thenbell()
returnendwhile self.value[self.position - 1] == ""do-- remove empty strings; double width chars
io.write(cursor_move_horiz(-1))
self.position = self.position - 1table.remove(self.value, self.position)
end-- remove char itself
io.write(cursor_move_horiz(-1))
self.position = self.position - 1table.remove(self.value, self.position)
self.value.chars = self.value.chars - 1draw(self)
end,
home = function(self)
local new_pos = 1io.write(cursor_move_horiz(new_pos - self.position))
self.position = new_pos
end,
["end"] = function(self)
local new_pos = #self.value + 1io.write(cursor_move_horiz(new_pos - self.position))
self.position = new_pos
end,
delete = function(self)
if self.position > #self.value thenbell()
returnend
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
functionhandle_key(self, key, keytype)
if self.exit_keys[key] then-- registered exit key
return"exit_key"endlocal handler = key_handlers[key_names[key] ortrue ]
if handler thenhandler(self)
return"ok"endif 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"endif self.value.chars >= self.max_length thenbell()
return"ok"end-- insert the key into the value
if sys.utf8cwidth(key) == 2then-- 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 + 2io.write(cursor_move_horiz(2))
elsetable.insert(self.value, self.position, key)
self.position = self.position + 1io.write(cursor_move_horiz(1))
end
self.value.chars = self.value.chars + 1draw(self)
return"ok"endend--- 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()
return1, #self.prompt + self.max_length * 2end--- 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()
return1, #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 + 1then
col = l_prompt + 1elseif col > l_prompt + l_value + 1then
col = l_prompt + l_value + 1endwhile self.value[col - l_prompt] == ""do
col = col - 1-- on an empty string, so move back to start of double-width char
endlocal 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 ormath.huge
local timeout_end = sys.gettime() + timeout
whiletruedolocal key, keytype = sys.readansi(timeout_end - sys.gettime())
ifnot key then-- error or timeout
returnnil, keytype
endlocal status = handle_key(self, key, keytype)
if status == "exit_key"thenreturntostring(self.value), key
elseif status ~= "ok"thenerror("unknown status received: " .. tostring(status))
endendend-- 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.0Last updated 2024-12-18 02:50:03
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 > 0and wy > 0do
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 + 1elseif y > wy and move == move_up then-- end of move up
dx = -1
dy = 0
move = move_left
wx = wx - 1
mx = mx + 1elseif x < mx and move == move_left then-- end of move left
dx = 0
dy = -1
move = move_down
wy = wy - 1
my = my + 1elseif y < my and move == move_down then-- end of move down
dx = 1
dy = 0
move = move_right
wx = wx - 1
mx = mx + 1endendio.write(move_down:rep(15))
print("\nDone!")
generated by LDoc 1.5.0Last updated 2024-12-18 02:50:03
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
localfunctioncursor_move_horiz(n)
if n == 0thenreturn""endreturn"\27[" .. (n > 0and n or -n) .. (n > 0and"C"or"D")
endlocal rows, cols
print("Change the terminal window size, press any key to exit")
whilenot 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()
endend
generated by LDoc 1.5.0Last updated 2024-12-18 02:50:03
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.
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.
valuestring
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.
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.
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).
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).
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) thenprint("Both flags are set")
elseprint("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)
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.
-- 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:
timeoutnumber
the timeout in seconds.
Returns:
string
the character that was received (can be multi-byte), or a complete ANSI sequence
string
the type of input: "char" for a single key, "ansi" for an ANSI sequence
Or
nil
in case of an error
string
error message; "timeout" if the timeout was reached.
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:
timeoutnumber
the timeout in seconds.
Returns:
byte
the byte value that was read.
Or
nil
if no key was read
string
error message; "timeout" if the timeout was reached.
booltrue 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).
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).
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:
local system = require('system')
local status = assert(tcgetattr(io.stdin))
if status.iflag:has_all_of(system.I_IGNBRK) thenprint("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.
local system = require('system')
local status = assert(tcgetattr(io.stdin))
ifnot 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:
backuptable
the backup of terminal settings, see termbackup.
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.0Last updated 2024-12-18 02:50:03
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.0Last updated 2024-12-18 02:50:03
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.
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).
-- 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.0Last updated 2024-12-18 02:50:03
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.0Last updated 2024-12-18 02:50:03
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.0Last updated 2024-12-18 02:50:03
luasystem-0.4.5/examples/ 0000775 0000000 0000000 00000000000 14730461633 0015373 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/examples/compat.lua 0000664 0000000 0000000 00000002557 14730461633 0017372 0 ustar 00root root 0000000 0000000 -- 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.lua 0000664 0000000 0000000 00000000262 14730461633 0021022 0 ustar 00root root 0000000 0000000 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)
luasystem-0.4.5/examples/password_input.lua 0000664 0000000 0000000 00000003101 14730461633 0021152 0 ustar 00root root 0000000 0000000 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)
luasystem-0.4.5/examples/read.lua 0000664 0000000 0000000 00000003621 14730461633 0017013 0 ustar 00root root 0000000 0000000 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)
luasystem-0.4.5/examples/readline.lua 0000664 0000000 0000000 00000032673 14730461633 0017674 0 ustar 00root root 0000000 0000000 --- 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.lua 0000664 0000000 0000000 00000002251 14730461633 0017554 0 ustar 00root root 0000000 0000000 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!")
luasystem-0.4.5/examples/spiral_snake.lua 0000664 0000000 0000000 00000003204 14730461633 0020550 0 ustar 00root root 0000000 0000000 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!")
luasystem-0.4.5/examples/terminalsize.lua 0000664 0000000 0000000 00000002363 14730461633 0020610 0 ustar 00root root 0000000 0000000 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
luasystem-0.4.5/luasystem-scm-0.rockspec 0000664 0000000 0000000 00000003725 14730461633 0020262 0 ustar 00root root 0000000 0000000 local 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/ 0000775 0000000 0000000 00000000000 14730461633 0015551 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/rockspecs/luasystem-0.2.1-0.rockspec 0000664 0000000 0000000 00000002433 14730461633 0022125 0 ustar 00root root 0000000 0000000 package = '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.rockspec 0000664 0000000 0000000 00000003205 14730461633 0022124 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003420 14730461633 0022123 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003640 14730461633 0022130 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022136 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022137 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022140 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022141 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022142 0 ustar 00root root 0000000 0000000 local 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.rockspec 0000664 0000000 0000000 00000003727 14730461633 0022143 0 ustar 00root root 0000000 0000000 local 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/ 0000775 0000000 0000000 00000000000 14730461633 0014507 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/spec/01-time_spec.lua 0000664 0000000 0000000 00000004014 14730461633 0017377 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000002417 14730461633 0017727 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000004631 14730461633 0021014 0 ustar 00root root 0000000 0000000 -- 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.lua 0000664 0000000 0000000 00000000704 14730461633 0017742 0 ustar 00root root 0000000 0000000 -- 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.lua 0000664 0000000 0000000 00000062263 14730461633 0017425 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000007736 14730461633 0020256 0 ustar 00root root 0000000 0000000 describe("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.lua 0000664 0000000 0000000 00000000777 14730461633 0016667 0 ustar 00root root 0000000 0000000 local 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/ 0000775 0000000 0000000 00000000000 14730461633 0014344 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/src/Makefile 0000664 0000000 0000000 00000014433 14730461633 0016011 0 ustar 00root root 0000000 0000000 # 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.c 0000664 0000000 0000000 00000021507 14730461633 0016310 0 ustar 00root root 0000000 0000000 /// 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.h 0000664 0000000 0000000 00000002036 14730461633 0016311 0 ustar 00root root 0000000 0000000 #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.c 0000664 0000000 0000000 00000002276 14730461633 0016002 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000002467 14730461633 0016011 0 ustar 00root root 0000000 0000000 #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.c 0000664 0000000 0000000 00000002236 14730461633 0015443 0 ustar 00root root 0000000 0000000 /// 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.c 0000664 0000000 0000000 00000012243 14730461633 0017056 0 ustar 00root root 0000000 0000000 /// @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.c 0000664 0000000 0000000 00000005713 14730461633 0015776 0 ustar 00root root 0000000 0000000 /// @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.c 0000664 0000000 0000000 00000106245 14730461633 0015467 0 ustar 00root root 0000000 0000000 /// @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.c 0000664 0000000 0000000 00000007734 14730461633 0015461 0 ustar 00root root 0000000 0000000 /// @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.h 0000664 0000000 0000000 00000003305 14730461633 0016345 0 ustar 00root root 0000000 0000000 #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.c 0000664 0000000 0000000 00000033576 14730461633 0016177 0 ustar 00root root 0000000 0000000 // 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.h 0000664 0000000 0000000 00000001041 14730461633 0016162 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14730461633 0015101 5 ustar 00root root 0000000 0000000 luasystem-0.4.5/system/init.lua 0000664 0000000 0000000 00000030616 14730461633 0016555 0 ustar 00root root 0000000 0000000 --- 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