Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Wobbo 2014-02-22 15:28:25 +01:00
commit 36f93eae9e
56 changed files with 726 additions and 332 deletions

26
LICENSE Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2013-2014 Florian "Sangar" Nücke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-------------------------------------------------------------------------------
All images / textures and localization strings (resources) are put in the
public domain. More specicially, see CC0 1.0 Universal:
http://creativecommons.org/publicdomain/zero/1.0/

25
LICENSE-jnlua Normal file
View File

@ -0,0 +1,25 @@
OpenComputers uses the JNLua library, with a patch to allow memory limits:
http://code.google.com/p/jnlua/issues/detail?id=9
JNLua is copyrighted and licensed as follows:
Copyright (C) 2008,2012 Andre Naef
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

19
LICENSE-luaj Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2007 LuaJ. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

22
LICENSE-mods Normal file
View File

@ -0,0 +1,22 @@
OpenComputers uses APIs from a couple of other mods.
To support accepting energy from their respective power systems:
- BuildCraft [1], license: Minecraft Mod Public License, see homepage.
- Universal Electricity [3], license: Lesser General Public License [4].
To support bundled redstone input and output:
- RedLogic [6], license: MIT License, see thread.
- MineFactory Reloaded [7], license: custom, see thread.
- Project: Red [9], license: Restricted Public Software License [10].
For providing a peripheral and reading their floppy disks:
- ComputerCraft [8], copyright Daniel Ratcliffe.
[1] http://www.mod-buildcraft.com/
[3] http://universalelectricity.com/
[4] http://www.gnu.org/copyleft/lesser.html
[6] http://www.minecraftforum.net/topic/1852277-162-redlogic-wip-replacement-for-rp2-wiringlogiccontrollighting/
[7] http://www.minecraftforum.net/topic/2016680-162164-powercrystals-mods-minefactoryreloaded-powercrystalscore-and-netherores-updated/
[8] http://www.computercraft.info/
[9] http://projectredwiki.com/
[10] https://github.com/MrTJP/ProjectRed/blob/master/LICENSE.md

207
LICENSE-typesafe Normal file
View File

@ -0,0 +1,207 @@
OpenComputers uses the typesafe config library, it is copyrighted as follows:
Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
It is licensed as follows:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,11 +1,13 @@
package li.cil.oc.api.fs; package li.cil.oc.api.fs;
import li.cil.oc.api.Persistable;
/** /**
* Used by file system components to get and set the file system's label. * Used by file system components to get and set the file system's label.
* *
* @see li.cil.oc.api.FileSystem#asManagedEnvironment(FileSystem, Label) * @see li.cil.oc.api.FileSystem#asManagedEnvironment(FileSystem, Label)
*/ */
public interface Label { public interface Label extends Persistable {
/** /**
* Get the current value of this label. * Get the current value of this label.
* <p/> * <p/>

View File

@ -37,5 +37,5 @@
@cpw.mods.fml.common.API( @cpw.mods.fml.common.API(
owner = "OpenComputers|Core", owner = "OpenComputers|Core",
provides = "OpenComputersAPI", provides = "OpenComputersAPI",
apiVersion = "1.3.0") apiVersion = "1.4.0")
package li.cil.oc.api; package li.cil.oc.api;

View File

@ -489,7 +489,7 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def installedMemory = 96 * 1024 override def installedMemory = 128 * 1024
override def tier = 0 override def tier = 0

View File

@ -28,7 +28,7 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
@Callback(doc = """function():boolean -- Returns whether the screen is currently on.""") @Callback(doc = """function():boolean -- Returns whether the screen is currently on.""")
def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn) def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn)
@Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""") @Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""")
def turnOn(computer: Context, args: Arguments): Array[AnyRef] = { def turnOn(computer: Context, args: Arguments): Array[AnyRef] = {
if (!origin.isOn) { if (!origin.isOn) {
origin.turnOn() origin.turnOn()
@ -37,7 +37,7 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
else result(false, origin.isOn) else result(false, origin.isOn)
} }
@Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""") @Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""")
def turnOff(computer: Context, args: Arguments): Array[AnyRef] = { def turnOff(computer: Context, args: Arguments): Array[AnyRef] = {
if (origin.isOn) { if (origin.isOn) {
origin.turnOff() origin.turnOff()

View File

@ -20,13 +20,15 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@Callback(direct = true) @Callback(direct = true)
def getLabel(context: Context, args: Arguments): Array[AnyRef] = label match { def getLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
case value: Label => result(label.getLabel) label match {
case _ => null case value: Label => result(label.getLabel)
case _ => null
}
} }
@Callback @Callback
def setLabel(context: Context, args: Arguments): Array[AnyRef] = { def setLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (label == null) throw new Exception("filesystem does not support labeling") if (label == null) throw new Exception("filesystem does not support labeling")
if (args.checkAny(0) == null) label.setLabel(null) if (args.checkAny(0) == null) label.setLabel(null)
else label.setLabel(args.checkString(0)) else label.setLabel(args.checkString(0))
@ -34,12 +36,12 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
@Callback(direct = true) @Callback(direct = true)
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = { def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isReadOnly) result(fileSystem.isReadOnly)
} }
@Callback(direct = true) @Callback(direct = true)
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = { def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val space = fileSystem.spaceTotal val space = fileSystem.spaceTotal
if (space < 0) if (space < 0)
Array("unlimited") Array("unlimited")
@ -48,32 +50,32 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
@Callback(direct = true) @Callback(direct = true)
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = { def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.spaceUsed) result(fileSystem.spaceUsed)
} }
@Callback(direct = true) @Callback(direct = true)
def exists(context: Context, args: Arguments): Array[AnyRef] = { def exists(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.exists(clean(args.checkString(0)))) result(fileSystem.exists(clean(args.checkString(0))))
} }
@Callback(direct = true) @Callback(direct = true)
def size(context: Context, args: Arguments): Array[AnyRef] = { def size(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.size(clean(args.checkString(0)))) result(fileSystem.size(clean(args.checkString(0))))
} }
@Callback(direct = true) @Callback(direct = true)
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = { def isDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isDirectory(clean(args.checkString(0)))) result(fileSystem.isDirectory(clean(args.checkString(0))))
} }
@Callback(direct = true) @Callback(direct = true)
def lastModified(context: Context, args: Arguments): Array[AnyRef] = { def lastModified(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.lastModified(clean(args.checkString(0)))) result(fileSystem.lastModified(clean(args.checkString(0))))
} }
@Callback @Callback
def list(context: Context, args: Arguments): Array[AnyRef] = { def list(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
Option(fileSystem.list(clean(args.checkString(0)))) match { Option(fileSystem.list(clean(args.checkString(0)))) match {
case Some(list) => Array(list) case Some(list) => Array(list)
case _ => null case _ => null
@ -81,26 +83,26 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
@Callback @Callback
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = { def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) || def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path))) (recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
result(recurse(clean(args.checkString(0)))) result(recurse(clean(args.checkString(0))))
} }
@Callback @Callback
def remove(context: Context, args: Arguments): Array[AnyRef] = { def remove(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) || def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent) fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
result(recurse(clean(args.checkString(0)))) result(recurse(clean(args.checkString(0))))
} }
@Callback @Callback
def rename(context: Context, args: Arguments): Array[AnyRef] = { def rename(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.rename(clean(args.checkString(0)), clean(args.checkString(1)))) result(fileSystem.rename(clean(args.checkString(0)), clean(args.checkString(1))))
} }
@Callback @Callback(direct = true)
def close(context: Context, args: Arguments): Array[AnyRef] = { def close(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0) val handle = args.checkInteger(0)
Option(fileSystem.getHandle(handle)) match { Option(fileSystem.getHandle(handle)) match {
case Some(file) => case Some(file) =>
@ -113,8 +115,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
null null
} }
@Callback @Callback(direct = true, limit = 4)
def open(context: Context, args: Arguments): Array[AnyRef] = { def open(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (owners.get(context.node.address).fold(false)(_.size >= Settings.get.maxHandles)) { if (owners.get(context.node.address).fold(false)(_.size >= Settings.get.maxHandles)) {
throw new IOException("too many open handles") throw new IOException("too many open handles")
} }
@ -127,8 +129,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
result(handle) result(handle)
} }
@Callback @Callback(direct = true, limit = 4)
def read(context: Context, args: Arguments): Array[AnyRef] = { def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0) val handle = args.checkInteger(0)
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1))) val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))
checkOwner(context.node.address, handle) checkOwner(context.node.address, handle)
@ -158,8 +160,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
} }
@Callback @Callback(direct = true, limit = 4)
def seek(context: Context, args: Arguments): Array[AnyRef] = { def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0) val handle = args.checkInteger(0)
val whence = args.checkString(1) val whence = args.checkString(1)
val offset = args.checkInteger(2) val offset = args.checkInteger(2)
@ -178,7 +180,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
@Callback @Callback
def write(context: Context, args: Arguments): Array[AnyRef] = { def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0) val handle = args.checkInteger(0)
val value = args.checkByteArray(1) val value = args.checkByteArray(1)
if (!node.tryChangeBuffer(-Settings.get.hddWriteCost * value.length)) { if (!node.tryChangeBuffer(-Settings.get.hddWriteCost * value.length)) {
@ -243,6 +245,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
}) })
label.load(nbt.getCompoundTag("label"))
fileSystem.load(nbt.getCompoundTag("fs")) fileSystem.load(nbt.getCompoundTag("fs"))
} }
@ -262,6 +265,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
} }
nbt.setTag("owners", ownersNbt) nbt.setTag("owners", ownersNbt)
nbt.setNewCompoundTag("label", label.save)
nbt.setNewCompoundTag("fs", fileSystem.save) nbt.setNewCompoundTag("fs", fileSystem.save)
} }

View File

@ -65,6 +65,15 @@ trait Architecture {
*/ */
def init(): Boolean def init(): Boolean
/**
* Called when the owning machine was connected to the component network.
*
* This can be useful for connecting custom file systems (read only memory)
* in case init() was called from the machine's load() method (where it was
* not yet connected to the network).
*/
def onConnect()
/** /**
* Called when a computer stopped. Used to clean up any handles, memory and * Called when a computer stopped. Used to clean up any handles, memory and
* so on. For example, for Lua this destroys the Lua state. * so on. For example, for Lua this destroys the Lua state.

View File

@ -0,0 +1,34 @@
package li.cil.oc.server.component.machine
import li.cil.oc.api.FileSystem
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{Settings, OpenComputers}
import net.minecraft.nbt.NBTTagCompound
abstract class LuaArchitecture(val machine: Machine) extends Architecture {
val rom = Option(FileSystem.asManagedEnvironment(FileSystem.
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/rom"), "rom"))
override def init() = {
if (machine.node.network != null) {
rom.foreach(fs => machine.node.connect(fs.node))
}
true
}
override def onConnect() {
rom.foreach(fs => machine.node.connect(fs.node))
}
override def close() {
rom.foreach(_.node.remove())
}
override def load(nbt: NBTTagCompound) {
rom.foreach(fs => fs.load(nbt.getCompoundTag("rom")))
}
override def save(nbt: NBTTagCompound) {
rom.foreach(fs => nbt.setNewCompoundTag("rom", fs.save))
}
}

View File

@ -12,7 +12,7 @@ import org.luaj.vm3.lib.jse.JsePlatform
import scala.Some import scala.Some
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
class LuaJLuaArchitecture(machine: Machine) extends Architecture { class LuaJLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
private var lua: Globals = _ private var lua: Globals = _
private var thread: LuaThread = _ private var thread: LuaThread = _
@ -132,6 +132,8 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def init() = { override def init() = {
super.init()
lua = JsePlatform.debugGlobals() lua = JsePlatform.debugGlobals()
lua.set("package", LuaValue.NIL) lua.set("package", LuaValue.NIL)
lua.set("io", LuaValue.NIL) lua.set("io", LuaValue.NIL)
@ -251,13 +253,13 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
computer.set("pushSignal", (args: Varargs) => LuaValue.valueOf(machine.signal(args.checkjstring(1), toSimpleJavaObjects(args, 2): _*))) computer.set("pushSignal", (args: Varargs) => LuaValue.valueOf(machine.signal(args.checkjstring(1), toSimpleJavaObjects(args, 2): _*)))
// And its ROM address. // And its ROM address.
computer.set("romAddress", (_: Varargs) => machine.rom.fold(LuaValue.NIL)(rom => Option(rom.node.address) match { computer.set("romAddress", (_: Varargs) => rom.fold(LuaValue.NIL)(fs => Option(fs.node.address) match {
case Some(address) => LuaValue.valueOf(address) case Some(address) => LuaValue.valueOf(address)
case _ => LuaValue.NIL case _ => LuaValue.NIL
})) }))
// And it's /tmp address... // And it's /tmp address...
computer.set("tmpAddress", (_: Varargs) => machine.tmp.fold(LuaValue.NIL)(tmp => Option(tmp.node.address) match { computer.set("tmpAddress", (_: Varargs) => machine.tmp.fold(LuaValue.NIL)(fs => Option(fs.node.address) match {
case Some(address) => LuaValue.valueOf(address) case Some(address) => LuaValue.valueOf(address)
case _ => LuaValue.NIL case _ => LuaValue.NIL
})) }))
@ -375,6 +377,8 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
} }
override def close() = { override def close() = {
super.close()
lua = null lua = null
thread = null thread = null
synchronizedCall = null synchronizedCall = null
@ -385,11 +389,11 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) { override def load(nbt: NBTTagCompound) {
super.load(nbt)
if (machine.isRunning) { if (machine.isRunning) {
machine.stop() machine.stop()
machine.start() machine.start()
} }
} }
override def save(nbt: NBTTagCompound) {}
} }

View File

@ -24,9 +24,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
withConnector(if (isRobot) Settings.get.bufferRobot + 30 * Settings.get.bufferPerLevel else Settings.get.bufferComputer). withConnector(if (isRobot) Settings.get.bufferRobot + 30 * Settings.get.bufferPerLevel else Settings.get.bufferComputer).
create() create()
val rom = Option(FileSystem.asManagedEnvironment(FileSystem.
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/rom"), "rom"))
val tmp = if (Settings.get.tmpSize > 0) { val tmp = if (Settings.get.tmpSize > 0) {
Option(FileSystem.asManagedEnvironment(FileSystem. Option(FileSystem.asManagedEnvironment(FileSystem.
fromMemory(Settings.get.tmpSize * 1024), "tmpfs")) fromMemory(Settings.get.tmpSize * 1024), "tmpfs"))
@ -208,7 +205,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
Option(node.network.node(address)) match { Option(node.network.node(address)) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node => case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
val direct = component.isDirect(method) val direct = component.isDirect(method)
if (direct) callCounts.synchronized { if (direct && architecture.isInitialized) callCounts.synchronized {
val limit = component.limit(method) val limit = component.limit(method)
val counts = callCounts.getOrElseUpdate(component.address, mutable.Map.empty[String, Int]) val counts = callCounts.getOrElseUpdate(component.address, mutable.Map.empty[String, Int])
val count = counts.getOrElseUpdate(method, 0) val count = counts.getOrElseUpdate(method, 0)
@ -424,8 +421,8 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
override def onConnect(node: Node) { override def onConnect(node: Node) {
if (node == this.node) { if (node == this.node) {
components += this.node.address -> this.node.name components += this.node.address -> this.node.name
rom.foreach(rom => node.connect(rom.node))
tmp.foreach(tmp => node.connect(tmp.node)) tmp.foreach(tmp => node.connect(tmp.node))
architecture.onConnect()
} }
else { else {
node match { node match {
@ -439,8 +436,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
override def onDisconnect(node: Node) { override def onDisconnect(node: Node) {
if (node == this.node) { if (node == this.node) {
stop() close()
rom.foreach(_.node.remove())
tmp.foreach(_.node.remove()) tmp.foreach(_.node.remove())
} }
else { else {
@ -522,7 +518,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
components ++= nbt.getTagList("components").iterator[NBTTagCompound].map(c => components ++= nbt.getTagList("components").iterator[NBTTagCompound].map(c =>
c.getString("address") -> c.getString("name")) c.getString("address") -> c.getString("name"))
rom.foreach(rom => rom.load(nbt.getCompoundTag("rom")))
tmp.foreach(tmp => tmp.load(nbt.getCompoundTag("tmp"))) tmp.foreach(tmp => tmp.load(nbt.getCompoundTag("tmp")))
if (state.size > 0 && state.top != Machine.State.Stopped && init()) { if (state.size > 0 && state.top != Machine.State.Stopped && init()) {
@ -587,7 +582,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
} }
nbt.setTag("components", componentsNbt) nbt.setTag("components", componentsNbt)
rom.foreach(rom => nbt.setNewCompoundTag("rom", rom.save))
tmp.foreach(tmp => nbt.setNewCompoundTag("tmp", tmp.save)) tmp.foreach(tmp => nbt.setNewCompoundTag("tmp", tmp.save))
if (state.top != Machine.State.Stopped) { if (state.top != Machine.State.Stopped) {
@ -637,7 +631,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
// Connect the ROM and `/tmp` node to our owner. We're not in a network in // Connect the ROM and `/tmp` node to our owner. We're not in a network in
// case we're loading, which is why we have to check it here. // case we're loading, which is why we have to check it here.
if (node.network != null) { if (node.network != null) {
rom.foreach(rom => node.connect(rom.node))
tmp.foreach(tmp => node.connect(tmp.node)) tmp.foreach(tmp => node.connect(tmp.node))
} }

View File

@ -12,7 +12,7 @@ import scala.Some
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
class NativeLuaArchitecture(val machine: Machine) extends Architecture { class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
private var lua: LuaState = null private var lua: LuaState = null
private var kernelMemory = 0 private var kernelMemory = 0
@ -179,6 +179,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def init(): Boolean = { override def init(): Boolean = {
super.init()
// Creates a new state with all base libraries and the persistence library // Creates a new state with all base libraries and the persistence library
// loaded into it. This means the state has much more power than it // loaded into it. This means the state has much more power than it
// rightfully should have, so we sandbox it a bit in the following. // rightfully should have, so we sandbox it a bit in the following.
@ -321,7 +323,7 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// And its ROM address. // And its ROM address.
lua.pushScalaFunction(lua => { lua.pushScalaFunction(lua => {
machine.rom.foreach(rom => Option(rom.node.address) match { rom.foreach(fs => Option(fs.node.address) match {
case None => lua.pushNil() case None => lua.pushNil()
case Some(address) => lua.pushString(address) case Some(address) => lua.pushString(address)
}) })
@ -331,7 +333,7 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// And it's /tmp address... // And it's /tmp address...
lua.pushScalaFunction(lua => { lua.pushScalaFunction(lua => {
machine.tmp.foreach(tmp => Option(tmp.node.address) match { machine.tmp.foreach(fs => Option(fs.node.address) match {
case None => lua.pushNil() case None => lua.pushNil()
case Some(address) => lua.pushString(address) case Some(address) => lua.pushString(address)
}) })
@ -563,6 +565,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
} }
override def close() { override def close() {
super.close()
if (lua != null) { if (lua != null) {
lua.setTotalMemory(Integer.MAX_VALUE) lua.setTotalMemory(Integer.MAX_VALUE)
lua.close() lua.close()
@ -574,6 +578,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) { override def load(nbt: NBTTagCompound) {
super.load(nbt)
// Unlimit memory use while unpersisting. // Unlimit memory use while unpersisting.
lua.setTotalMemory(Integer.MAX_VALUE) lua.setTotalMemory(Integer.MAX_VALUE)
@ -609,6 +615,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
} }
override def save(nbt: NBTTagCompound) { override def save(nbt: NBTTagCompound) {
super.save(nbt)
// Unlimit memory while persisting. // Unlimit memory while persisting.
lua.setTotalMemory(Integer.MAX_VALUE) lua.setTotalMemory(Integer.MAX_VALUE)

View File

@ -121,24 +121,22 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
val block = Block.blocksList(blockId) val block = Block.blocksList(blockId)
val canActivate = block != null && Settings.get.allowActivateBlocks val canActivate = block != null && Settings.get.allowActivateBlocks
val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z))) val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z)))
if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) { val result =
return ActivationType.BlockActivated if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ))
} ActivationType.BlockActivated
else if (tryPlaceBlockWhileHandlingFunnySpecialCases(stack, x, y, z, side, hitX, hitY, hitZ))
ActivationType.ItemPlaced
else if (tryUseItem(stack, duration))
ActivationType.ItemUsed
else
ActivationType.None
if (stack != null) { if (stack != null) {
val didPlace = tryPlaceBlockWhileHandlingFunnySpecialCases(stack, x, y, z, side, hitX, hitY, hitZ)
if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack) if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack)
if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null) if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null)
if (didPlace) {
return ActivationType.ItemPlaced
}
if (tryUseItem(stack, duration)) {
return ActivationType.ItemUsed
}
} }
ActivationType.None result
} }
def useEquippedItem(duration: Double) = { def useEquippedItem(duration: Double) = {
@ -324,14 +322,16 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
} }
private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = { private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = {
val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0 stack != null && stack.stackSize > 0 && {
setPosition(posX, posY - fakeEyeHeight, posZ) val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ) setPosition(posX, posY - fakeEyeHeight, posZ)
setPosition(posX, posY + fakeEyeHeight, posZ) val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
if (didPlace) { setPosition(posX, posY + fakeEyeHeight, posZ)
robot.addXp(Settings.get.robotActionXp) if (didPlace) {
robot.addXp(Settings.get.robotActionXp)
}
didPlace
} }
didPlace
} }
private def isSomeKindOfPiston(stack: ItemStack) = private def isSomeKindOfPiston(stack: ItemStack) =

View File

@ -72,17 +72,32 @@ object FileSystem extends Item {
override def setLabel(value: String) { override def setLabel(value: String) {
media.setLabel(stack, value) media.setLabel(stack, value)
} }
override def load(nbt: NBTTagCompound) {}
override def save(nbt: NBTTagCompound) {}
} }
private class ItemLabel(val stack: ItemStack) extends Label { private class ItemLabel(val stack: ItemStack) extends Label {
override def getLabel = var label: Option[String] = None
if (dataTag(stack).hasKey(Settings.namespace + "fs.label"))
dataTag(stack).getString(Settings.namespace + "fs.label") override def getLabel = label.orNull
else null
override def setLabel(value: String) { override def setLabel(value: String) {
dataTag(stack).setString(Settings.namespace + "fs.label", label = Option(if (value != null && value.length > 16) value.substring(0, 16) else value)
if (value.length > 16) value.substring(0, 16) else value) }
override def load(nbt: NBTTagCompound) {
if (dataTag(stack).hasKey(Settings.namespace + "fs.label")) {
label = Option(dataTag(stack).getString(Settings.namespace + "fs.label"))
}
}
override def save(nbt: NBTTagCompound) {
label match {
case Some(value) => dataTag(stack).setString(Settings.namespace + "fs.label", value)
case _ =>
}
} }
} }

View File

@ -8,6 +8,7 @@ import li.cil.oc.api.fs.Label
import li.cil.oc.server.component import li.cil.oc.server.component
import li.cil.oc.{Settings, api} import li.cil.oc.{Settings, api}
import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.DimensionManager
import net.minecraft.nbt.NBTTagCompound
object FileSystem extends api.detail.FileSystemAPI { object FileSystem extends api.detail.FileSystemAPI {
override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = { override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = {
@ -84,6 +85,10 @@ object FileSystem extends api.detail.FileSystemAPI {
def setLabel(value: String) = throw new IllegalArgumentException("label is read only") def setLabel(value: String) = throw new IllegalArgumentException("label is read only")
def getLabel = label def getLabel = label
override def load(nbt: NBTTagCompound) {}
override def save(nbt: NBTTagCompound) {}
} }
private class ReadOnlyFileSystem(protected val root: io.File) private class ReadOnlyFileSystem(protected val root: io.File)

View File

@ -1,12 +1,12 @@
local event = require("event") local event = require("event")
local fs = require("filesystem") local fs = require("filesystem")
local shell = require("shell") local process = require("process")
local proxy = ... local proxy = ...
-- Install symlinks if they don't already exist. -- Install symlinks if they don't already exist.
local links = {} local links = {}
local fsroot = fs.path(shell.running()) local fsroot = fs.path(process.running())
local function inject(path) local function inject(path)
for file in fs.list(fs.concat(fsroot, path)) do for file in fs.list(fs.concat(fsroot, path)) do
local source = fs.concat(fsroot, path, file) local source = fs.concat(fsroot, path, file)

View File

@ -6,7 +6,7 @@ local internet = require("internet")
local shell = require("shell") local shell = require("shell")
if not component.isAvailable("internet") then if not component.isAvailable("internet") then
io.write("This program requires an internet card to run.") io.stderr:write("This program requires an internet card to run.")
return return
end end
@ -16,7 +16,7 @@ local args, options = shell.parse(...)
local function get(pasteId, filename) local function get(pasteId, filename)
local f, reason = io.open(filename, "w") local f, reason = io.open(filename, "w")
if not f then if not f then
io.write("Failed opening file for writing: ", reason) io.stderr:write("Failed opening file for writing: ", reason)
return return
end end
@ -35,9 +35,10 @@ local function get(pasteId, filename)
f:close() f:close()
io.write("Saved data to ", filename, "\n") io.write("Saved data to ", filename, "\n")
else else
io.write("failed.\n")
f:close() f:close()
fs.remove(filename) fs.remove(filename)
io.write("HTTP request failed: ", response, "\n") io.stderr:write("HTTP request failed: ", response, "\n")
end end
end end
@ -61,7 +62,7 @@ function run(pasteId, ...)
local success, reason = shell.execute(tmpFile, nil, ...) local success, reason = shell.execute(tmpFile, nil, ...)
if not success then if not success then
io.write(reason) io.stderr:write(reason)
end end
fs.remove(tmpFile) fs.remove(tmpFile)
end end
@ -73,14 +74,14 @@ function put(path)
if configFile then if configFile then
local result, reason = pcall(configFile) local result, reason = pcall(configFile)
if not result then if not result then
io.write("Failed loading config: ", reason) io.stderr:write("Failed loading config: ", reason)
end end
end end
config.key = config.key or "fd92bd40a84c127eeb6804b146793c97" config.key = config.key or "fd92bd40a84c127eeb6804b146793c97"
local file, reason = io.open(path, "r") local file, reason = io.open(path, "r")
if not file then if not file then
io.write("Failed opening file for reading: ", reason) io.stderr:write("Failed opening file for reading: ", reason)
return return
end end
@ -112,7 +113,8 @@ function put(path)
io.write('Run "pastebin get ', pasteId, '" to download anywhere.') io.write('Run "pastebin get ', pasteId, '" to download anywhere.')
end end
else else
io.write("failed: ", response) io.write("failed.\n")
io.stderr:write(response)
end end
end end
@ -127,7 +129,7 @@ elseif command == "get" then
local path = shell.resolve(args[3]) local path = shell.resolve(args[3])
if fs.exists(path) then if fs.exists(path) then
if not options.f or not os.remove(path) then if not options.f or not os.remove(path) then
io.write("file already exists") io.stderr:write("file already exists")
return return
end end
end end

View File

@ -5,7 +5,7 @@ local shell = require("shell")
local text = require("text") local text = require("text")
if not component.isAvailable("internet") then if not component.isAvailable("internet") then
io.write("This program requires an internet card to run.") io.stderr:write("This program requires an internet card to run.")
return return
end end
@ -33,21 +33,21 @@ if not filename then
end end
filename = text.trim(filename) filename = text.trim(filename)
if filename == "" then if filename == "" then
io.write("could not infer filename, please specify one") io.stderr:write("could not infer filename, please specify one")
return return
end end
filename = shell.resolve(filename) filename = shell.resolve(filename)
if fs.exists(filename) then if fs.exists(filename) then
if not options.f or not os.remove(filename) then if not options.f or not os.remove(filename) then
io.write("file already exists") io.stderr:write("file already exists")
return return
end end
end end
local f, reason = io.open(filename, "wb") local f, reason = io.open(filename, "wb")
if not f then if not f then
io.write("Failed opening file for writing: ", reason) io.stderr:write("failed opening file for writing: ", reason)
return return
end end
@ -68,7 +68,10 @@ if result then
io.write("Saved data to ", filename, "\n") io.write("Saved data to ", filename, "\n")
end end
else else
if not options.q then
io.write("failed.\n")
end
f:close() f:close()
fs.remove(filename) fs.remove(filename)
io.write("HTTP request failed: ", response, "\n") io.stderr:write("HTTP request failed: ", response, "\n")
end end

View File

@ -1,12 +1,12 @@
local event = require("event") local event = require("event")
local fs = require("filesystem") local fs = require("filesystem")
local shell = require("shell") local process = require("process")
local proxy = ... local proxy = ...
-- Install symlinks if they don't already exist. -- Install symlinks if they don't already exist.
local links = {} local links = {}
local fsroot = fs.path(shell.running()) local fsroot = fs.path(process.running())
local function inject(path) local function inject(path)
for file in fs.list(fs.concat(fsroot, path)) do for file in fs.list(fs.concat(fsroot, path)) do
local source = fs.concat(fsroot, path, file) local source = fs.concat(fsroot, path, file)

View File

@ -5,7 +5,8 @@ local shell = require("shell")
local sides = require("sides") local sides = require("sides")
if not computer.isRobot() then if not computer.isRobot() then
error("can only run on robots", 0) io.stderr:write("can only run on robots")
return
end end
local args, options = shell.parse(...) local args, options = shell.parse(...)
@ -17,7 +18,8 @@ end
local size = tonumber(args[1]) local size = tonumber(args[1])
if not size then if not size then
error("invalid size", 0) io.stderr:write("invalid size")
return
end end
local r = component.computer local r = component.computer

View File

@ -50,12 +50,27 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local running = setmetatable({}, {__mode="k"})
local function findProcess(co)
co = co or coroutine.running()
for _, process in pairs(running) do
for _, instance in pairs(process.instances) do
if instance == co then
return process
end
end
end
end
-------------------------------------------------------------------------------
--[[ This is the global environment we make available to userland programs. ]] --[[ This is the global environment we make available to userland programs. ]]
-- You'll notice that we do a lot of wrapping of native functions and adding -- You'll notice that we do a lot of wrapping of native functions and adding
-- parameter checks in those wrappers. This is to avoid errors from the host -- parameter checks in those wrappers. This is to avoid errors from the host
-- side that would push error objects - which are userdata and cannot be -- side that would push error objects - which are userdata and cannot be
-- persisted. -- persisted.
local sandbox local sandbox, libprocess
sandbox = { sandbox = {
assert = assert, assert = assert,
dofile = nil, -- in boot/*_base.lua dofile = nil, -- in boot/*_base.lua
@ -70,10 +85,7 @@ sandbox = {
if not allowBytecode() then if not allowBytecode() then
mode = "t" mode = "t"
end end
pcall(function() env = env or select(2, libprocess.running())
local _, penv = sandbox.require("shell").running()
env = env or penv
end)
return load(ld, source, mode, env or sandbox) return load(ld, source, mode, env or sandbox)
end, end,
loadfile = nil, -- in boot/*_base.lua loadfile = nil, -- in boot/*_base.lua
@ -96,9 +108,7 @@ sandbox = {
coroutine = { coroutine = {
create = function(f) create = function(f)
local co = coroutine.create(f) local co = coroutine.create(f)
pcall(function() table.insert(findProcess().instances, co)
sandbox.require("shell").register(co)
end)
return co return co
end, end,
resume = function(co, ...) -- custom resume part for bubbling sysyields resume = function(co, ...) -- custom resume part for bubbling sysyields
@ -257,7 +267,7 @@ sandbox = {
sandbox._G = sandbox sandbox._G = sandbox
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Start of non-standard stuff made available via package.preload. -- Start of non-standard stuff.
local libcomponent local libcomponent
libcomponent = { libcomponent = {
@ -359,6 +369,51 @@ local libcomputer = {
end end
} }
libprocess = {
load = function(path, env, init, name)
checkArg(1, path, "string")
checkArg(2, env, "table", "nil")
checkArg(3, init, "function", "nil")
checkArg(4, name, "string", "nil")
local process = findProcess()
if process then
env = env or process.env
end
env = setmetatable({}, {__index=env or sandbox})
local code, reason = sandbox.loadfile(path, "t", env)
if not code then
return nil, reason
end
local thread = coroutine.create(function(...)
if init then
init()
end
return code(...)
end)
running[thread] = {
path = path,
command = name,
env = env,
parent = process,
instances = setmetatable({thread}, {__mode="v"})
}
return thread
end,
running = function(level)
level = level or 1
local process = findProcess()
while level > 1 and process do
process = process.parent
level = level - 1
end
if process then
return process.path, process.env, process.command
end
end
}
local libunicode = { local libunicode = {
char = function(...) char = function(...)
local args = table.pack(...) local args = table.pack(...)
@ -396,107 +451,103 @@ local libunicode = {
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local function main() local function bootstrap()
local args -- Minimalistic hard-coded pure async proxy for our ROM.
local function bootstrap() local rom = {}
-- Minimalistic hard-coded pure async proxy for our ROM. function rom.invoke(method, ...)
local rom = {} return invoke(true, computer.romAddress(), method, ...)
function rom.invoke(method, ...)
return invoke(true, computer.romAddress(), method, ...)
end
function rom.open(file) return rom.invoke("open", file) end
function rom.read(handle) return rom.invoke("read", handle, math.huge) end
function rom.close(handle) return rom.invoke("close", handle) end
function rom.inits(file) return ipairs(rom.invoke("list", "boot")) end
function rom.isDirectory(path) return rom.invoke("isDirectory", path) end
-- Custom low-level loadfile/dofile implementation reading from our ROM.
local function loadfile(file)
local handle, reason = rom.open(file)
if not handle then
error(reason)
end
if handle then
local buffer = ""
repeat
local data, reason = rom.read(handle)
if not data and reason then
error(reason)
end
buffer = buffer .. (data or "")
until not data
rom.close(handle)
return load(buffer, "=" .. file, "t", sandbox)
end
end
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
-- Make all calls in the bootstrapper direct to speed up booting. This is
-- safe because all invokes are rom fs related - or should be, anyway.
local realInvoke = invoke
invoke = function(_, ...) return realInvoke(true, ...) end
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local buffer = loadfile("/lib/buffer.lua")
local fs = loadfile("/lib/filesystem.lua")
local io = loadfile("/lib/io.lua")
local package = dofile("/lib/package.lua")
-- Initialize the package module with some of our own APIs.
package.preload["buffer"] = buffer
package.preload["component"] = function() return libcomponent end
package.preload["computer"] = function() return libcomputer end
package.preload["filesystem"] = fs
package.preload["io"] = io
package.preload["unicode"] = function() return libunicode end
-- Inject the package and io modules into the global namespace, as in Lua.
sandbox.package = package
sandbox.io = sandbox.require("io")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
sandbox.require("filesystem").mount(computer.romAddress(), "/")
if computer.tmpAddress() then
sandbox.require("filesystem").mount(computer.tmpAddress(), "/tmp")
end
-- Run library startup scripts. These mostly initialize event handlers.
local scripts = {}
for _, file in rom.inits() do
local path = "boot/" .. file
if not rom.isDirectory(path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
-- Step out of the fast lane, all the basic stuff should now be loaded.
invoke = realInvoke
-- Yield once to get a memory baseline.
coroutine.yield()
return coroutine.create(function() dofile("/init.lua") end)
end end
local co, args = bootstrap(), {n=0} function rom.open(file) return rom.invoke("open", file) end
function rom.read(handle) return rom.invoke("read", handle, math.huge) end
function rom.close(handle) return rom.invoke("close", handle) end
function rom.inits(file) return ipairs(rom.invoke("list", "boot")) end
function rom.isDirectory(path) return rom.invoke("isDirectory", path) end
-- Custom low-level loadfile/dofile implementation reading from our ROM.
local function loadfile(file)
local handle, reason = rom.open(file)
if not handle then
error(reason)
end
local buffer = ""
repeat
local data, reason = rom.read(handle)
if not data and reason then
error(reason)
end
buffer = buffer .. (data or "")
until not data
rom.close(handle)
return load(buffer, "=" .. file, "t", sandbox)
end
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local package = dofile("/lib/package.lua")
-- Initialize the package module with some of our own APIs.
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["component"] = function() return libcomponent end
package.preload["computer"] = function() return libcomputer end
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
package.preload["io"] = loadfile("/lib/io.lua")
package.preload["process"] = function() return libprocess end
package.preload["unicode"] = function() return libunicode end
-- Inject the package and io modules into the global namespace, as in Lua.
sandbox.package = package
sandbox.io = sandbox.require("io")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
sandbox.require("filesystem").mount(computer.romAddress(), "/")
if computer.tmpAddress() then
sandbox.require("filesystem").mount(computer.tmpAddress(), "/tmp")
end
-- Run library startup scripts. These mostly initialize event handlers.
local scripts = {}
for _, file in rom.inits() do
local path = "boot/" .. file
if not rom.isDirectory(path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
return coroutine.create(function() dofile("/init.lua") end), {n=0}
end
local function main()
-- Make all calls in the bootstrapper direct to speed up booting.
local realInvoke = invoke
invoke = function(_, ...) return realInvoke(true, ...) end
local co, args = bootstrap()
-- Step out of the fast lane, all the basic stuff should now be loaded.
invoke = realInvoke
-- Yield once to get a memory baseline.
coroutine.yield()
while true do while true do
deadline = computer.realTime() + timeout -- timeout global is set by host deadline = computer.realTime() + timeout -- timeout global is set by host
debug.sethook(co, checkDeadline, "", hookInterval) debug.sethook(co, checkDeadline, "", hookInterval)

View File

@ -11,7 +11,7 @@ elseif #args == 1 then
if value then if value then
io.write(value) io.write(value)
else else
io.write("no such alias") io.stderr:write("no such alias")
end end
else else
shell.setAlias(args[1], args[2]) shell.setAlias(args[1], args[2])

View File

@ -12,7 +12,7 @@ else
for i = 1, #args do for i = 1, #args do
local file, reason = io.open(shell.resolve(args[i])) local file, reason = io.open(shell.resolve(args[i]))
if not file then if not file then
io.write(reason) io.stderr:write(reason)
return return
end end
repeat repeat

View File

@ -6,6 +6,6 @@ if #args == 0 then
else else
local result, reason = shell.setWorkingDirectory(shell.resolve(args[1])) local result, reason = shell.setWorkingDirectory(shell.resolve(args[1]))
if not result then if not result then
io.write(reason) io.stderr:write(reason)
end end
end end

View File

@ -14,9 +14,10 @@ if fs.isDirectory(to) then
to = to .. "/" .. fs.name(from) to = to .. "/" .. fs.name(from)
end end
if fs.exists(to) and not options.f then if fs.exists(to) and not options.f then
error("target file exists") io.stderr:write("target file exists")
return
end end
local result, reason = fs.copy(from, to) local result, reason = fs.copy(from, to)
if not result then if not result then
io.write(reason) io.stderr:write(reason)
end end

View File

@ -12,7 +12,7 @@ else
for i = 1, #args do for i = 1, #args do
local proxy, path = fs.get(args[i]) local proxy, path = fs.get(args[i])
if not proxy then if not proxy then
io.write(args[i], ": no such file or directory\n") io.stderr:write(args[i], ": no such file or directory\n")
else else
mounts[path] = proxy mounts[path] = proxy
end end

View File

@ -22,7 +22,7 @@ local filename = shell.resolve(args[1])
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly() local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
if fs.isDirectory(filename) or readonly and not fs.exists(filename) then if fs.isDirectory(filename) or readonly and not fs.exists(filename) then
io.write("file not found") io.stderr:write("file not found")
return return
end end

View File

@ -15,15 +15,15 @@ else
proxy, reaons = fs.get(args[1]) proxy, reaons = fs.get(args[1])
end end
if not proxy then if not proxy then
io.write(reason) io.stderr:write(reason)
return return
end end
if #args < 2 then if #args < 2 then
io.write(proxy.getLabel() or "no label") io.stderr:write(proxy.getLabel() or "no label")
else else
local result, reason = proxy.setLabel(args[2]) local result, reason = proxy.setLabel(args[2])
if not result then if not result then
io.write(reason or "could not set label") io.stderr:write(reason or "could not set label")
end end
end end

View File

@ -18,5 +18,5 @@ end
local result, reason = fs.link(target, linkpath) local result, reason = fs.link(target, linkpath)
if not result then if not result then
io.write(reason) io.stderr:write(reason)
end end

View File

@ -46,7 +46,7 @@ while term.isAvailable() do
if type(result[2]) == "table" and result[2].reason == "terminated" then if type(result[2]) == "table" and result[2].reason == "terminated" then
os.exit(result[2].code) os.exit(result[2].code)
end end
io.write(tostring(result[2]), "\n") io.stderr:write(tostring(result[2]), "\n")
else else
for i = 2, result.n do for i = 2, result.n do
io.write(text.serialize(result[i], true), "\t") io.write(text.serialize(result[i], true), "\t")
@ -56,6 +56,6 @@ while term.isAvailable() do
end end
end end
else else
io.write(reason, "\n") io.stderr:write(reason, "\n")
end end
end end

View File

@ -16,4 +16,4 @@ for path in string.gmatch(os.getenv("MANPATH"), "[^:]+") do
os.exit() os.exit()
end end
end end
io.write("No manual entry for " .. topic) io.stderr:write("No manual entry for " .. topic)

View File

@ -18,6 +18,6 @@ for i = 1, #args do
reason = "unknown reason" reason = "unknown reason"
end end
end end
io.write(path, ": ", reason, "\n") io.stderr:write(path, ": ", reason, "\n")
end end
end end

View File

@ -12,7 +12,7 @@ end
local file, reason = io.open(shell.resolve(args[1])) local file, reason = io.open(shell.resolve(args[1]))
if not file then if not file then
io.write(reason) io.stderr:write(reason)
return return
end end

View File

@ -18,11 +18,11 @@ end
local proxy, reason = fs.proxy(args[1]) local proxy, reason = fs.proxy(args[1])
if not proxy then if not proxy then
io.write(reason) io.stderr:write(reason)
return return
end end
local result, reason = fs.mount(proxy, shell.resolve(args[2])) local result, reason = fs.mount(proxy, shell.resolve(args[2]))
if not result then if not result then
io.write(reason) io.stderr:write(reason)
end end

View File

@ -15,11 +15,11 @@ if fs.isDirectory(to) then
end end
if fs.exists(to) then if fs.exists(to) then
if not options.f then if not options.f then
io.write("target file exists") io.stderr:write("target file exists")
end end
fs.remove(to) fs.remove(to)
end end
local result, reason = os.rename(from, to) local result, reason = os.rename(from, to)
if not result then if not result then
io.write(reason or "unknown error") io.stderr:write(reason or "unknown error")
end end

View File

@ -13,7 +13,7 @@ local componentType = args[1]
if #args > 1 then if #args > 1 then
local address = args[2] local address = args[2]
if not component.get(address) then if not component.get(address) then
io.write("no component with this address") io.stderr:write("no component with this address")
return return
else else
component.setPrimary(componentType, address) component.setPrimary(componentType, address)
@ -23,5 +23,5 @@ end
if component.isAvailable(componentType) then if component.isAvailable(componentType) then
io.write(component.getPrimary(componentType).address) io.write(component.getPrimary(componentType).address)
else else
io.write("no primary component for this type") io.stderr:write("no primary component for this type")
end end

View File

@ -3,6 +3,10 @@ local component = require("component")
local shell = require("shell") local shell = require("shell")
local sides = require("sides") local sides = require("sides")
if not component.isAvailable("redstone") then
io.stderr:write("This program requires a redstone card or redstone I/O block.")
return
end
local rs = component.redstone local rs = component.redstone
local args, options = shell.parse(...) local args, options = shell.parse(...)
@ -17,7 +21,8 @@ end
local side = sides[args[1]] local side = sides[args[1]]
if not side then if not side then
error("Invalid side.") io.stderr:write("invalid side")
return
end end
if type(side) == "string" then if type(side) == "string" then
side = sides[side] side = sides[side]
@ -25,11 +30,13 @@ end
if options.b then if options.b then
if not rs.setBundledOutput then if not rs.setBundledOutput then
error("Bundled redstone not available.") io.stderr:write("bundled redstone not available")
return
end end
local color = colors[args[2]] local color = colors[args[2]]
if not color then if not color then
error("Invalid color.") io.stderr:write("invalid color")
return
end end
if type(color) == "string" then if type(color) == "string" then
color = colors[color] color = colors[color]

View File

@ -17,12 +17,13 @@ end
local w = tonumber(args[1]) local w = tonumber(args[1])
local h = tonumber(args[2]) local h = tonumber(args[2])
if not w or not h then if not w or not h then
io.write("invalid width or height") io.stderr:write("invalid width or height")
return return
end end
local result, reason = component.gpu.setResolution(w, h) local result, reason = component.gpu.setResolution(w, h)
if not result then if not result then
io.write(reason) io.stderr:write(reason)
return
end end
term.clear() term.clear()

View File

@ -9,6 +9,6 @@ end
for i = 1, #args do for i = 1, #args do
local path = shell.resolve(args[i]) local path = shell.resolve(args[i])
if not os.remove(path) then if not os.remove(path) then
io.write(path, ": no such file, or permission denied\n") io.stderr:write(path, ": no such file, or permission denied\n")
end end
end end

View File

@ -2,7 +2,7 @@ local args = {...}
if #args < 1 then if #args < 1 then
for k,v in pairs(os.getenv()) do for k,v in pairs(os.getenv()) do
io.write(k..'='..v..'\n') io.write(k, "='", string.gsub(v, "'", [['"'"']]), "'\n")
end end
else else
local count = 0 local count = 0

View File

@ -1,6 +1,7 @@
local component = require("component") local component = require("component")
local computer = require("computer") local computer = require("computer")
local event = require("event") local event = require("event")
local process = require("process")
local shell = require("shell") local shell = require("shell")
local term = require("term") local term = require("term")
local text = require("text") local text = require("text")
@ -8,8 +9,8 @@ local text = require("text")
local args, options = shell.parse(...) local args, options = shell.parse(...)
local history = {} local history = {}
if options.v or not shell.running(2) then if options.v or not process.running(2) then
print(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)") io.write(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)\n")
end end
while true do while true do
@ -19,7 +20,7 @@ while true do
end end
term.clear() term.clear()
if options.v then if options.v then
print(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)") io.write(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)\n")
end end
end end
while term.isAvailable() do while term.isAvailable() do
@ -28,7 +29,7 @@ while true do
component.gpu.setForeground(foreground) component.gpu.setForeground(foreground)
local command = term.read(history) local command = term.read(history)
if not command then if not command then
print("exit") io.write("exit\n")
return -- eof return -- eof
end end
while #history > 10 do while #history > 10 do
@ -40,10 +41,10 @@ while true do
elseif command ~= "" then elseif command ~= "" then
local result, reason = os.execute(command) local result, reason = os.execute(command)
if not result then if not result then
print(reason) io.stderr:write(reason .. "\n")
elseif term.getCursor() > 1 then elseif term.getCursor() > 1 then
print() io.write("\n")
end end
end end
end end
end end

View File

@ -21,16 +21,16 @@ else
if proxy then if proxy then
proxy = reason -- = path proxy = reason -- = path
if proxy ~= path then if proxy ~= path then
io.write("not a mount point") io.stderr:write("not a mount point")
return return
end end
end end
end end
if not proxy then if not proxy then
io.write(reason) io.stderr:write(reason)
return return
end end
if not fs.umount(proxy) then if not fs.umount(proxy) then
io.write("nothing to unmount here") io.stderr:write("nothing to unmount here")
end end

View File

@ -8,7 +8,7 @@ end
local result = shell.getAlias(args[1]) local result = shell.getAlias(args[1])
if not result then if not result then
io.write("no such alias") io.stderr:write("no such alias")
else else
shell.setAlias(args[1], nil) shell.setAlias(args[1], nil)
io.write("alias removed: ", args[1], " -> ", result) io.write("alias removed: ", args[1], " -> ", result)

View File

@ -1,9 +1,9 @@
local args = {...} local args = {...}
if #args < 1 then if #args < 1 then
print "You have to specify which arguments to unset!" print("Usage: unset <varname>[ <varname2> [...]]")
else else
for _, k in ipairs(args) do for _, k in ipairs(args) do
os.setenv(k, nil) os.setenv(k, nil)
end end
end end

View File

@ -9,5 +9,5 @@ end
local result, reason = computer.addUser(args[1]) local result, reason = computer.addUser(args[1])
if not result then if not result then
io.write(reason) io.stderr:write(reason)
end end

View File

@ -8,5 +8,5 @@ if #args < 1 then
end end
if not computer.removeUser(args[1]) then if not computer.removeUser(args[1]) then
io.write("no such user") io.stderr:write("no such user")
end end

View File

@ -16,6 +16,6 @@ for i = 1, #args do
if result then if result then
io.write(result, "\n") io.write(result, "\n")
else else
io.write(args[i], ": ", reason, "\n") io.stderr:write(args[i], ": ", reason, "\n")
end end
end end

View File

@ -3,6 +3,7 @@ local term = require("term")
local stdinStream = {handle="stdin"} local stdinStream = {handle="stdin"}
local stdoutStream = {handle="stdout"} local stdoutStream = {handle="stdout"}
local stderrStream = {handle="stderr"}
local stdinHistory = {} local stdinHistory = {}
local function badFileDescriptor() local function badFileDescriptor()
@ -13,6 +14,7 @@ function stdinStream:close()
return nil, "cannot close standard file" return nil, "cannot close standard file"
end end
stdoutStream.close = stdinStream.close stdoutStream.close = stdinStream.close
stderrStream.close = stdinStream.close
function stdinStream:read(n) function stdinStream:read(n)
local result = term.read(stdinHistory) local result = term.read(stdinHistory)
@ -27,16 +29,32 @@ function stdoutStream:write(str)
return self return self
end end
function stderrStream:write(str)
local component = require("component")
if component.isAvailable("gpu") and component.gpu.getDepth() > 1 then
local foreground = component.gpu.getForeground()
component.gpu.setForeground(0xFF0000)
term.write(str, true)
component.gpu.setForeground(foreground)
else
term.write(str, true)
end
return self
end
stdinStream.seek = badFileDescriptor stdinStream.seek = badFileDescriptor
stdinStream.write = badFileDescriptor stdinStream.write = badFileDescriptor
stdoutStream.read = badFileDescriptor stdoutStream.read = badFileDescriptor
stdoutStream.seek = badFileDescriptor stdoutStream.seek = badFileDescriptor
stderrStream.read = badFileDescriptor
stderrStream.seek = badFileDescriptor
io.stdin = buffer.new("r", stdinStream) io.stdin = buffer.new("r", stdinStream)
io.stdout = buffer.new("w", stdoutStream) io.stdout = buffer.new("w", stdoutStream)
io.stderr = io.stdout io.stderr = buffer.new("w", stderrStream)
io.stdout:setvbuf("no") io.stdout:setvbuf("no")
io.stderr:setvbuf("no")
io.input(io.stdin) io.input(io.stdin)
io.output(io.stdout) io.output(io.stdout)

View File

@ -3,7 +3,7 @@ local unicode = require("unicode")
local filesystem, fileStream = {}, {} local filesystem, fileStream = {}, {}
local isAutorunEnabled = true local isAutorunEnabled = true
local mtab = {children={}, links={}} local mtab = {name="", children={}, links={}}
local function segments(path) local function segments(path)
path = path:gsub("\\", "/") path = path:gsub("\\", "/")
@ -258,7 +258,7 @@ function filesystem.exists(path)
if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link
return true return true
end end
if node.fs then if node and node.fs then
return node.fs.exists(rest) return node.fs.exists(rest)
end end
return false return false
@ -472,18 +472,9 @@ function filesystem.open(path, mode)
local stream = {fs = node.fs, handle = handle} local stream = {fs = node.fs, handle = handle}
-- stream:close does a syscall, which yields, and that's not possible in
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
local function cleanup(self) local function cleanup(self)
if not self.handle then return end if not self.handle then return end
-- save non-gc'ed values as upvalues pcall(self.fs.close, self.handle)
local fs = self.fs
local handle = self.handle
local function close()
fs.close(handle)
end
-- Required locally because this is a bootstrapped file.
require("event").timer(0, close)
end end
local metatable = {__index = fileStream, local metatable = {__index = fileStream,
__gc = cleanup, __gc = cleanup,

View File

@ -8,7 +8,6 @@ local loaded = {
["_G"] = _G, ["_G"] = _G,
["bit32"] = bit32, ["bit32"] = bit32,
["coroutine"] = coroutine, ["coroutine"] = coroutine,
["io"] = io,
["math"] = math, ["math"] = math,
["os"] = os, ["os"] = os,
["package"] = package, ["package"] = package,
@ -29,13 +28,14 @@ function package.searchpath(name, path, sep, rep)
rep = rep or '/' rep = rep or '/'
sep, rep = '%' .. sep, rep sep, rep = '%' .. sep, rep
name = string.gsub(name, sep, rep) name = string.gsub(name, sep, rep)
local fs = require("filesystem")
local errorFiles = {} local errorFiles = {}
for subPath in string.gmatch(path, "([^;]+)") do for subPath in string.gmatch(path, "([^;]+)") do
subPath = string.gsub(subPath, "?", name) subPath = string.gsub(subPath, "?", name)
if loaded.shell then if subPath:sub(1, 1) ~= "/" and os.getenv then
subPath = require("shell").resolve(subPath) subPath = fs.concat(os.getenv("PWD") or "/", subPath)
end end
if require("filesystem").exists(subPath) then if fs.exists(subPath) then
local file = io.open(subPath, "r") local file = io.open(subPath, "r")
if file then if file then
file:close() file:close()
@ -80,8 +80,11 @@ function require(module)
loading[module] = true loading[module] = true
local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"} local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"}
for i = 1, #package.searchers do for i = 1, #package.searchers do
local f, extra = package.searchers[i](module) -- the pcall is mostly for out of memory errors
if f and type(f) ~= "string" then local ok, f, extra = pcall(package.searchers[i], module)
if not ok then
table.insert(errorMsg, "\t" .. f)
elseif f and type(f) ~= "string" then
loader = f loader = f
value = extra value = extra
break break

View File

@ -5,23 +5,13 @@ local text = require("text")
local shell = {} local shell = {}
local aliases = {} local aliases = {}
local running = setmetatable({}, {__mode="k"})
local isLoading = false
local function findProcess(co)
co = co or coroutine.running()
for _, process in pairs(running) do
for _, instance in pairs(process.instances) do
if instance == co then
return process
end
end
end
end
local function findFile(name, ext) local function findFile(name, ext)
checkArg(1, name, "string") checkArg(1, name, "string")
local function findIn(dir) local function findIn(dir)
if dir:sub(1, 1) ~= "/" then
dir = shell.resolve(dir)
end
dir = fs.concat(fs.concat(dir, name), "..") dir = fs.concat(fs.concat(dir, name), "..")
name = fs.name(name) name = fs.name(name)
local list = fs.list(dir) local list = fs.list(dir)
@ -475,52 +465,11 @@ function shell.execute(command, env, ...)
end end
function shell.load(path, env, init, name) function shell.load(path, env, init, name)
checkArg(1, path, "string") local path, reason = shell.resolve(path, "lua")
checkArg(2, env, "table", "nil") if not path then
checkArg(3, init, "function", "nil")
checkArg(4, name, "string", "nil")
local filename, reason = shell.resolve(path, "lua")
if not filename then
return nil, reason return nil, reason
end end
return require("process").load(path, env, init, name)
local process = findProcess()
if process then
env = env or process.env
end
env = setmetatable({}, {__index=env or _ENV})
local code, reason = loadfile(filename, "t", env)
if not code then
return nil, reason
end
isLoading = true
local thread = coroutine.create(function(...)
if init then
init()
end
return code(...)
end)
isLoading = false
running[thread] = {
path = filename,
command = name,
env = env,
parent = process,
instances = setmetatable({thread}, {__mode="v"})
}
return thread
end
function shell.register(thread) -- called from coroutine.create
checkArg(1, thread, "thread")
if findProcess(thread) then
return false -- already attached somewhere
end
if not isLoading then
table.insert(findProcess().instances, thread)
end
return true
end end
function shell.parse(...) function shell.parse(...)
@ -540,16 +489,8 @@ function shell.parse(...)
return args, options return args, options
end end
function shell.running(level) function shell.running(level) -- deprecated
level = level or 1 return require("process").running(level)
local process = findProcess()
while level > 1 and process do
process = process.parent
level = level - 1
end
if process then
return process.path, process.env, process.command
end
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

@ -588,7 +588,7 @@ opencomputers {
# allocations regardless of the amount of RAM installed in the computer it # allocations regardless of the amount of RAM installed in the computer it
# runs on. As a side effect this pretty much determines the read # runs on. As a side effect this pretty much determines the read
# performance of file systems. # performance of file systems.
maxReadBuffer: 8192 maxReadBuffer: 2048
} }
# Internet settings, security related. # Internet settings, security related.