summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2023-11-15 19:28:06 +0100
committerGitHub <noreply@github.com>2023-11-15 19:28:06 +0100
commit90997893cc568c86805afa95db28439c28e32980 (patch)
tree4a692fcc91a811e18023ba1896aeee23fe2ee2c5
parentf868c16514be69b20a4e771cc347a80c9c439db6 (diff)
parent048f3cec7a18e7a28146f03c3c9e5d89d9613028 (diff)
downloadluasystem-90997893cc568c86805afa95db28439c28e32980.zip
Merge pull request #6 from lunarmodules/time
-rw-r--r--CHANGELOG.md48
-rw-r--r--LICENSE.md (renamed from LICENSE)29
-rw-r--r--README.md29
-rw-r--r--appveyor.yml2
-rw-r--r--config.ld16
-rw-r--r--doc_topics/01-introduction.md12
-rw-r--r--doc_topics/ldoc.css291
-rw-r--r--docs/index.html366
-rw-r--r--docs/ldoc.css291
-rw-r--r--docs/topics/01-introduction.md.html70
-rw-r--r--docs/topics/CHANGELOG.md.html121
-rw-r--r--docs/topics/LICENSE.md.html79
-rw-r--r--luasystem-scm-0.rockspec13
-rw-r--r--rockspecs/luasystem-0.2.1-1.rockspec2
-rw-r--r--spec/01-time_spec.lua76
-rw-r--r--spec/02-random_spec.lua47
-rw-r--r--spec/03-environment_spec.lua81
-rw-r--r--spec/time_spec.lua31
-rw-r--r--src/Makefile4
-rw-r--r--src/compat.h27
-rw-r--r--src/core.c16
-rw-r--r--src/environment.c173
-rw-r--r--src/random.c117
-rw-r--r--src/term.c37
-rw-r--r--src/time.c87
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
diff --git a/LICENSE b/LICENSE.md
index 0421a4a..df2befb 100644
--- a/LICENSE
+++ b/LICENSE.md
@@ -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.
diff --git a/README.md b/README.md
index 3aac3a1..6f3c9f6 100644
--- a/README.md
+++ b/README.md
@@ -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 &gt;= 5.1</code> and <code>luajit &gt;= 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
diff --git a/src/core.c b/src/core.c
index 6c46981..87fd105 100644
--- a/src/core.c
+++ b/src/core.c
@@ -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);
+}
diff --git a/src/time.c b/src/time.c
index 8c6a4f2..5f0ead0 100644
--- a/src/time.c
+++ b/src/time.c
@@ -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