diff options
author | Thijs Schreijer <thijs@thijsschreijer.nl> | 2023-11-15 19:28:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-15 19:28:06 +0100 |
commit | 90997893cc568c86805afa95db28439c28e32980 (patch) | |
tree | 4a692fcc91a811e18023ba1896aeee23fe2ee2c5 | |
parent | f868c16514be69b20a4e771cc347a80c9c439db6 (diff) | |
parent | 048f3cec7a18e7a28146f03c3c9e5d89d9613028 (diff) | |
download | luasystem-90997893cc568c86805afa95db28439c28e32980.zip |
Merge pull request #6 from lunarmodules/time
-rw-r--r-- | CHANGELOG.md | 48 | ||||
-rw-r--r-- | LICENSE.md (renamed from LICENSE) | 29 | ||||
-rw-r--r-- | README.md | 29 | ||||
-rw-r--r-- | appveyor.yml | 2 | ||||
-rw-r--r-- | config.ld | 16 | ||||
-rw-r--r-- | doc_topics/01-introduction.md | 12 | ||||
-rw-r--r-- | doc_topics/ldoc.css | 291 | ||||
-rw-r--r-- | docs/index.html | 366 | ||||
-rw-r--r-- | docs/ldoc.css | 291 | ||||
-rw-r--r-- | docs/topics/01-introduction.md.html | 70 | ||||
-rw-r--r-- | docs/topics/CHANGELOG.md.html | 121 | ||||
-rw-r--r-- | docs/topics/LICENSE.md.html | 79 | ||||
-rw-r--r-- | luasystem-scm-0.rockspec | 13 | ||||
-rw-r--r-- | rockspecs/luasystem-0.2.1-1.rockspec | 2 | ||||
-rw-r--r-- | spec/01-time_spec.lua | 76 | ||||
-rw-r--r-- | spec/02-random_spec.lua | 47 | ||||
-rw-r--r-- | spec/03-environment_spec.lua | 81 | ||||
-rw-r--r-- | spec/time_spec.lua | 31 | ||||
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/compat.h | 27 | ||||
-rw-r--r-- | src/core.c | 16 | ||||
-rw-r--r-- | src/environment.c | 173 | ||||
-rw-r--r-- | src/random.c | 117 | ||||
-rw-r--r-- | src/term.c | 37 | ||||
-rw-r--r-- | src/time.c | 87 |
25 files changed, 1970 insertions, 95 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..56d1886 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# 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/time.c` (in module constants) +- create a new rockspec and update the version inside the new rockspec:<br/> + `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:<br/> + `luarocks upload ./rockspecs/luasystem-X.Y.Z-1.rockspec --api-key=ABCDEFGH` +- test the newly created rock:<br/> + `luarocks install luasystem` + +## Version history + +### Version X.Y.Z, unreleased + +- 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 @@ -1,20 +1,21 @@ -MIT License Terms -================= +# MIT License -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 +### Copyright (c) 2016-2023 Oscar Lim + +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 +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 +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. @@ -1,20 +1,21 @@ -LuaSystem -====== - -[![travis-ci status](https://travis-ci.org/o-lim/luasystem.svg?branch=master)](https://travis-ci.org/o-lim/luasystem/builds) +[![Unix build](https://img.shields.io/github/actions/workflow/status/lunarmodules/luasystem/unix_build.yml?branch=master&label=Unix%20build&logo=linux)](https://github.com/lunarmodules/luasystem/actions/workflows/unix_build.yml) +[![AppVeyor build status](https://img.shields.io/appveyor/build/Tieske/luasystem/master?label=Windows%20build&logo=windows)](https://ci.appveyor.com/project/Tieske/luasystem/branch/master) +[![Lint](https://github.com/lunarmodules/luasystem/workflows/Lint/badge.svg)](https://github.com/lunarmodules/luasystem/actions/workflows/lint.yml) +[![SemVer](https://img.shields.io/github/v/tag/lunarmodules/luasystem?color=brightgreen&label=SemVer&logo=semver&sort=semver)](CHANGELOG.md) +# LuaSystem luasystem is a platform independent system call library for Lua. -Supports Lua >= 5.1 and luajit >= 2.0.0. +Supports Unix, Windows, MacOS, `Lua >= 5.1` and `luajit >= 2.0.0`. + +## License and copyright + +See [LICENSE.md](LICENSE.md) + +## Documentation -Currently the following functions are supported: -* gettime -* monotime -* sleep +See [online documentation](https://lunarmodules.github.io/luasystem/) -License -------- +## Changelog & Versioning -This code and its accompanying README are -[MIT licensed](http://www.opensource.org/licenses/mit-license.php). -See LICENSE for details. +See [CHANGELOG.md](CHANGELOG.md) diff --git a/appveyor.yml b/appveyor.yml index 596c846..f39445b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,4 +26,4 @@ build_script: - luarocks make test_script: - - busted + - busted --Xoutput "--color" diff --git a/config.ld b/config.ld new file mode 100644 index 0000000..c13936d --- /dev/null +++ b/config.ld @@ -0,0 +1,16 @@ +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 diff --git a/doc_topics/01-introduction.md b/doc_topics/01-introduction.md new file mode 100644 index 0000000..9cc2347 --- /dev/null +++ b/doc_topics/01-introduction.md @@ -0,0 +1,12 @@ +# 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. diff --git a/doc_topics/ldoc.css b/doc_topics/ldoc.css new file mode 100644 index 0000000..5b9fbbf --- /dev/null +++ b/doc_topics/ldoc.css @@ -0,0 +1,291 @@ +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; } diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..eb241c6 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,366 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Lua-System docs</title> + <link rel="stylesheet" href="ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Lua-System</h1> + + +<h2>Contents</h2> +<ul> +<li><a href="#environment_Functions">environment Functions</a></li> +<li><a href="#random_Functions">random Functions</a></li> +<li><a href="#term_Functions">term Functions</a></li> +<li><a href="#time_Functions">time Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><strong>system</strong></li> +</ul> +<h2>Topics</h2> +<ul class=""> + <li><a href="topics/01-introduction.md.html">1. Introduction</a></li> + <li><a href="topics/CHANGELOG.md.html">CHANGELOG</a></li> + <li><a href="topics/LICENSE.md.html">MIT License</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>system</code></h1> +<p>Platform independent system calls for Lua.</p> +<p> + +</p> + + +<h2><a href="#environment_Functions">environment Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#getenv">getenv (name)</a></td> + <td class="summary">Gets the value of an environment variable.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getenvs">getenvs ()</a></td> + <td class="summary">Returns a table with all environment variables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#setenv">setenv (name[, value])</a></td> + <td class="summary">Sets an environment variable.</td> + </tr> +</table> +<h2><a href="#random_Functions">random Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#random">random ([length=1])</a></td> + <td class="summary">Generate random bytes.</td> + </tr> +</table> +<h2><a href="#term_Functions">term Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#isatty">isatty (file)</a></td> + <td class="summary">Checks if a file-handle is a TTY.</td> + </tr> +</table> +<h2><a href="#time_Functions">time Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#gettime">gettime ()</a></td> + <td class="summary">Get system time.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#monotime">monotime ()</a></td> + <td class="summary">Get monotonic time.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sleep">sleep (seconds[, precision=16])</a></td> + <td class="summary">Sleep without a busy loop.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="environment_Functions"></a>environment Functions</h2> + + <dl class="function"> + <dt> + <a name = "getenv"></a> + <strong>getenv (name)</strong> + </dt> + <dd> + Gets the value of an environment variable. </p> + +<p><strong>NOTE</strong>: Windows has multiple copies of environment variables. For this reason, +the <a href="index.html#setenv">setenv</a> function will not work with Lua's <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv">os.getenv</a> on Windows. If you want +to use <a href="index.html#setenv">setenv</a> then consider patching <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv">os.getenv</a> with this implementation of <a href="index.html#getenv">getenv</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of the environment variable + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + value of the environment variable, or nil if the variable is not set + </ol> + + + + +</dd> + <dt> + <a name = "getenvs"></a> + <strong>getenvs ()</strong> + </dt> + <dd> + Returns a table with all environment variables. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table with all environment variables and their values + </ol> + + + + +</dd> + <dt> + <a name = "setenv"></a> + <strong>setenv (name[, value])</strong> + </dt> + <dd> + Sets an environment variable. </p> + +<p><strong>NOTE</strong>: Windows has multiple copies of environment variables. For this reason, the +<a href="index.html#setenv">setenv</a> function will not work with Lua's <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv">os.getenv</a> on Windows. If you want to use +it then consider patching <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv">os.getenv</a> with the implementation of <a href="index.html#getenv">system.getenv</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of the environment variable + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + value of the environment variable, if <code>nil</code> the variable will be deleted (on +Windows, setting an empty string, will also delete the variable) + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">boolean</span></span> + success + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="random_Functions"></a>random Functions</h2> + + <dl class="function"> + <dt> + <a name = "random"></a> + <strong>random ([length=1])</strong> + </dt> + <dd> + Generate random bytes. +This uses <code>CryptGenRandom()</code> on Windows, and <code>/dev/urandom</code> on other platforms. It will return the +requested number of bytes, or an error, never a partial result. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">length</span> + <span class="types"><span class="type">int</span></span> + number of bytes to get + (<em>default</em> 1) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string of random bytes + </ol> + <h3>Or</h3> + <ol> + <li> + <span class="types"><span class="type">nil</span></span> + + +</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="term_Functions"></a>term Functions</h2> + + <dl class="function"> + <dt> + <a name = "isatty"></a> + <strong>isatty (file)</strong> + </dt> + <dd> + Checks if a file-handle is a TTY. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + <span class="types"><span class="type">file</span></span> + the file-handle to check + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">boolean</span></span> + true if the file is a tty + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="time_Functions"></a>time Functions</h2> + + <dl class="function"> + <dt> + <a name = "gettime"></a> + <strong>gettime ()</strong> + </dt> + <dd> + Get system time. +The time is returned as the seconds since the epoch (1 January 1970 00:00:00). + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + seconds (fractional) + </ol> + + + + +</dd> + <dt> + <a name = "monotime"></a> + <strong>monotime ()</strong> + </dt> + <dd> + Get monotonic time. +The time is returned as the seconds since system start. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + seconds (fractional) + </ol> + + + + +</dd> + <dt> + <a name = "sleep"></a> + <strong>sleep (seconds[, precision=16])</strong> + </dt> + <dd> + Sleep without a busy loop. +This function will sleep, without doing a busy-loop and wasting CPU cycles. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">seconds</span> + <span class="types"><span class="type">number</span></span> + seconds to sleep (fractional). + </li> + <li><span class="parameter">precision</span> + <span class="types"><span class="type">integer</span></span> + minimum stepsize in milliseconds (Windows only, ignored elsewhere) + (<em>default</em> 16) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> on success, or <code>nil+err</code> on failure + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-11-15 07:42:18 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/ldoc.css b/docs/ldoc.css new file mode 100644 index 0000000..5b9fbbf --- /dev/null +++ b/docs/ldoc.css @@ -0,0 +1,291 @@ +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; } diff --git a/docs/topics/01-introduction.md.html b/docs/topics/01-introduction.md.html new file mode 100644 index 0000000..2901cc9 --- /dev/null +++ b/docs/topics/01-introduction.md.html @@ -0,0 +1,70 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Lua-System docs</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Lua-System</h1> + + + + +<h2>Topics</h2> +<ul class=""> + <li><strong>1. Introduction</strong></li> + <li><a href="../topics/CHANGELOG.md.html">CHANGELOG</a></li> + <li><a href="../topics/LICENSE.md.html">MIT License</a></li> +</ul> +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../index.html">system</a></li> +</ul> + +</div> + +<div id="content"> + + +<h1>1. Introduction</h1> + +<p>luasystem is a platform independent system call library for Lua. +Supports Unix, Windows, MacOS, <code>Lua >= 5.1</code> and <code>luajit >= 2.0.0</code>.</p> + +<p>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.</p> + +<p>This is not a kitchen sink library, but a minimalistic one with a focus on platform +independence.</p> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-11-15 07:42:18 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/topics/CHANGELOG.md.html b/docs/topics/CHANGELOG.md.html new file mode 100644 index 0000000..f909b4b --- /dev/null +++ b/docs/topics/CHANGELOG.md.html @@ -0,0 +1,121 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Lua-System docs</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Lua-System</h1> + + +<h2>Contents</h2> +<ul> +<li><a href="#Versioning">Versioning </a></li> +<li><a href="#Version_history">Version history </a></li> +</ul> + + +<h2>Topics</h2> +<ul class=""> + <li><a href="../topics/01-introduction.md.html">1. Introduction</a></li> + <li><strong>CHANGELOG</strong></li> + <li><a href="../topics/LICENSE.md.html">MIT License</a></li> +</ul> +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../index.html">system</a></li> +</ul> + +</div> + +<div id="content"> + + +<h1>CHANGELOG</h1> + +<p><a name="Versioning"></a></p> +<h2>Versioning</h2> + +<p>This library is versioned based on Semantic Versioning (<a href="https://semver.org/">SemVer</a>).</p> + +<h4>Version scoping</h4> + +<p>The scope of what is covered by the version number excludes:</p> + +<ul> + <li>error messages; the text of the messages can change, unless specifically documented.</li> +</ul> + +<h4>Releasing new versions</h4> + +<ul> + <li>create a release branch</li> + <li>update the changelog below</li> + <li>update version and copyright-years in <code>./LICENSE.md</code> and <code>./src/time.c</code> (in module constants)</li> + <li>create a new rockspec and update the version inside the new rockspec:<br/> + <code>cp luasystem-scm-0.rockspec ./rockspecs/luasystem-X.Y.Z-1.rockspec</code></li> + <li>clean and render the docs: run <code>ldoc .</code></li> + <li>commit the changes as <code>Release vX.Y.Z</code></li> + <li>push the commit, and create a release PR</li> + <li>after merging tag the release commit with <code>vX.Y.Z</code></li> + <li>upload to LuaRocks:<br/> + <code>luarocks upload ./rockspecs/luasystem-X.Y.Z-1.rockspec --api-key=ABCDEFGH</code></li> + <li>test the newly created rock:<br/> + <code>luarocks install luasystem</code></li> +</ul> + +<p><a name="Version_history"></a></p> +<h2>Version history</h2> + +<h3>Version X.Y.Z, unreleased</h3> + +<ul> + <li>Feat: on Windows <a href="../index.html#sleep">sleep</a> now has a precision parameter</li> + <li>Feat: <a href="../index.html#setenv">setenv</a> added to set environment variables.</li> + <li>Feat: <a href="../index.html#getenvs">getenvs</a> added to list environment variables.</li> + <li>Feat: <a href="../index.html#getenv">getenv</a> added to get environment variable previously set (Windows).</li> + <li>Feat: <a href="../index.html#random">random</a> added to return high-quality random bytes</li> + <li>Feat: <a href="../index.html#isatty">isatty</a> added to check if a file-handle is a tty</li> +</ul> + +<h3>Version 0.2.1, released 02-Oct-2016</h3> + +<h3>Version 0.2.0, released 08-May-2016</h3> + +<h3>Version 0.1.1, released 10-Apr-2016</h3> + +<h3>Version 0.1.0, released 11-Feb-2016</h3> + +<ul> + <li>initial release</li> +</ul> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-11-15 07:42:18 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/topics/LICENSE.md.html b/docs/topics/LICENSE.md.html new file mode 100644 index 0000000..67614d2 --- /dev/null +++ b/docs/topics/LICENSE.md.html @@ -0,0 +1,79 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Lua-System docs</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Lua-System</h1> + + + + +<h2>Topics</h2> +<ul class=""> + <li><a href="../topics/01-introduction.md.html">1. Introduction</a></li> + <li><a href="../topics/CHANGELOG.md.html">CHANGELOG</a></li> + <li><strong>MIT License</strong></li> +</ul> +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../index.html">system</a></li> +</ul> + +</div> + +<div id="content"> + + +<h1>MIT License</h1> + +<h3>Copyright (c) 2016-2023 Oscar Lim</h3> + +<p>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:</p> + +<p>The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software.</p> + +<p>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.</p> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-11-15 07:42:18 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/luasystem-scm-0.rockspec b/luasystem-scm-0.rockspec index 74e301d..86209a6 100644 --- a/luasystem-scm-0.rockspec +++ b/luasystem-scm-0.rockspec @@ -11,7 +11,7 @@ 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 package_version or nil, + tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { @@ -39,13 +39,20 @@ local function make_platform(plat) linux = { "rt" }, unix = { }, macosx = { }, - win32 = { }, + win32 = { "advapi32", "winmm" }, mingw32 = { }, } return { modules = { ['system.core'] = { - sources = { 'src/core.c', 'src/compat.c', 'src/time.c', }, + 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], }, diff --git a/rockspecs/luasystem-0.2.1-1.rockspec b/rockspecs/luasystem-0.2.1-1.rockspec index 7d8b9b0..e14118d 100644 --- a/rockspecs/luasystem-0.2.1-1.rockspec +++ b/rockspecs/luasystem-0.2.1-1.rockspec @@ -11,7 +11,7 @@ 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 package_version or nil, + tag = (package_version ~= "scm") and "v"..package_version or nil, } description = { diff --git a/spec/01-time_spec.lua b/spec/01-time_spec.lua new file mode 100644 index 0000000..1607cca --- /dev/null +++ b/spec/01-time_spec.lua @@ -0,0 +1,76 @@ +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) diff --git a/spec/02-random_spec.lua b/spec/02-random_spec.lua new file mode 100644 index 0000000..23b6d95 --- /dev/null +++ b/spec/02-random_spec.lua @@ -0,0 +1,47 @@ +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) diff --git a/spec/03-environment_spec.lua b/spec/03-environment_spec.lua new file mode 100644 index 0000000..842ed6f --- /dev/null +++ b/spec/03-environment_spec.lua @@ -0,0 +1,81 @@ +-- Import the library that contains the environment-related functions +local system = require("system") + +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) + + + local func = system.windows and pending or it --pending on Windows + -- Windows will unset a variable if set as an empty string + func("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) diff --git a/spec/time_spec.lua b/spec/time_spec.lua deleted file mode 100644 index a017cfe..0000000 --- a/spec/time_spec.lua +++ /dev/null @@ -1,31 +0,0 @@ -local system = require 'system.core' - -describe('Test time functions', function() - it('gettime returns current time', function() - local starttime = system.gettime() - local expected = os.time() - local endtime = system.gettime() - local delta = endtime - starttime - local avg = starttime + delta/2 - assert.is_true(expected >= math.floor(starttime)) - assert.is_true(expected <= math.ceil(endtime)) - assert.is_near(expected, avg, 1 + delta) - end) - - it('monottime 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) - - it('sleep will wait for specified amount of time', function() - local starttime = system.gettime() - local starttick = system.monotime() - system.sleep(0.5) - assert.is_near(0.5, system.gettime() - starttime, 0.15) - assert.is_near(0.5, system.monotime() - starttick, 0.15) - end) -end) diff --git a/src/Makefile b/src/Makefile index 10fc31a..b4ed16f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -85,7 +85,7 @@ 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 +LUALIBNAME_win32?=lua$(subst .,,$(LUA_VERSION)).lib # prefix: /usr/local /usr /opt/local /sw @@ -217,7 +217,7 @@ LUALIB= $(LUALIB_$(PLAT)) #------ # Objects # -OBJS=core.$(O) compat.$(O) time.$(O) +OBJS=core.$(O) compat.$(O) time.$(O) environment.$(O) random.$(O) term.$(O) #------ # Targets diff --git a/src/compat.h b/src/compat.h index f523fd9..5aca6df 100644 --- a/src/compat.h +++ b/src/compat.h @@ -8,4 +8,31 @@ void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup); #endif +// Windows doesn't have ssize_t, so we define it here +#ifdef _WIN32 +#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 + #endif @@ -1,3 +1,6 @@ +/// Platform independent system calls for Lua. +// @module system + #include <lua.h> #include <lauxlib.h> @@ -10,6 +13,9 @@ #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); /*------------------------------------------------------------------------- * Initializes all library modules. @@ -19,6 +25,16 @@ LUAEXPORT int luaopen_system_core(lua_State *L) { lua_pushstring(L, "_VERSION"); lua_pushstring(L, LUASYSTEM_VERSION); lua_rawset(L, -3); + lua_pushstring(L, "windows"); +#ifdef _WIN32 + lua_pushboolean(L, 1); +#else + lua_pushboolean(L, 0); +#endif + lua_rawset(L, -3); time_open(L); + random_open(L); + term_open(L); + environment_open(L); return 1; } diff --git a/src/environment.c b/src/environment.c new file mode 100644 index 0000000..5f1c3da --- /dev/null +++ b/src/environment.c @@ -0,0 +1,173 @@ +/// @submodule system +#include <lua.h> +#include <lauxlib.h> +#include "compat.h" +#include <stdlib.h> +#include <string.h> + +#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); +} diff --git a/src/random.c b/src/random.c new file mode 100644 index 0000000..90fb3f2 --- /dev/null +++ b/src/random.c @@ -0,0 +1,117 @@ +/// @submodule system +#include <lua.h> +#include <lauxlib.h> +#include "compat.h" +#include <fcntl.h> + +#ifdef _WIN32 +#include "windows.h" +#include "wincrypt.h" +#else +#include <errno.h> +#include <unistd.h> +#include <string.h> +#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); +} diff --git a/src/term.c b/src/term.c new file mode 100644 index 0000000..2adb1e9 --- /dev/null +++ b/src/term.c @@ -0,0 +1,37 @@ +/// @submodule system +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#include "compat.h" + +#ifndef _MSC_VER +# include <unistd.h> +#endif + + +/*** +Checks if a file-handle is a TTY. + +@function isatty +@tparam file file the file-handle to check +@treturn boolean true if the file is a tty +*/ +static int lua_isatty(lua_State* L) { + FILE **fh = (FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); + lua_pushboolean(L, isatty(fileno(*fh))); + return 1; +} + + + +static luaL_Reg func[] = { + { "isatty", lua_isatty }, + { NULL, NULL } +}; + +/*------------------------------------------------------------------------- + * Initializes module + *-------------------------------------------------------------------------*/ +void term_open(lua_State *L) { + luaL_setfuncs(L, func, 0); +} @@ -1,3 +1,4 @@ +/// @submodule system #include <lua.h> #include <lauxlib.h> @@ -50,11 +51,8 @@ static double time_gettime(void) { } #endif -/*------------------------------------------------------------------------- - * Gets monotonic time in s - * Returns - * time in s. - *-------------------------------------------------------------------------*/ + + #ifdef _WIN32 WINBASEAPI ULONGLONG WINAPI GetTickCount64(VOID); @@ -70,53 +68,84 @@ static double time_monotime(void) { } #endif -/*------------------------------------------------------------------------- - * Returns the current system time, 1970 (UTC), in secconds. - *-------------------------------------------------------------------------*/ + + +/*** +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; } -/*------------------------------------------------------------------------- - * Returns the monotonic time the system has been up, in secconds. - *-------------------------------------------------------------------------*/ + + +/*** +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 for n seconds. - *-------------------------------------------------------------------------*/ + + +/*** +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); - if (n < 0.0) n = 0.0; - if (n < DBL_MAX/1000.0) n *= 1000.0; - if (n > INT_MAX) n = INT_MAX; - Sleep((int)n); - return 0; + + 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) 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; + 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; + } } - return 0; + lua_pushboolean(L, 1); + return 1; } #endif |