diff --git a/assets/items.psd b/assets/items.psd
index 084bc15bfe..75f167bfa4 100644
Binary files a/assets/items.psd and b/assets/items.psd differ
diff --git a/build.properties b/build.properties
index bfdc33c8c6..0384425f6e 100644
--- a/build.properties
+++ b/build.properties
@@ -1,7 +1,7 @@
minecraft.version=1.7.10
forge.version=10.13.4.1448-1.7.10
-oc.version=1.6.1
+oc.version=1.6.2
oc.subversion=
ae2.version=rv2-beta-26
diff --git a/libs/OpenComputers-JNLua.jar b/libs/OpenComputers-JNLua.jar
index f3abfe1f4e..f06a8f722a 100644
Binary files a/libs/OpenComputers-JNLua.jar and b/libs/OpenComputers-JNLua.jar differ
diff --git a/libs/OpenComputers-LuaJ.jar b/libs/OpenComputers-LuaJ.jar
index 70c315e47d..1d2cc019b4 100644
Binary files a/libs/OpenComputers-LuaJ.jar and b/libs/OpenComputers-LuaJ.jar differ
diff --git a/src/main/java/li/cil/oc/api/Items.java b/src/main/java/li/cil/oc/api/Items.java
index 7a0d5657ed..9018aba67b 100644
--- a/src/main/java/li/cil/oc/api/Items.java
+++ b/src/main/java/li/cil/oc/api/Items.java
@@ -58,6 +58,8 @@ public static ItemInfo get(ItemStack stack) {
*
* To use some directory in your mod JAR as the directory provided by the
* loot disk, use {@link FileSystem#fromClass} in your callable.
+ *
+ * Call this in the init phase or later, not in pre-init.
*
* @param name the label and identifier to use for the loot disk.
* @param color the color of the disk, as a Minecraft color (so 0-15,
@@ -65,13 +67,47 @@ public static ItemInfo get(ItemStack stack) {
* @param factory the callable to call for creating file system instances.
* @return an item stack representing the registered loot disk, to allow
* adding a recipe for your loot disk, for example.
+ * @deprecated use {@link #registerFloppy(String, int, Callable, boolean)} instead.
*/
+ @Deprecated
public static ItemStack registerFloppy(String name, int color, Callable factory) {
if (API.items != null)
return API.items.registerFloppy(name, color, factory);
return null;
}
+ /**
+ * Register a single loot floppy disk.
+ *
+ * The disk will be listed in the creative tab of OpenComputers.
+ *
+ * The specified factory callable will be used to generate a new file
+ * system when the loot disk is used as a component. The specified name
+ * will be used as the label for the loot disk, as well as the identifier
+ * to select the corresponding factory method, so choose wisely.
+ *
+ * To use some directory in your mod JAR as the directory provided by the
+ * loot disk, use {@link FileSystem#fromClass} in your callable.
+ *
+ * If doRecipeCycling is true , the floppy disk will be
+ * included in the floppy disk recipe cycle if that is enabled.
+ *
+ * Call this in the init phase or later, not in pre-init.
+ *
+ * @param name the label and identifier to use for the loot disk.
+ * @param color the color of the disk, as a Minecraft color (so 0-15,
+ * with 0 being black, 1 red and so on).
+ * @param factory the callable to call for creating file system instances.
+ * @param doRecipeCycling whether to include this floppy disk in floppy disk cycling.
+ * @return an item stack representing the registered loot disk, to allow
+ * adding a recipe for your loot disk, for example.
+ */
+ public static ItemStack registerFloppy(String name, int color, Callable factory, boolean doRecipeCycling) {
+ if (API.items != null)
+ return API.items.registerFloppy(name, color, factory, doRecipeCycling);
+ return null;
+ }
+
/**
* Register a single custom EEPROM.
*
diff --git a/src/main/java/li/cil/oc/api/detail/ItemAPI.java b/src/main/java/li/cil/oc/api/detail/ItemAPI.java
index 0de2b2f3d8..e1917a3062 100644
--- a/src/main/java/li/cil/oc/api/detail/ItemAPI.java
+++ b/src/main/java/li/cil/oc/api/detail/ItemAPI.java
@@ -51,9 +51,39 @@ public interface ItemAPI {
* @param factory the callable to call for creating file system instances.
* @return an item stack representing the registered loot disk, to allow
* adding a recipe for your loot disk, for example.
+ * @deprecated use {@link #registerFloppy(String, int, Callable, boolean)} instead.
*/
+ @Deprecated
ItemStack registerFloppy(String name, int color, Callable factory);
+ /**
+ * Register a single loot floppy disk.
+ *
+ * The disk will be listed in the creative tab of OpenComputers.
+ *
+ * The specified factory callable will be used to generate a new file
+ * system when the loot disk is used as a component. The specified name
+ * will be used as the label for the loot disk, as well as the identifier
+ * to select the corresponding factory method, so choose wisely.
+ *
+ * To use some directory in your mod JAR as the directory provided by the
+ * loot disk, use {@link FileSystem#fromClass} in your callable.
+ *
+ * If doRecipeCycling is true , the floppy disk will be
+ * included in the floppy disk recipe cycle if that is enabled.
+ *
+ * Call this in the init phase or later, not in pre-init.
+ *
+ * @param name the label and identifier to use for the loot disk.
+ * @param color the color of the disk, as a Minecraft color (so 0-15,
+ * with 0 being black, 1 red and so on).
+ * @param factory the callable to call for creating file system instances.
+ * @param doRecipeCycling whether to include this floppy disk in floppy disk cycling.
+ * @return an item stack representing the registered loot disk, to allow
+ * adding a recipe for your loot disk, for example.
+ */
+ ItemStack registerFloppy(String name, int color, Callable factory, boolean doRecipeCycling);
+
/**
* Register a single custom EEPROM.
*
diff --git a/src/main/java/li/cil/oc/api/network/Network.java b/src/main/java/li/cil/oc/api/network/Network.java
index 3d76f0014f..e1d6fa8931 100644
--- a/src/main/java/li/cil/oc/api/network/Network.java
+++ b/src/main/java/li/cil/oc/api/network/Network.java
@@ -103,7 +103,7 @@ public interface Network {
Iterable nodes();
/**
- * The list of addressed nodes in the network visible to the specified node.
+ * The list of addressed nodes in the network reachable by the specified node.
*
* This does not include nodes with a visibility of None
* or a visibility of Neighbors when there is no direct connection
diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf
index bd8ef92c1b..9c834010e2 100644
--- a/src/main/resources/application.conf
+++ b/src/main/resources/application.conf
@@ -807,6 +807,10 @@ opencomputers {
# Energy consumed when reconfiguring nanomachines.
nanomachinesReconfigure: 5000
+
+ # Energy consumed by a MFU per tick while connected.
+ # Similarly to `wirelessCostPerRange`, this is multiplied with the distance to the bound block.
+ mfuRelay: 1
}
# The rate at which different blocks accept external power. All of these
@@ -1345,6 +1349,9 @@ opencomputers {
# The maximum range between the drone/robot and a villager for a trade to
# be performed by the trading upgrade
tradingRange: 8.0
+
+ # Radius the MFU is able to operate in
+ mfuRange: 3
}
# Settings for mod integration (the mod previously known as OpenComponents).
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/adapter.md b/src/main/resources/assets/opencomputers/doc/en_US/block/adapter.md
index 8df3e7188d..4e6bb63710 100644
--- a/src/main/resources/assets/opencomputers/doc/en_US/block/adapter.md
+++ b/src/main/resources/assets/opencomputers/doc/en_US/block/adapter.md
@@ -5,3 +5,4 @@
The adapter allows [computers](../general/computer.md) to interact with blocks from vanilla Minecraft or other mods. Supported blocks adjacent to the adapter will show up as components in [computers](../general/computer.md) connected to the adapter.
In addition to this, the adapter provides a slot for a few select upgrades. For example, the [inventory controller upgrade](../item/inventoryControllerUpgrade.md) allows computers to query more information from an inventory adjacent to the adapter, similar to when the upgrade is installed in a device (such as a [robot](robot.md) or [drone](../item/drone.md)), and a [tank controller upgrade](../item/tankControllerUpgrade.md) provides similar functionality for fluid tanks next to the adapter.
+Furthermore, you can insert a bound [MFU](../item/mfu.md) to interact with blocks a few spaces away.
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/debugCard.md b/src/main/resources/assets/opencomputers/doc/en_US/item/debugCard.md
index 624803cac7..aa35df5a79 100644
--- a/src/main/resources/assets/opencomputers/doc/en_US/item/debugCard.md
+++ b/src/main/resources/assets/opencomputers/doc/en_US/item/debugCard.md
@@ -5,3 +5,5 @@
The debug card is a creative-only item that was originally only intended to make debugging things easier, by automating some processes. It has since gotten a bunch more functionality, making it quite useful for custom map-making.
Note that you can use sneak-activate while holding the card to bind it to you or unbind it, meaning `runCommand` will be performed using your permission levels instead of the default OpenComputers ones.
+
+A debug card can receive messages similar to a [linked card](linkedCard.md), firing a `debug_message` event. You can send such a message using either another debug card's `sendDebugMessage` or the Minecraft command `/oc_sendDebugMessage` (or `/oc_sdbg`).
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/index.md b/src/main/resources/assets/opencomputers/doc/en_US/item/index.md
index 8fc6a20292..f0eb3a2981 100644
--- a/src/main/resources/assets/opencomputers/doc/en_US/item/index.md
+++ b/src/main/resources/assets/opencomputers/doc/en_US/item/index.md
@@ -43,6 +43,7 @@ Keep in mind that some of these may not be available, depending on the recipe se
* [Inventory Controller](inventoryControllerUpgrade.md)
* [Inventory Upgrade](inventoryUpgrade.md)
* [Leash Upgrade](leashUpgrade.md)
+* [MFU](mfu.md)
* [Navigation Upgrade](navigationUpgrade.md)
* [Piston Upgrade](pistonUpgrade.md)
* [Sign Upgrade](signUpgrade.md)
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/inventoryControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/en_US/item/inventoryControllerUpgrade.md
index c5f8edbae7..8cad6000e1 100644
--- a/src/main/resources/assets/opencomputers/doc/en_US/item/inventoryControllerUpgrade.md
+++ b/src/main/resources/assets/opencomputers/doc/en_US/item/inventoryControllerUpgrade.md
@@ -5,3 +5,5 @@
The inventory controller upgrade provides extended inventory interaction to [robots](../block/robot.md) and [drones](drone.md). It allows the device to explicitly target slots in external inventories when dropping or sucking items. It also allows devices to read detailed information about item stacks. Lastly it provides [robots](../block/robot.md) with a means to change their equipped tool without external help.
This upgrade can also be placed in [adapters](../block/adapter.md), where it provides similar inspection methods for inventories adjacent to the [adapter](../block/adapter.md) as it does to the [robot](../block/robot.md). It does not allow the [adapter](../block/adapter.md) to move items into or out of inventories, however. This feature is only available to [robots](../block/robot.md) and [drones](drone.md).
+
+See also: [Transposers](../block/transposer.md)
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/mfu.md b/src/main/resources/assets/opencomputers/doc/en_US/item/mfu.md
new file mode 100644
index 0000000000..e288f6653b
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/doc/en_US/item/mfu.md
@@ -0,0 +1,7 @@
+# MFU
+
+![You will never know the true meaning of this acronym.](oredict:oc:mfu)
+
+This upgrade acts as a remote [adapter](../block/adapter.md). Click while sneaking onto any side of any block to bind it to a specific position. Then, place it into an adapter nearby (the range is very limited) and it will act as if the adapter was placed right next to the specific side you bound it to!
+
+Keep in mind that keeping the remote adapter connection active uses energy.
diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/tankControllerUpgrade.md b/src/main/resources/assets/opencomputers/doc/en_US/item/tankControllerUpgrade.md
index 9f041919ed..57bbda9b72 100644
--- a/src/main/resources/assets/opencomputers/doc/en_US/item/tankControllerUpgrade.md
+++ b/src/main/resources/assets/opencomputers/doc/en_US/item/tankControllerUpgrade.md
@@ -5,3 +5,5 @@
The tank controller upgrade is to fluid tanks what the [inventory controller upgrade](inventoryControllerUpgrade.md) is to normal inventories. It allows devices to query more detailed information about tanks inside and next to them.
This upgrade can also be installed in [adapters](../block/adapter.md), allowing [computers](../general/computer.md) connected to the [adapter](../block/adapter.md) to query information about the tanks adjacent to the [adapter](../block/adapter.md).
+
+See also: [Transposers](../block/transposer.md)
diff --git a/src/main/resources/assets/opencomputers/lang/de_DE.lang b/src/main/resources/assets/opencomputers/lang/de_DE.lang
index 03d61ba851..f3405864c2 100644
--- a/src/main/resources/assets/opencomputers/lang/de_DE.lang
+++ b/src/main/resources/assets/opencomputers/lang/de_DE.lang
@@ -23,12 +23,14 @@ tile.oc.hologram2.name=Hologrammprojektor (Stufe 2)
tile.oc.keyboard.name=Tastatur
tile.oc.microcontroller.name=Mikrocontroller
tile.oc.motionSensor.name=Bewegungsmelder
+tile.oc.netSplitter.name=Net Splitter
tile.oc.powerConverter.name=Leistungswandler
tile.oc.powerDistributor.name=Stromverteiler
tile.oc.print.name=3D-Druck
tile.oc.printer.name=3D-Drucker
tile.oc.raid.name=Raid
tile.oc.redstone.name=Redstone-I/O
+tile.oc.relay.name=Relais
tile.oc.robot.name=Roboter
tile.oc.robotAfterimage.name=Roboter
tile.oc.screen1.name=Bildschirm (Stufe 1)
@@ -36,7 +38,7 @@ tile.oc.screen2.name=Bildschirm (Stufe 2)
tile.oc.screen3.name=Bildschirm (Stufe 3)
tile.oc.rack.name=Serverschrank
tile.oc.switch.name=Switch
-tile.oc.netSplitter.name=Net Splitter
+tile.oc.transposer.name=Transposer
tile.oc.waypoint.name=Wegpunkt
# Items
@@ -65,7 +67,9 @@ item.oc.DataCard1.name=Datenkarte (Stufe 2)
item.oc.DataCard2.name=Datenkarte (Stufe 3)
item.oc.DebugCard.name=Debug-Karte
item.oc.Debugger.name=Netzwerk-Debugger
+item.oc.DiamondChip.name=Diamantsplitter
item.oc.Disk.name=Platte
+item.oc.DiskDriveMountable.name=Diskettenlaufwerk
item.oc.Drone.name=Drohne
item.oc.DroneCase0.name=Drohnengehäuse (Stufe 1)
item.oc.DroneCase1.name=Drohnengehäuse (Stufe 2)
@@ -98,6 +102,7 @@ item.oc.Microchip2.name=Microchip (Stufe 3)
item.oc.MicrocontrollerCase0.name=Mikrocontroller-Gehäuse (Stufe 1)
item.oc.MicrocontrollerCase1.name=Mikrocontroller-Gehäuse (Stufe 2)
item.oc.MicrocontrollerCase3.name=Mikrocontroller-Gehäuse (Kreativ)
+item.oc.Nanomachines.name=Nanomaschinen
item.oc.NetworkCard.name=Netzwerkkarte
item.oc.NumPad.name=Ziffernblock
item.oc.Present.name=Ein kleines Etwas...
@@ -114,6 +119,7 @@ item.oc.TabletCase0.name=Tablet-Gehäuse (Stufe 1)
item.oc.TabletCase1.name=Tablet-Gehäuse (Stufe 2)
item.oc.TabletCase3.name=Tablet-Gehäuse (Kreativ)
item.oc.Terminal.name=Fernbedienung
+item.oc.TerminalServer.name=Terminalserver
item.oc.TexturePicker.name=Textur-Leser
item.oc.Transistor.name=Transistor
item.oc.UpgradeAngel.name=Schwebe-Upgrade
@@ -138,6 +144,7 @@ item.oc.UpgradeHover1.name=Schwebe-Upgrade (Stufe 2)
item.oc.UpgradeInventory.name=Inventar-Upgrade
item.oc.UpgradeInventoryController.name=Inventarbedienungs-Upgrade
item.oc.UpgradeLeash.name=Leinen-Upgrade
+item.oc.UpgradeMF.name=MFU
item.oc.UpgradeNavigation.name=Navigations-Upgrade
item.oc.UpgradePiston.name=Kolben-Upgrade
item.oc.UpgradeSign.name=Schild-I/O-Upgrade
@@ -235,6 +242,7 @@ oc:container.Disassembler=Recycler
oc:container.DiskDrive=Diskettenlaufwerk
oc:container.Printer=Drucker
oc:container.Raid=Raid
+oc:container.Relay=Relais
oc:container.Server=Server
oc:container.Rack=Serverschrank
oc:container.Switch=Switch
@@ -271,10 +279,12 @@ oc:tooltip.DataCard1=Stellt einige komplexe Algorithmen wie Hash-Funktionen und
oc:tooltip.DataCard2=Stellt einige komplexe Algorithmen wie Hash-Funktionen und deflate/inflate bereit.
oc:tooltip.DebugCard=Kreativ-Modus-Gegenstand, erlaubt es die Welt zu manipulieren um das Testen zu erleichtern. Verwendung auf eigene Gefahr.
oc:tooltip.Debugger=Erlaubt, Informationen über OCs internes Netzwerk auszugeben. Nur verwenden, wenn von einem Entwickler dazu aufgefordert.
+oc:tooltip.DiamondChip=Ein kleines Stück eines einst wunderschönen Diamanten. Er wird niemals wieder so sein, wie er war.
oc:tooltip.Disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!
oc:tooltip.Disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Disketten und Festplatten zu fertigen.
oc:tooltip.DiskDrive.CC=ComputerCraft-Disketten werden §aauch unterstützt§7.
oc:tooltip.DiskDrive=Erlaubt es, Disketten zu lesen und zu beschreiben. Kann in Robotern installiert werden, um später Disketten einlegen zu können.
+oc:tooltip.DiskDriveMountable=Funktioniert genauso wie ein normales Diskettenlaufwerk, wird aber in einem Serverschrank installiert.
oc:tooltip.DiskUsage=Festplattennutzung: %s/%s Byte
oc:tooltip.DiskModeManaged=Modus: Managed
oc:tooltip.DiskModeUnmanaged=Modus: Unmanaged
@@ -303,6 +313,7 @@ oc:tooltip.Microchip=Tritt auch unter dem Alias Integrierter Schaltkreis auf. Ke
oc:tooltip.Microcontroller=Mikrocontroller sind einfachst-mögliche Computer. Sie sind dazu gedacht, für Spezialfälle verwendet zu werden, bei denen sie einfach das Programm auf dem in ihnen eingebauten EEPROM ausführen.
oc:tooltip.MicrocontrollerCase=Dieses Gehäuse wird verwendet, um Mikrocontroller in der Elektronik-Werkbank zu bauen. Platziere es in eine solche und füge weitere Komponenten hinzu, um einen Mikrocontroller zu erstellen.
oc:tooltip.MotionSensor=Kann Bewegungen sich in der Nähe befindender Lebewesen erkennen. Benötigt eine klare Sichtlinie.
+oc:tooltip.Nanomachines=Kontrolleinheit und eine Menge Nanomaschinen zur Einnahme, sofern du dich traust.
oc:tooltip.NetworkCard=Erlaubt es Computern, die über mehrere Blöcke miteinander verbunden sind (z.B. Kabel), mittels Netzwerknachrichten zu kommunizieren.
oc:tooltip.PowerAcceptor=Energiewandelgeschwindigkeit: §f%s/t§7
oc:tooltip.PowerConverter.BuildCraft=§fBuildCraft MJ§7: §a%s:%s§7
@@ -328,6 +339,7 @@ oc:tooltip.RedstoneCard.RedNet=§fRedNet§7 wird §aunterstützt§7.
oc:tooltip.RedstoneCard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 wird §aunterstützt§7.
oc:tooltip.RedstoneCard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 wird §aunterstützt§7.
oc:tooltip.RedstoneCard=Erlaubt das Lesen und Ausgeben von Redstonesignalen um den Computer oder Roboter herum.
+oc:tooltip.Relay=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten "hinter" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.b. mittels Netzwerkkarten.
oc:tooltip.Robot=Im Gegensatz zu normalen Computern können sich Roboter in der Welt bewegen und ähnlich wie Spieler mit der Welt interagieren. Sie können jedoch §onicht§r§7 mit externen Komponenten interagieren!
# The underscore makes sure this isn't hidden with the rest of the tooltip.
oc:tooltip.Robot_Level=§fStufe§7: §a%s§7.
@@ -340,11 +352,13 @@ oc:tooltip.Switch=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet
oc:tooltip.Tablet=Ein Tablet-PC, für Lua unterwegs. Kann durch Sneak-Aktivierung zwangsgestoppt werden.
oc:tooltip.TabletCase=Einfaches Gehäuse für Tablet-PCs. Kann in der Elektronik-Werkbank mit Komponenten bestückt werden, um einen Tablet-PC zu fertigen.
oc:tooltip.Terminal=Erlaubt es, einen Server aus der Ferne zu steuern, so lange man sich in Reichweite des Servers befindet. Verhält sich wie Bildschirm und Tastatur in einem. Kann mit Shift-Rechtsklick an einen Server in einem Serverschrank gebunden werden.
+oc:tooltip.TerminalServer=Das Rückgrat für Fernzugriff. Fernbedienungen können hiermit verbunden werden. Enthält virtuelle Tastatur und Bildschirm.
oc:tooltip.TexturePicker=Dieses Werkzeug erlaubt es, eine Zeichenkette anzuzeigen, die die Oberfläche eines Blocks beschreibt und in Form-Definitionen für 3D-Drucker verwendet werden kann. Definiv keine Texturnamen, oh nein. Nix da.
oc:tooltip.Tier=§8Stufe %s
oc:tooltip.NetSplitter=Kann Netzwerke dynamisch verbinden. Die Konnektivität jeder Seite kann umgeschaltet werden, in dem man "Professionelle Wartungsarbeiten" mit einem Schraubenschlüssel durchführt. Sie kann auch mit einem Redstone-Signal an entsprechenden Seiten invertiert werden.
oc:tooltip.TooLong=Halte [§f%s§7] gedrückt für mehr Infos.
oc:tooltip.Transistor=Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren.
+oc:tooltip.Transposer=Ermöglicht automatischen Transfer von Items und Flüssigkeiten zwischen angrenzenden Blöcken.
oc:tooltip.UpgradeAngel=Erlaubt es Robotern, Blöcke in die Luft zu setzen, selbst wenn kein Referenzblock daneben existiert.
oc:tooltip.UpgradeBattery=Erhöht die Energiespeicherkapazität eines Roboters, was ihm erlaubt, länger zu arbeiten, ohne erneut aufgeladen werden zu müssen.[nl] Kapazität: §f%s§7
oc:tooltip.UpgradeChunkloader=Wenn sich im Wald ein Roboter bewegt, und niemand da ist, ihn zu sehen, bewegt er sich dann wirklich? Dieses Upgrade stellt sicher, dass dem so ist. Es hält den Chunk, in dem sich der Roboter befindet, geladen, verbraucht jedoch Energie, während es aktiv ist.
@@ -357,6 +371,9 @@ oc:tooltip.UpgradeGenerator=Kann verwendet werden, um unterwegs Energie aus Bren
oc:tooltip.UpgradeHover=Erlaubt Robotern höher zu fliegen, ohne an Wänden klettern zu müssen.[nl] Maximalhöhe: §f%s§7
oc:tooltip.UpgradeInventory=Dieses Upgrade gibt Robotern ein internes Inventar. Ohne ein solches Upgrade können Roboter keine Gegenstände verwahren.
oc:tooltip.UpgradeInventoryController=Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Inventaren zu interagieren, und erlaubt es ihm, das angelegte Werkzeug mit einem Gegenstand in seinem Inventar auszutauschen.
+oc:tooltip.UpgradeMF=Erlaubt es Adaptern, mit Blöcken zu interagieren, die etwas weiter weg sind.
+oc:tooltip.UpgradeMF.Linked=§fVerbindung hergestellt§7
+oc:tooltip.UpgradeMF.Unlinked=§fKeine Verbindung§7
oc:tooltip.UpgradeLeash=Erlaubt es manchen Geräten, wie etwa Drohnen, B- *getuschel* Verzeihung. Mir wurde soeben zugetragen, dass es tatsächlich dazu verwendet werden kann, Tiere an die Leine zu legen. Sogar viele Viecher.
oc:tooltip.UpgradeNavigation=Erlaubt es Robotern, ihre Position und Ausrichtung zu bestimmen. Die Position ist relativ zur Mitte der Karte, die in diesem Upgrade verbaut wurde.
oc:tooltip.UpgradePiston=Dieses Upgrade erlaubt es zu drängeln. Es macht es möglich Blöcke zu verschieben, ähnlich dem Kolben. Es kann jedoch §lkeine§7 Entities bewegen.
@@ -444,6 +461,15 @@ achievement.oc.transistor.desc=Jetzt kannst du dir den Soundtrack anhören.
achievement.oc.wirelessNetworkCard=Das WLAN - Unendliche Weiten
achievement.oc.wirelessNetworkCard.desc=Netzwerke, die nie ein Paket zuvor gesehen hat.
+# Death messages. Note that the number of entries here must match the number
+# set in the actual damage source in code.
+death.attack.oc.nanomachinesOverload.1=%s ist zu gierig geworden.
+death.attack.oc.nanomachinesOverload.2=%s erlitt einen Nervenzusammenbruch.
+death.attack.oc.nanomachinesOverload.3=Die Nanomaschinen von %s gerieten wohl außer Kontrolle. Ups.
+death.attack.oc.nanomachinesHungry.1=%s wurde von Nanomaschinen verspeist.
+death.attack.oc.nanomachinesHungry.2=%s hat wohl seine Nanomaschinen nicht gefüttert.
+death.attack.oc.nanomachinesHungry.3=%s wurde verdaut.
+
# NEI Integration
nei.options.inventory.oredict=OreDictionary-Namen anzeigen
nei.options.inventory.oredict.true=True
diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang
index 9fa64316ed..e938d2ce9e 100644
--- a/src/main/resources/assets/opencomputers/lang/en_US.lang
+++ b/src/main/resources/assets/opencomputers/lang/en_US.lang
@@ -144,6 +144,7 @@ item.oc.UpgradeHover1.name=Hover Upgrade (Tier 2)
item.oc.UpgradeInventory.name=Inventory Upgrade
item.oc.UpgradeInventoryController.name=Inventory Controller Upgrade
item.oc.UpgradeLeash.name=Leash Upgrade
+item.oc.UpgradeMF.name=MFU
item.oc.UpgradeNavigation.name=Navigation Upgrade
item.oc.UpgradePiston.name=Piston Upgrade
item.oc.UpgradeSign.name=Sign I/O Upgrade
@@ -370,6 +371,9 @@ oc:tooltip.UpgradeGenerator=Can be used to generate energy from fuel on the go.
oc:tooltip.UpgradeHover=This upgrade allows robots to fly higher above the ground without having to climb walls.[nl] Maximum height: §f%s§7
oc:tooltip.UpgradeInventory=This upgrade provides inventory space to a robot or drone. Without one of these, they will not be able to store items internally.
oc:tooltip.UpgradeInventoryController=This upgrade allows robots and drones more control in how it interacts with external inventories, and allows robots to swap their equipped tool with an item in their inventory.
+oc:tooltip.UpgradeMF=Allows adapters to access blocks they are not adjacent to.
+oc:tooltip.UpgradeMF.Linked=§fConnection established§7
+oc:tooltip.UpgradeMF.Unlinked=§fNo connection§7
oc:tooltip.UpgradeLeash=Allows some devices, such as drones, to bind Isaa- excuse me... *chatter* My apologies. I'm just being told this is actually used to put animals on a leash. Multiple animals, even. Odd.
oc:tooltip.UpgradeNavigation=Can be used to determine the position and orientation of a device. The position is relative to the center of the map that was used to craft this upgrade.
oc:tooltip.UpgradePiston=This upgrade is very pushy. It allows moving blocks, similar to when using a piston. It does §lnot§7 move entities, however.
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/cd.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/cd.lua
index e5666c120c..44236919a8 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/cd.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/cd.lua
@@ -41,7 +41,7 @@ path = resolved
local oldpwd = shell.getWorkingDirectory()
local result, reason = shell.setWorkingDirectory(path)
if not result then
- io.stderr:write("cd: ",reason)
+ io.stderr:write("cd: ", path, ": ", reason)
return 1
else
os.setenv("OLDPWD", oldpwd)
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua
index 6d5aaad175..a69e4c1a61 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua
@@ -1,9 +1,9 @@
-local fs = require("filesystem")
local shell = require("shell")
-local computer = require("computer")
+local transfer = require("tools/transfer")
local args, options = shell.parse(...)
-if #args < 2 then
+options.h = options.h or options.help
+if #args < 2 or options.h then
io.write("Usage: cp [-inrv] \n")
io.write(" -i: prompt before overwrite (overrides -n option).\n")
io.write(" -n: do not overwrite an existing file.\n")
@@ -13,161 +13,20 @@ if #args < 2 then
io.write(" -P: preserve attributes, e.g. symbolic links.\n")
io.write(" -v: verbose output.\n")
io.write(" -x: stay on original source file system.\n")
- return 1
+ return not not options.h
end
-local exit_code = nil
-options.P = options.P or options.r
-
-local function status(from, to)
- if options.v then
- io.write(from .. " -> " .. to .. "\n")
- end
- os.sleep(0) -- allow interrupting
-end
-
-local result, reason
-
-local function prompt(message)
- io.write(message .. " [Y/n] ")
- local result = io.read()
- if not result then -- closed pipe
- os.exit(1)
- end
- return result and (result == "" or result:sub(1, 1):lower() == "y")
-end
-
-local function areEqual(path1, path2)
- local f1 = io.open(path1, "rb")
- if not f1 then
- return nil, "could not open `" .. path1 .. "' for update test"
- end
- local f2 = io.open(path2, "rb")
- if not f2 then
- f1:close()
- return nil, "could not open `" .. path2 .. "' for update test"
- end
- local result = true
- local chunkSize = 4 * 1024
- repeat
- local s1, s2 = f1:read(chunkSize), f2:read(chunkSize)
- if s1 ~= s2 then
- result = false
- break
- end
- until not s1 or not s2
- f1:close()
- f2:close()
- return result
-end
-
-local mounts = {}
-for dev,path in fs.mounts() do
- mounts[fs.canonical(path)] = dev
-end
-
-local function recurse(fromPath, toPath, origin)
- local isLink, target = fs.isLink(fromPath)
- local toIsLink, toLinkTarget = fs.isLink(toPath)
- local same_path = fs.canonical(isLink and target or fromPath) == fs.canonical(toIsLink and toLinkTarget or toPath)
- local same_link = isLink and toIsLink and same_path
- local toExists = fs.exists(toPath)
-
- if isLink and options.P and not (toExists and same_path and not toIsLink) then
- if toExists and options.n then
- return true
- end
- fs.remove(toPath)
- if toExists and options.v then
- io.write(string.format("removed '%s'\n", toPath))
- end
- status(fromPath, toPath)
- return fs.link(target, toPath)
- elseif fs.isDirectory(fromPath) then
- if not options.r then
- io.write("omitting directory `" .. fromPath .. "'\n")
- exit_code = 1
- return true
- end
- if fs.exists(toPath) and not fs.isDirectory(toPath) then
- -- my real cp always does this, even with -f, -n or -i.
- return nil, "cannot overwrite non-directory `" .. toPath .. "' with directory `" .. fromPath .. "'"
- end
- if options.x and origin and mounts[fs.canonical(fromPath)] then
- return true
- end
- if fs.get(fromPath) == fs.get(toPath) and (fs.canonical(toPath).."/"):find(fs.canonical(fromPath).."/",1,true) then
- return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'"
- end
- if not fs.exists(toPath) then
- status(fromPath, toPath)
- fs.makeDirectory(toPath)
- end
- for file in fs.list(fromPath) do
- local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file), origin or fs.get(fromPath))
- if not result then
- return nil, reason
- end
- end
- return true
- elseif fs.exists(fromPath) then
- if toExists then
- if same_path then
- return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file"
- end
-
- if options.n then
- return true
- end
-
- -- if target is link, we are updating the target
- if toIsLink then
- toPath = toLinkTarget
- end
-
- if options.u and not fs.isDirectory(toPath) and areEqual(fromPath, toPath) then
- return true
- end
-
- if options.i then
- if not prompt("overwrite `" .. toPath .. "'?") then
- return true
- end
- end
-
- if fs.isDirectory(toPath) then
- return nil, "cannot overwrite directory `" .. toPath .. "' with non-directory"
- end
-
- fs.remove(toPath)
- end
- status(fromPath, toPath)
- return fs.copy(fromPath, toPath)
- else
- return nil, "`" .. fromPath .. "': No such file or directory"
- end
-end
-
-local to = shell.resolve(args[#args])
-
-for i = 1, #args - 1 do
- local arg = args[i]
- local fromPath = shell.resolve(arg)
- -- a "contents of" copy is where src path ends in . or ..
- -- a source path ending with . is not sufficient - could be the source filename
- local contents_of = arg:match("%.$") and not fromPath:match("%.$")
- local toPath = to
- -- we do not append fromPath name to toPath in case of contents_of copy
- if not contents_of and fs.isDirectory(toPath) then
- toPath = fs.concat(toPath, fs.name(fromPath))
- end
- result, reason = recurse(fromPath, toPath)
- if not result then
- if reason then
- io.stderr:write(reason..'\n')
- end
- return 1
- end
-end
-
-return exit_code
+-- clean options for copy (as opposed to move)
+options =
+{
+ cmd = "cp",
+ i = options.i,
+ n = options.n,
+ r = options.r,
+ u = options.u,
+ P = options.P,
+ v = options.v,
+ x = options.x,
+}
+
+return transfer.batch(args, options)
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/df.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/df.lua
index 41e1d73daa..d7b6184cb9 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/df.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/df.lua
@@ -23,7 +23,9 @@ end
local mounts = {}
if #args == 0 then
for proxy, path in fs.mounts() do
- mounts[path] = proxy
+ if not mounts[proxy] or mounts[proxy]:len() > path:len() then
+ mounts[proxy] = path
+ end
end
else
for i = 1, #args do
@@ -31,13 +33,13 @@ else
if not proxy then
io.stderr:write(args[i], ": no such file or directory\n")
else
- mounts[path] = proxy
+ mounts[proxy] = path
end
end
end
local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}}
-for path, proxy in pairs(mounts) do
+for proxy, path in pairs(mounts) do
local label = proxy.getLabel() or proxy.address
local used, total = proxy.spaceUsed(), proxy.spaceTotal()
local available, percent
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua
index 9a5becea9a..a5f9cbe3df 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua
@@ -20,14 +20,12 @@ local filename = shell.resolve(args[1])
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
-if not fs.exists(filename) then
- if fs.isDirectory(filename) then
- io.stderr:write("file is a directory\n")
- return 1
- elseif readonly then
- io.stderr:write("file system is read only\n")
- return 1
- end
+if fs.isDirectory(filename) then
+ io.stderr:write("file is a directory\n")
+ return 1
+elseif not fs.exists(filename) and readonly then
+ io.stderr:write("file system is read only\n")
+ return 1
end
local function loadConfig()
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/grep.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/grep.lua
index 7ec81a96e0..3c66033c11 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/grep.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/grep.lua
@@ -195,7 +195,7 @@ local function readLines()
meta.label = file
local file, reason = resolve(file)
if fs.exists(file) then
- curHand = io.open(file, 'r')
+ curHand, reason = io.open(file, 'r')
if not curHand then
local msg = string.format("failed to read from %s: %s", meta.label, reason)
stderr:write("grep: ",msg,"\n")
@@ -265,7 +265,7 @@ local function test(m,p)
if max_matches == 0 then os.exit(1) end
any_hit_ec = 0
m.hits, hit_value = m.hits + hit_value, 0
- if max_matches == m.hits or f_only or no_only then
+ if f_only or no_only then
m.close = true
end
if flush or quiet then return end
@@ -294,6 +294,9 @@ local function test(m,p)
elseif p:find("^^") and not plain then p="^$" end
end
if not empty_line then write("\n") end
+ if max_matches ~= math.huge and max_matches >= m.hits then
+ m.close = true
+ end
end
for meta,status in readLines() do
if not meta then
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/head.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/head.lua
index ea731f89ba..99dfa2166b 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/head.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/head.lua
@@ -2,22 +2,25 @@ local shell = require("shell")
local fs = require("filesystem")
local args, options = shell.parse(...)
+local error_code = 0
local function pop(key, convert)
local result = options[key]
options[key] = nil
if result and convert then
- local c = convert(result)
+ local c = tonumber(result)
if not c then
- error('invalid ' .. key .. ': could not convert ' .. result)
+ io.stderr:write(string.format("use --%s=n where n is a number\n", key))
+ options.help = true
+ error_code = 1
end
result = c
end
return result
end
-local bytes = pop('bytes', tonumber)
-local lines = pop('lines', tonumber)
+local bytes = pop('bytes', true)
+local lines = pop('lines', true)
local quiet = {pop('q'), pop('quiet'), pop('silent')}
quiet = quiet[1] or quiet[2] or quiet[3]
local verbose = {pop('v'), pop('verbose')}
@@ -33,13 +36,14 @@ if help or next(options) then
local invalid_key = next(options)
if invalid_key then
invalid_key = string.format('invalid option: %s\n', invalid_key)
+ error_code = 1
else
invalid_key = ''
end
print(invalid_key .. [[Usage: head [--lines=n] file
Print the first 10 lines of each FILE to stdout.
For more info run: man head]])
- os.exit()
+ os.exit(error_code)
end
if #args == 0 then
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua
index 60ab9cb53c..1b9176d519 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua
@@ -13,9 +13,10 @@ do
end
if not options then return end
+local write = io.write
if computer.freeMemory() < 50000 then
- print("Low memory, collecting garbage")
+ write("Low memory, collecting garbage\n")
for i=1,20 do os.sleep(0) end
end
@@ -26,7 +27,6 @@ if ec ~= nil and ec ~= 0 then
return ec
end
-local write = io.write
write("Installation complete!\n")
if options.setlabel then
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/label.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/label.lua
index 7ecf77c023..25cf958f2b 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/label.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/label.lua
@@ -1,26 +1,43 @@
-local fs = require("filesystem")
local shell = require("shell")
+local devfs = require("devfs")
+local comp = require("component")
local args, options = shell.parse(...)
if #args < 1 then
- io.write("Usage: label [-a] []\n")
- io.write(" -a File system is specified via label or address instead of by path.\n")
+ io.write("Usage: label [-a] []\n")
+ io.write(" -a Device is specified via label or address instead of by path.\n")
return 1
end
+local filter = args[1]
+local label = args[2]
+
local proxy, reason
+
if options.a then
- proxy, reason = fs.proxy(args[1])
+ for addr in comp.list() do
+ if addr:sub(1, filter:len()) == filter then
+ proxy, reason = comp.proxy(addr)
+ break
+ end
+ local tmp_proxy = comp.proxy(addr)
+ local tmp_label = devfs.getDeviceLabel(tmp_proxy)
+ if tmp_label == filter then
+ proxy = tmp_proxy
+ break
+ end
+ end
else
- proxy, reason = fs.get(args[1])
+ proxy, reason = devfs.getDevice(args[1])
end
+
if not proxy then
io.stderr:write(reason..'\n')
return 1
end
if #args < 2 then
- local label = proxy.getLabel()
+ local label = devfs.getDeviceLabel(proxy)
if label then
print(label)
else
@@ -28,9 +45,5 @@ if #args < 2 then
return 1
end
else
- local result, reason = proxy.setLabel(args[2])
- if not result then
- io.stderr:write((reason or "could not set label")..'\n')
- return 1
- end
+ devfs.setDeviceLabel(proxy, args[2])
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/less.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/less.lua
index 6fbd1d6f12..c9eb8aa51d 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/less.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/less.lua
@@ -6,6 +6,10 @@ local unicode = require("unicode")
local computer = require("computer")
local tx = require("transforms")
+if not io.output().tty then
+ return loadfile(shell.resolve("cat", "lua"), "bt", _G)(...)
+end
+
local args = shell.parse(...)
if #args > 1 then
io.write("Usage: more \n")
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/list.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/list.lua
index 7f16012aa5..24f7f16104 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/list.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/list.lua
@@ -7,7 +7,8 @@ if #args == 0 then
table.insert(args, ".")
end
-local path = args[1]
+local arg = args[1]
+local path = shell.resolve(arg)
if ops.help then
print([[Usage: list [path]
@@ -18,13 +19,15 @@ if ops.help then
return 0
end
-local abs_path = path:match("^/") and path or shell.resolve(shell.getWorkingDirectory() .. '/' .. path)
-
-if not fs.exists(abs_path) then
- io.stderr:write("cannot access " .. tostring(path) .. ": No such file or directory\n")
- return 2
+local real, why = fs.realPath(path)
+if real and not fs.exists(real) then
+ why = "no such file or directory"
+end
+if why then
+ io.stderr:write(string.format("cannot access '%s': %s", arg, tostring(why)))
+ return 1
end
-for path in fs.list(abs_path) do
- print(path)
+for item in fs.list(real) do
+ print(item)
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/ln.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/ln.lua
index 85bdc1a529..b89ca53895 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/ln.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/ln.lua
@@ -2,23 +2,24 @@ local component = require("component")
local fs = require("filesystem")
local shell = require("shell")
-local dirs = shell.parse(...)
-if #dirs == 0 then
+local args = shell.parse(...)
+if #args == 0 then
io.write("Usage: ln []\n")
return 1
end
-local target = shell.resolve(dirs[1])
+local target_name = args[1]
+local target = shell.resolve(target_name)
-- don't link from target if it doesn't exist, unless it is a broken link
if not fs.exists(target) and not fs.isLink(target) then
- io.stderr:write("ln: failed to access '" .. target .. "': No such file or directory\n")
+ io.stderr:write("ln: failed to access '" .. target_name .. "': No such file or directory\n")
return 1
end
local linkpath
-if #dirs > 1 then
- linkpath = shell.resolve(dirs[2])
+if #args > 1 then
+ linkpath = shell.resolve(args[2])
else
linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target))
end
@@ -27,7 +28,7 @@ if fs.isDirectory(linkpath) then
linkpath = fs.concat(linkpath, fs.name(target))
end
-local result, reason = fs.link(target, linkpath)
+local result, reason = fs.link(target_name, linkpath)
if not result then
io.stderr:write(reason..'\n')
return 1
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua
index ea3287139c..68e2ddfd26 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua
@@ -1,34 +1,13 @@
-local shell = require("shell")
-
-local _, ops = shell.parse(...)
+-- load complex, if we can (might be low on memory)
-if ops.help then
- print([[Usage: ls [OPTION]... [FILE]...
- -a, --all do not ignore entries starting with .
- --full-time with -l, print time in full iso format
- -h, --human-readable with -l and/or -s, print human readable sizes
- --si likewise, but use powers of 1000 not 1024
- -l use a long listing format
- -r, --reverse reverse order while sorting
- -R, --recursive list subdirectories recursively
- -S sort by file size
- -t sort by modification time, newest first
- -X sort alphabetically by entry extension
- -1 list one file per line
- -p append / indicator to directories
- -M display Microsoft-style file and directory count after listing
- --no-color Do not colorize the output (default colorized)
- --help display this help and exit
-For more info run: man ls]])
- return 0
-end
+local ok, why = pcall(function(...)
+ local full_ls_path = package.searchpath("tools/full_ls", package.path)
+ return loadfile(full_ls_path, "bt", _G)(...)
+end, ...)
--- load complex, if we can (might be low on memory)
-local full_ls_path = package.searchpath("tools/full-ls", package.path)
-assert(full_ls_path, "could not find ls libraries")
-local full_ls, reason = loadfile(full_ls_path, "bt", _G)
-if not full_ls then
- io.stderr:write(tostring(reason).."\nFor low memory systems, try using `list` instead\n")
+if not ok then
+ io.stderr:write((why or "") .. "\nFor low memory systems, try using `list` instead\n")
return 1
end
-return full_ls(...)
+
+return why
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/lua.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/lua.lua
index 09a8fef4b7..e88d86553b 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/lua.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/lua.lua
@@ -1,164 +1,17 @@
-local package = require("package")
-local term = require("term")
local shell = require("shell")
+local args = shell.parse(...)
-local gpu = term.gpu()
-local args, options = shell.parse(...)
-local env = setmetatable({}, {__index = _ENV})
-
-if #args > 0 then
- local script, reason = loadfile(args[1], nil, env)
- if not script then
- io.stderr:write(tostring(reason) .. "\n")
- os.exit(false)
- end
- local result, reason = pcall(script, table.unpack(args, 2))
- if not result then
- io.stderr:write(reason, "\n")
- os.exit(false)
- end
+if #args == 0 then
+ args = {"/lib/tools/lua_shell.lua"}
end
-if #args == 0 or options.i then
- local function optrequire(...)
- local success, module = pcall(require, ...)
- if success then
- return module
- end
- end
- setmetatable(env, {
- __index = function(t, k)
- _ENV[k] = _ENV[k] or optrequire(k)
- return _ENV[k]
- end,
- __pairs = function(self)
- local t = self
- return function(_, key)
- local k, v = next(t, key)
- if not k and t == env then
- t = _ENV
- k, v = next(t)
- end
- if not k and t == _ENV then
- t = package.loaded
- k, v = next(t)
- end
- return k, v
- end
- end
- })
-
- local history = {}
-
- local function findTable(t, path)
- if type(t) ~= "table" then return nil end
- if not path or #path == 0 then return t end
- local name = string.match(path, "[^.]+")
- for k, v in pairs(t) do
- if k == name then
- return findTable(v, string.sub(path, #name + 2))
- end
- end
- local mt = getmetatable(t)
- if t == env then mt = {__index=_ENV} end
- if mt then
- return findTable(mt.__index, path)
- end
- return nil
- end
- local function findKeys(t, r, prefix, name)
- if type(t) ~= "table" then return end
- for k, v in pairs(t) do
- if type(k) == "string" and string.match(k, "^"..name) then
- local postfix = ""
- if type(v) == "function" then postfix = "()"
- elseif type(v) == "table" and getmetatable(v) and getmetatable(v).__call then postfix = "()"
- elseif type(v) == "table" then postfix = "."
- end
- r[prefix..k..postfix] = true
- end
- end
- local mt = getmetatable(t)
- if t == env then mt = {__index=_ENV} end
- if mt then
- return findKeys(mt.__index, r, prefix, name)
- end
- end
- local function hint(line, index)
- line = (line or "")
- local tail = line:sub(index)
- line = line:sub(1, index - 1)
- local path = string.match(line, "[a-zA-Z_][a-zA-Z0-9_.]*$")
- if not path then return nil end
- local suffix = string.match(path, "[^.]+$") or ""
- local prefix = string.sub(path, 1, #path - #suffix)
- local t = findTable(env, prefix)
- if not t then return nil end
- local r1, r2 = {}, {}
- findKeys(t, r1, string.sub(line, 1, #line - #suffix), suffix)
- for k in pairs(r1) do
- table.insert(r2, k .. tail)
- end
- table.sort(r2)
- if #r2 == 1 then
- setmetatable(r2, {
- __index=function(tbl, key)
- if key==2 then
- local prev=tbl[1]
- local next = hint(prev,#prev+1)
- if next then
- for i,v in ipairs(next) do
- tbl[i] = v
- end
- end
- setmetatable(tbl,getmetatable(next))
- return tbl[1]
- end
- end,
- __len=function()return 2 end})
- end
- return r2
- end
-
- gpu.setForeground(0xFFFFFF)
- term.write(_VERSION .. " Copyright (C) 1994-2015 Lua.org, PUC-Rio\n")
- gpu.setForeground(0xFFFF00)
- term.write("Enter a statement and hit enter to evaluate it.\n")
- term.write("Prefix an expression with '=' to show its value.\n")
- term.write("Press Ctrl+D to exit the interpreter.\n")
- gpu.setForeground(0xFFFFFF)
-
- while term.isAvailable() do
- local foreground = gpu.setForeground(0x00FF00)
- term.write(tostring(env._PROMPT or "lua> "))
- gpu.setForeground(foreground)
- local command = term.read(history, nil, hint)
- if not command then -- eof
- return
- end
- local code, reason
- if string.sub(command, 1, 1) == "=" then
- code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env)
- else
- code, reason = load(command, "=stdin", "t", env)
- end
- if code then
- local result = table.pack(xpcall(code, debug.traceback))
- if not result[1] then
- if type(result[2]) == "table" and result[2].reason == "terminated" then
- os.exit(result[2].code)
- end
- io.stderr:write(tostring(result[2]) .. "\n")
- else
- for i = 2, result.n do
- term.write(require("serialization").serialize(result[i], true) .. "\t", true)
- end
- if term.getCursor() > 1 then
- term.write("\n")
- end
- end
- else
- io.stderr:write(tostring(reason) .. "\n")
- end
- end
+local script, reason = loadfile(args[1], nil, setmetatable({},{__index=_ENV}))
+if not script then
+ io.stderr:write(tostring(reason) .. "\n")
+ os.exit(false)
+end
+local result, reason = pcall(script, table.unpack(args, 2))
+if not result then
+ io.stderr:write(reason, "\n")
+ os.exit(false)
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/more.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/more.lua
index 64e1d407ba..5556e691c2 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/more.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/more.lua
@@ -4,6 +4,10 @@ local term = require("term")
local text = require("text")
local unicode = require("unicode")
+if not io.output().tty then
+ return loadfile(shell.resolve("cat", "lua"), "bt", _G)(...)
+end
+
local args = shell.parse(...)
if #args > 1 then
io.write("Usage: more \n")
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua
index 7a3e8c3ba1..8cfd2639d4 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua
@@ -1,8 +1,13 @@
local fs = require("filesystem")
local shell = require("shell")
-local args = shell.parse(...)
+local args, ops = shell.parse(...)
local argc = #args
+
+if ops and (ops.h or ops.help) then
+ print("see `man mount` for help");
+ os.exit(1)
+end
if argc == 0 then
-- for each mount
@@ -20,10 +25,15 @@ if argc == 0 then
local dev_mounts = mounts[device.dev_path]
table.insert(dev_mounts, device)
end
-
- table.sort(mounts)
-
- for dev_path, dev_mounts in pairs(mounts) do
+
+ local smounts = {}
+ for key,value in pairs(mounts) do
+ smounts[#smounts+1] = {key, value}
+ end
+ table.sort(smounts, function(a,b) return a[1] < b[1] end)
+
+ for _, dev in ipairs(smounts) do
+ local dev_path, dev_mounts = table.unpack(dev)
for _,device in ipairs(dev_mounts) do
local rw_ro = "(" .. device.rw_ro .. ")"
local fs_label = "\"" .. device.fs_label .. "\""
@@ -44,6 +54,8 @@ else
if not proxy then
io.stderr:write(reason,"\n")
return 1
+ elseif ops.r then
+ proxy = require("tools/ro_wrapper").wrap(proxy)
end
local result, reason = fs.mount(proxy, shell.resolve(args[2]))
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua
index 33151dbb42..f3666605a1 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua
@@ -1,63 +1,31 @@
-local fs = require("filesystem")
local shell = require("shell")
-local sh = require("sh")
+local transfer = require("tools/transfer")
local args, options = shell.parse(...)
-if #args < 2 then
- io.write("Usage: mv [-f] \n")
- io.write(" -f: overwrite file if it already exists.\n")
- return 1
+options.h = options.h or options.help
+if #args < 2 or options.h then
+ io.write([[Usage: mv [-fiv]
+ -f overwrite without prompt
+ -i prompt before overwriting
+ unless -f
+ -v verbose
+ -n do not overwrite an existing file
+ -h, --help show this help
+]])
+ return not not options.h
end
-local function is_mount(path)
-end
-
-local from = args[1] ~= "" and shell.resolve(args[1])
-local to = args[2] ~= "" and shell.resolve(args[2])
-
-if not from or not fs.exists(from) then
- io.stderr:write(string.format("No such file or directory: '%s'\n", args[1]))
- return 1
-elseif not to then
- io.stderr:write(string.format("Cannot move '%s' to '%s' No such file or directory\n", args[1], args[2]))
- return 1
-elseif fs.get(from).isReadOnly() then
- io.stderr:write("cannot remove " .. args[1] .. ", filesystem is readonly\n");
- return 1
-elseif fs.get(to).isReadOnly() then
- io.stderr:write("cannot write to " .. args[2] .. ", filesystem is readonly\n");
- return 1
-elseif fs.isDirectory(from) then
- local path = fs.canonical(from) .. '/'
- for driver, mount_point in fs.mounts() do
- if path == mount_point then
- io.stderr:write("cannot move " .. args[1] .. ", it is a mount point\n");
- return 1
- end
- end
-end
-
-if fs.isDirectory(to) then
- to = to .. "/" .. fs.name(from)
-end
-if fs.exists(to) then
- if not options.f then
- io.stderr:write("target file exists\n")
- return 1
- end
-end
+-- clean options for move (as opposed to copy)
+options =
+{
+ cmd = "mv",
+ f = options.f,
+ i = options.i,
+ v = options.v,
+ n = options.n, -- no clobber
+ P = true, -- move operations always preserve
+ r = true, -- move is allowed to move entire dirs
+ x = true, -- cannot move mount points
+}
-local result, reason
-if fs.get(from) == fs.get(to) then -- same filesystem
- result, reason = os.rename(from, to)
-else
- result, reason = sh.execute(nil, shell.resolve("cp","lua"), "-rf", from, to)
- if result then
- result, reason = sh.execute(nil, shell.resolve("rm","lua"), "-rf", from)
- end
-end
-
-if not result then
- io.stderr:write((reason or "unknown error")..'\n')
- return 1
-end
+return transfer.batch(args, options)
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/pastebin.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/pastebin.lua
index 685428499d..b5ff93473b 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/pastebin.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/pastebin.lua
@@ -21,7 +21,7 @@ local function get(pasteId, filename)
end
io.write("Downloading from pastebin.com... ")
- local url = "http://pastebin.com/raw.php?i=" .. pasteId
+ local url = "http://pastebin.com/raw/" .. pasteId
local result, response = pcall(internet.request, url)
if result then
io.write("success.\n")
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/pwd.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/pwd.lua
index 331084e585..5ea691f8db 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/pwd.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/pwd.lua
@@ -1,3 +1,14 @@
local shell = require("shell")
+local fs = require("filesystem")
+local _,op = shell.parse(...)
-io.write(shell.getWorkingDirectory(), "\n")
+local path, why = shell.getWorkingDirectory(), ""
+if op.P then
+ path, why = fs.realPath(path)
+end
+if not path then
+ io.stderr:write(string.format("error retrieving current directory: %s", why))
+ os.exit(1)
+end
+
+io.write(path, "\n")
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
index 9dd968c7ab..9a7d7b2ddb 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
@@ -54,7 +54,7 @@ if #args == 0 and (io.stdin.tty or options.i) and not options.c then
if command == "exit" then
return
elseif command ~= "" then
- local result, reason = os.execute(command)
+ local result, reason = sh.execute(_ENV, command)
if term.getCursor() > 1 then
print()
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua
index fe854b8c6a..a1b040716f 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua
@@ -19,7 +19,7 @@ if not file then
else
local status, reason = xpcall(function()
repeat
- local line = file:read("*L")
+ local line = file:read()
if line then
sh.execute(nil, line)
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/touch.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/touch.lua
index e88ee44de1..74e541d523 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/touch.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/touch.lua
@@ -30,13 +30,23 @@ for _,arg in ipairs(args) do
if fs.isDirectory(path) then
io.stderr:write(string.format("`%s' ignored: directories not supported\n", arg))
- elseif fs.exists(path) or not options.c then
- local f, reason = io.open(path, "a")
- if not f then
- io.stderr:write(string.format("touch: cannot touch `%s': permission denied\n", arg))
+ else
+ local real, reason = fs.realPath(path)
+ if real then
+ local file
+ if fs.exists(real) or not options.c then
+ file = io.open(real, "a")
+ end
+ if not file then
+ real = options.c
+ reason = "permission denied"
+ else
+ file:close()
+ end
+ end
+ if not real then
+ io.stderr:write(string.format("touch: cannot touch `%s': %s\n", arg, reason))
errors = 1
- else
- f:close()
end
end
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua
index a802133f8c..5815843479 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua
@@ -1,3 +1,25 @@
+function loadfile(filename, mode, env)
+ local handle, reason = require("filesystem").open(filename)
+ if not handle then
+ return nil, reason
+ end
+ local buffer = {}
+ while true do
+ local data, reason = handle:read(1024)
+ if not data then
+ handle:close()
+ if reason then
+ return nil, reason
+ end
+ break
+ end
+ table.insert(buffer, data)
+ end
+ buffer[1] = (buffer[1] or ""):gsub("^#![^\n]+", "") -- remove shebang if any
+ buffer = table.concat(buffer)
+ return load(buffer, "=" .. filename, mode, env)
+end
+
function dofile(filename)
local program, reason = loadfile(filename)
if not program then
@@ -6,27 +28,6 @@ function dofile(filename)
return program()
end
-function loadfile(filename, mode, env)
- local file, reason = io.open(filename)
- if not file then
- return nil, reason
- end
- local source, reason = file:read("*a")
- file:close()
- if not source then
- return nil, reason
- end
- if string.sub(source, 1, 1) == "#" then
- local endline = string.find(source, "\n", 2, true)
- if endline then
- source = string.sub(source, endline + 1)
- else
- source = ""
- end
- end
- return load(source, "=" .. filename, mode, env)
-end
-
function print(...)
local args = table.pack(...)
local stdout = io.stdout
diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua
index b6104260ab..ee6a396493 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua
@@ -21,13 +21,11 @@ function os.exit(code)
end
function os.getenv(varname)
- if varname == '#' then
- return #env()
- elseif varname ~= nil then
- return env()[varname]
- else
- return env()
+ local env = env()
+ if not varname then
+ return env
end
+ return env[varname]
end
function os.setenv(varname, value)
@@ -38,7 +36,7 @@ function os.setenv(varname, value)
local success, val = pcall(tostring, value)
if success then
env()[varname] = val
- return env()[varname]
+ return val
else
return nil, val
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/10_devfs.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/10_devfs.lua
index 17536849e0..ee07509093 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/boot/10_devfs.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/boot/10_devfs.lua
@@ -4,17 +4,20 @@ setmetatable({
},
{
__index=function(tbl,key)
- local pass
- local passthrough = function() return pass end
- if key == "getLabel" then
- pass = "devfs"
- elseif key == "spaceTotal" or key == "spaceUsed" then
- pass = 0
- elseif key == "isReadOnly" then
- pass = false
- else
- return require("devfs")[key]
+ local result =
+ ({
+ getLabel = "devfs",
+ spaceTotal = 0,
+ spaceUsed = 0,
+ isReadOnly = false,
+ })[key]
+
+ if result ~= nil then
+ return function() return result end
end
- return passthrough
+ local lib = require("devfs")
+ lib.register(tbl)
+ return lib.proxy[key]
end
}), "/dev")
+
diff --git a/src/main/resources/assets/opencomputers/loot/openos/init.lua b/src/main/resources/assets/opencomputers/loot/openos/init.lua
index 9c05bc1930..24a2446134 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/init.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/init.lua
@@ -1,20 +1,18 @@
do
local loadfile = load([[return function(file)
+ local pc,cp = computer or package.loaded.computer, component or package.loaded.component
+ local addr, invoke = pc.getBootAddress(), cp.invoke
local handle, reason = invoke(addr, "open", file)
- if not handle then
- error(reason)
- end
+ assert(handle, reason)
local buffer = ""
repeat
local data, reason = invoke(addr, "read", handle, math.huge)
- if not data and reason then
- error(reason)
- end
+ assert(data or not reason, reason)
buffer = buffer .. (data or "")
until not data
invoke(addr, "close", handle)
return load(buffer, "=" .. file, "bt", _G)
- end]], "=loadfile", "bt", {load=load,math=math,addr=computer.getBootAddress(), invoke=component.invoke})()
+ end]], "=loadfile", "bt", _G)()
loadfile("/lib/tools/boot.lua")(loadfile)
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua
index 62c8711105..df72bb5d1a 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua
@@ -2,6 +2,10 @@ local computer = require("computer")
local unicode = require("unicode")
local buffer = {}
+local metatable = {
+ __index = buffer,
+ __metatable = "file"
+}
function buffer.new(mode, stream)
local result = {
@@ -17,10 +21,6 @@ function buffer.new(mode, stream)
for i = 1, unicode.len(mode) do
result.mode[unicode.sub(mode, i, i)] = true
end
- local metatable = {
- __index = buffer,
- __metatable = "file"
- }
return setmetatable(result, metatable)
end
@@ -62,163 +62,69 @@ function buffer:lines(...)
end
end
-function buffer:read(...)
- if not self.mode.r then
- return nil, "read mode was not enabled for this stream"
+local function readChunk(self)
+ if computer.uptime() > self.timeout then
+ error("timeout")
end
-
- local timeout = computer.uptime() + self.readTimeout
-
- local function readChunk()
- if computer.uptime() > timeout then
- error("timeout")
- end
- local result, reason = self.stream:read(math.max(1,self.bufferSize))
- if result then
- self.bufferRead = self.bufferRead .. result
- return self
- else -- error or eof
- return nil, reason
- end
+ local result, reason = self.stream:read(math.max(1,self.bufferSize))
+ if result then
+ self.bufferRead = self.bufferRead .. result
+ return self
+ else -- error or eof
+ return nil, reason
end
+end
- local function readBytesOrChars(n)
- n = math.max(n, 0)
- local len, sub
- if self.mode.b then
- len = rawlen
- sub = string.sub
- else
- len = unicode.len
- sub = unicode.sub
- end
- local buffer = ""
- repeat
- if len(self.bufferRead) == 0 then
- local result, reason = readChunk()
- if not result then
- if reason then
- return nil, reason
- else -- eof
- return #buffer > 0 and buffer or nil
- end
- end
+function buffer:readLine(chop, timeout)
+ self.timeout = timeout or (computer.uptime() + self.readTimeout)
+ local start = 1
+ while true do
+ local buf = self.bufferRead
+ local i = buf:find("[\r\n]", start)
+ local c = i and buf:sub(i,i)
+ local is_cr = c == "\r"
+ if i and (not is_cr or i < #buf) then
+ local n = buf:sub(i+1,i+1)
+ if is_cr and n == "\n" then
+ c = c .. n
end
- local left = n - len(buffer)
- buffer = buffer .. sub(self.bufferRead, 1, left)
- self.bufferRead = sub(self.bufferRead, left + 1)
- until len(buffer) == n
- return buffer
- end
-
- local function readLine(chop)
- local start = 1
- while true do
- local buf = self.bufferRead
- local i = buf:find("[\r\n]", start)
- local c = i and buf:sub(i,i)
- local is_cr = c == "\r"
- if i and (not is_cr or i < #buf) then
- local n = buf:sub(i+1,i+1)
- if is_cr and n == "\n" then
- c = c .. n
- end
- local result = buf:sub(1, i - 1) .. (chop and "" or c)
- self.bufferRead = buf:sub(i + #c)
- return result
- else
- start = #self.bufferRead - (is_cr and 1 or 0)
- local result, reason = readChunk()
- if not result then
- if reason then
- return nil, reason
- else -- eof
- local result = #self.bufferRead > 0 and self.bufferRead or nil
- self.bufferRead = ""
- return result
- end
+ local result = buf:sub(1, i - 1) .. (chop and "" or c)
+ self.bufferRead = buf:sub(i + #c)
+ return result
+ else
+ start = #self.bufferRead - (is_cr and 1 or 0)
+ local result, reason = readChunk(self)
+ if not result then
+ if reason then
+ return nil, reason
+ else -- eof
+ local result = #self.bufferRead > 0 and self.bufferRead or nil
+ self.bufferRead = ""
+ return result
end
end
end
end
+end
- local function readAll()
- repeat
- local result, reason = readChunk()
- if not result and reason then
- return nil, reason
- end
- until not result -- eof
- local result = self.bufferRead
- self.bufferRead = ""
- return result
- end
-
- local function read(n, format)
- if type(format) == "number" then
- return readBytesOrChars(format)
- else
- local first_char_index = 1
- if type(format) ~= "string" then
- error("bad argument #" .. n .. " (invalid option)")
- elseif unicode.sub(format, 1, 1) == "*" then
- first_char_index = 2
- end
- format = unicode.sub(format, first_char_index, first_char_index)
- if format == "n" then
- return require("tools/advanced-buffering").readNumber(self, readChunk)
- elseif format == "l" then
- return readLine(true)
- elseif format == "L" then
- return readLine(false)
- elseif format == "a" then
- return readAll()
- else
- error("bad argument #" .. n .. " (invalid format)")
- end
- end
+function buffer:read(...)
+ if not self.mode.r then
+ return nil, "read mode was not enabled for this stream"
end
if self.mode.w or self.mode.a then
self:flush()
end
- local results = {}
local formats = table.pack(...)
if formats.n == 0 then
- return readLine(true)
- end
- for i = 1, formats.n do
- local result, reason = read(i, formats[i])
- if result then
- results[i] = result
- elseif reason then
- return nil, reason
- end
+ return self:readLine(true)
end
- return table.unpack(results, 1, formats.n)
+ return require("tools/buffered_read").read(self, readChunk, formats)
end
function buffer:seek(whence, offset)
- whence = tostring(whence or "cur")
- assert(whence == "set" or whence == "cur" or whence == "end",
- "bad argument #1 (set, cur or end expected, got " .. whence .. ")")
- offset = offset or 0
- checkArg(2, offset, "number")
- assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
-
- if self.mode.w or self.mode.a then
- self:flush()
- elseif whence == "cur" then
- offset = offset - #self.bufferRead
- end
- local result, reason = self.stream:seek(whence, offset)
- if result then
- self.bufferRead = ""
- return result
- else
- return nil, reason
- end
+ return require("tools/buffered_read").seek(self, whence, offset)
end
function buffer:setvbuf(mode, size)
@@ -263,50 +169,10 @@ function buffer:write(...)
local arg = args[i]
local result, reason
- if self.bufferMode == "full" then
- if self.bufferSize - #self.bufferWrite < #arg then
- result, reason = self:flush()
- if not result then
- return nil, reason
- end
- end
- if #arg > self.bufferSize then
- result, reason = self.stream:write(arg)
- else
- self.bufferWrite = self.bufferWrite .. arg
- result = self
- end
-
- elseif self.bufferMode == "line" then
- local l
- repeat
- local idx = arg:find("\n", (l or 0) + 1, true)
- if idx then
- l = idx
- end
- until not idx
- if l or #arg > self.bufferSize then
- result, reason = self:flush()
- if not result then
- return nil, reason
- end
- end
- if l then
- result, reason = self.stream:write(arg:sub(1, l))
- if not result then
- return nil, reason
- end
- arg = arg:sub(l + 1)
- end
- if #arg > self.bufferSize then
- result, reason = self.stream:write(arg)
- else
- self.bufferWrite = self.bufferWrite .. arg
- result = self
- end
-
- else -- self.bufferMode == "no"
+ if self.bufferMode == "no" then
result, reason = self.stream:write(arg)
+ else
+ result, reason = require("tools/buffered_write").write(self, arg)
end
if not result then
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua
index a6c7ba909b..e8faa2f72e 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua
@@ -1,327 +1,345 @@
local fs = require("filesystem")
-local comp = require("component")
local text = require("text")
-local sys = {} -- base class
+local api = {}
-local function new_node(parent, name, is_dir, proxy)
- local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy}
- if not proxy then
+local function new_node(proxy)
+ local node = {proxy=proxy}
+ if not proxy or not proxy.list then
node.children = {}
end
return node
end
--- node may support isAvailable, and may choose to not be available
-local function isAvailable(node)
- return node and not (node.proxy and node.proxy.isAvailable and not node.proxy.isAvailable())
+local function array_read(array, separator)
+ separator = separator or " "
+ local builder = {}
+ for _,value in ipairs(array) do
+ table.insert(builder, tostring(value))
+ end
+ return table.concat(builder, separator)
end
--- returns: dir, point or path
--- node (table): the handler responsible for the path
--- this is essentially the device filesystem that is registered for the given path
--- point (string): the point name (like a file name)
-function sys:findNode(path, create)
- checkArg(1, path, "string")
- local segments = fs.segments(path)
- local node = self.mtab
- while #segments > 0 do
- local name = table.remove(segments, 1)
- local prev_path = path
- path = table.concat(segments, "/")
-
- local next_node = node.children[name]
- if not isAvailable(next_node) then
- next_node = nil
+local function child_iterator(node)
+ -- a node can either list or have children, but not both (see add_child)
+ -- a node can be a file, which has a proxy, but no children
+ local listed = {}
+ if node then
+ if node.proxy and node.proxy.list then
+ -- list should return a table, not another iterator
+ -- the elements in the list are not nodes, but proxies
+ -- we have to wrap each entry with a virtual node (a node that is not in a child-parent tree)
+ -- list can be a function that returns a table, or the table already
+ local list = node.proxy.list
+ listed = type(list) == "table" and list or list()
+ elseif node.children then
+ listed = node.children
end
-
- if not next_node then
- if not create then
- path = prev_path
- break
+ end
+ local availables = {}
+ for name, item in pairs(listed) do
+ if name:len() > 0 then
+ if not item.proxy then item = new_node(item) end
+ if not item.proxy.isAvailable or item.proxy.isAvailable() then
+ availables[name] = item
end
- node.children[name] = new_node(node, name, true, false)
- end
-
- node = node.children[name]
-
- if node.proxy then -- if there is a proxy handler we stop searching here
- break
end
end
+ return pairs(availables)
+end
- -- only dirs can have trailing path
- -- trailing path on a dev point (file) not allowed
- if path == "" or node.is_dir and node.proxy then
- return node, path
+local function get_child(node, name)
+ for child_name, child in child_iterator(node) do
+ if child_name == name then
+ return child
+ end
end
end
-function sys:invoke(method, path, ...)
- local node, rest = self.findNode(path)
- if not node or -- not found
- rest == "" and node.is_dir or -- path is dir
- not node.proxy[method] then -- optional method
- return 0
+local function add_child(node, name, proxy)
+ if not node or node.proxy and node.proxy.list then
+ return nil, "cannot add child to listing proxy"
end
- -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed
- return node.proxy[method](rest)
-end
-function sys:size(path)
- return self.invoke("size", path)
+ local child = new_node(proxy)
+ node.children[name] = child
+ return child
end
-function sys:lastModified(path)
- return self.invoke("lastModified", path)
+local function findNode(path, bCreate)
+ local segments = fs.segments(path)
+ local node = api.root
+ while #segments > 0 do
+ local name = table.remove(segments, 1)
+ local next = get_child(node, name)
+ if not next then
+ if bCreate then
+ if not add_child(node, name) then
+ return nil, "cannot create child node"
+ end
+ else
+ return nil, "no such file or directory"
+ end
+ end
+ node = next or get_child(node, name)
+ end
+ return node
end
-function sys:isDirectory(path)
- local node, rest = self.findNode(path)
- if not node then
- return
- end
+-- devfs api
- if rest == "" then
- return node.is_dir
- elseif node.proxy then
- return node.proxy.isDirectory(rest)
- end
-end
+api.root = new_node()
-function sys:open(path, mode)
+function api.create(path, proxy)
checkArg(1, path, "string")
- checkArg(2, mode, "string", "nil")
-
- if not self.exists(path) then
- return nil, path.." file not found"
- elseif self.isDirectory(path) then
- return nil, path.." is a directory"
+ checkArg(2, proxy, "table", "nil")
+ local pwd = fs.path(path)
+ local name = fs.name(path)
+ if not name then return nil, "invalid devfs path" end
+ local pnode, why = findNode(pwd, true)
+ if not pnode then
+ return nil, why
end
- mode = mode or "r"
- -- everything at this level should be a binary open
- mode = mode:gsub("b", "")
-
- if not ({a=true,w=true,r=true})[mode] then
- return nil, "invalid mode"
+ if get_child(pnode, name) then
+ return nil, "file or directory exists"
end
- local node, rest = self.findNode(path)
- -- there must be a node, else exists would have failed
+ return add_child(pnode, name, proxy)
+end
- local args = {}
- if rest ~= "" then
- -- having more rest means we expect the proxy fs to open the point
- args[1] = rest
+-- the filesystem object as seen from the system mount interface
+api.proxy = {}
+
+-- forward declare injector
+local inject_dynamic_pairs
+local function dynamic_list(path, fsnode)
+ local nodes, links, dirs = {}, {}, {}
+ local node = findNode(path)
+ if node then
+ for name,node in child_iterator(node) do
+ if node.proxy and node.proxy.link then
+ links[name] = node.proxy.link
+ elseif node.proxy and node.proxy.list then
+ local child = {name=name,parent=fsnode}
+ local child_path = path .. "/" .. name
+ inject_dynamic_pairs(child, child_path, true)
+ dirs[name] = child
+ else
+ nodes[name] = node
+ end
+ end
end
- args[#args+1] = mode
+ return nodes, links, dirs
+end
- return node.proxy.open(table.unpack(args))
+inject_dynamic_pairs = function(fsnode, path, bStoreUse)
+ if getmetatable(fsnode) then return end
+ fsnode.children = nil
+ fsnode.links = nil
+ setmetatable(fsnode,
+ {
+ __index = function(tbl, key)
+ local bLinks = key == "links"
+ local bChildren = key == "children"
+ if not bLinks and not bChildren then return end
+ local nodes, links, dirs = dynamic_list(path, tbl)
+ if bStoreUse then
+ tbl.children = dirs
+ tbl.links = links
+ end
+ return bLinks and links or dirs
+ end
+ })
end
-function sys:list(path)
- local node, rest = self.findNode(path)
- if not node or (rest ~= "" and not node.is_dir) then-- not found
- return {}
- elseif rest == "" and not node.is_dir then -- path is file
- return {path}
- elseif node.proxy then
- -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed
- return node.proxy.list(rest)
+local label_lib = dofile("/lib/tools/device_labeling.lua")
+label_lib.loadRules()
+api.getDeviceLabel = label_lib.getDeviceLabel
+api.setDeviceLabel = label_lib.setDeviceLabel
+
+local registered = false
+function api.register(public_proxy)
+ if registered then return end
+ registered = true
+
+ local start_path = "/lib/tools/devfs/"
+ for starter in fs.list(start_path) do
+ local full_path = start_path .. starter
+ local _,matched = starter:gsub("%.lua$","")
+ if matched > 0 then
+ local data = dofile(full_path)
+ for name, entry in pairs(data) do
+ api.create(name, entry)
+ end
+ end
+ end
+
+ if rawget(public_proxy, "fsnode") then
+ inject_dynamic_pairs(public_proxy.fsnode, "")
end
+end
- -- rest == "" and node.is_dir
- local keys = {}
- for k,node in pairs(node.children) do
- if isAvailable(node) then
- table.insert(keys, k)
- end
+function api.proxy.list(path)
+ local result = {}
+ for name,node in pairs(dynamic_list(path, false, false)) do
+ table.insert(result, name)
end
- return keys
+ return result
end
-function sys:remove(path)
- return nil, "cannot remove devfs files or directories"
- --checkArg(1, path, "string")
+function api.proxy.isDirectory(path)
+ local node = findNode(path)
+ return node and node.proxy and node.proxy.list
+end
- --if path == "" then
- -- return nil, "no such file or directory"
- --end
+function api.proxy.size(path)
+ checkArg(1, path, "string")
+ local node, why = findNode(path)
+ if not node or not node.proxy then
+ return 0
+ end
- --if not self.exists(path) then
- -- return nil, path.." file not found"
- --end
+ local proxy = node.proxy
+ if proxy.list then return 0 end
+ if proxy.size then return proxy.size() end
+ if proxy.open then return 0 end
+ if proxy.read then return proxy.read():len() end
+ if proxy[1] ~= nil then return array_read(proxy):len() end
+ return 0
+end
- --local node, rest = self.findNode(path)
+function api.proxy.lastModified()
+ return 0
+end
- --if rest ~= "" then -- if rest is not resolved, this isn't our path
- -- return node.proxy.remove(rest)
- --end
+function api.proxy.exists(path)
+ checkArg(1, path, "string")
+ return not not findNode(path)
+end
- --node.parent.children[node.name] = nil
+function api.getDevice(path)
+ checkArg(1, path, "string")
+ local device
+ local reason = "no such device"
+ local real, why = fs.realPath(require("shell").resolve(path))
+ if not real then return nil, why end
+ if fs.exists(real) then
+ -- we don't have a good way of knowing where dev is mounted still
+ -- similar hack in api.proxy.open
+ real = fs.path(real) .. (fs.name(real) or "")
+ local part, subbed = real:gsub("^/dev/", "")
+ if subbed > 0 and part:len() > 0 then
+ local node = findNode(part)
+ if node and node.proxy then
+ -- must be a special device node
+ device = node.proxy.device
+ end
+ if not device then
+ reason = "not a device"
+ end
+ else
+ device, reason = fs.get(real)
+ end
+ end
+ return device, reason
end
-function sys:exists(path)
+function api.proxy.open(path, mode)
checkArg(1, path, "string")
- local node, rest = self.findNode(path)
+ checkArg(2, mode, "string", "nil")
- if not node then
- return false
- elseif rest == "" then
- return true
- else
- return node.proxy.exists(rest)
+ mode = mode or "r"
+ local bRead = mode:match("[ra]")
+ local bWrite = mode:match("[wa]")
+
+ if not bRead and not bWrite then
+ return nil, "invalid mode"
end
-end
-function sys:create(path, handler)
- if self.exists(path) then
- return nil, "path already exists"
+ local node, why = findNode(path)
+ if not node then
+ return nil, why
+ elseif not node.proxy or node.proxy.list then
+ return nil, "is a directory"
end
- local segments = fs.segments(path)
- local target = table.remove(segments)
- path = table.concat(segments, "/")
+ local proxy = node.proxy
- if not target or target == "" then
- return nil, "missing argument"
+ -- in case someone tries to open a link directly, refer them back to fs
+ -- this is an unfortunate pathing hack due to optimizations for memory
+ if proxy.link then
+ return fs.open("/dev/"..path, mode)
end
- local node, rest = self.findNode(path, true)
- if rest ~= "" then
- return node.proxy.create(rest, handler)
+ -- special (but common) simple readonly cases
+ if proxy[1] ~= nil then -- contains special readonly value
+ local array = proxy
+ proxy.read = function()return array_read(array) end
end
- node.children[target] = new_node(node, target, not not handler.list, handler)
- return true
-end
-local function new_devfs_dir(name)
- local sys_child = setmetatable({}, {__index=function(tbl,key)
- if sys[key] then
- return function(...)
- return sys[key](tbl, ...)
- end
- end
- end})
- sys_child.mtab = new_node(nil, name or "/", true)
+ if proxy.open then
+ return proxy.open(mode)
+ end
- return sys_child
-end
+ if bRead and not proxy.read then
+ return nil, "cannot open for read"
+ elseif bWrite and not proxy.write then
+ return nil, "cannot open for write"
+ end
-local devfs = new_devfs_dir()
+ local txtRead = bRead and proxy.read()
-local bfd = "bad file descriptor"
+ if bWrite then
+ return text.internal.writer(proxy.write, mode, txtRead)
+ end
--- to allow sub dirs to act like sub devfs
-devfs.new_dir = new_devfs_dir
-devfs.new_node = new_node
-function devfs.new_callback_proxy(read_callback, write_callback)
- return
- {
- open = function(mode)
- if ({r=true, rb=true})[mode] then
- if not read_callback then
- return nil, "file cannot be opened for read"
- end
- return text.internal.reader(read_callback())
- end
- if not write_callback then
- return nil, "file cannot be opened for write"
- end
- return text.internal.writer(write_callback, ({a=true,ab=true})[mode] and read_callback())
- end,
- size = function()
- return read_callback and string.len(read_callback()) or 0
- end
- }
+ return text.internal.reader(txtRead, mode)
end
-function devfs.setLabel(value)
- error("drive does not support labeling")
+-- as long as the fsnode hack is used, fs.isLink is not needed here
+-- function api.proxy.isLink(path) end
+
+local function checked_invoke(handle, method, ...)
+ checkArg(1, handle, "table")
+ checkArg(2, method, "string")
+ checkArg(3, handle[method], "function", "table", "nil")
+ local m = handle[method]
+ if not m then
+ return nil, "bad file handle"
+ elseif type(m) == "table" then
+ local mm = getmetatable(m)
+ assert(mm and mm.__call, string.format("FILE handle [%s] method defined, but is not callable", tostring(method)))
+ end
+ return m(handle, ...)
end
-function devfs.makeDirectory(path)
- return false, "to create dirs in devfs use devfs.create"
+function api.proxy.read(h, ...)
+ return checked_invoke(h, "read", ...)
end
-function devfs.read(h,...)
- if not h.read then return nil, bfd end
- return h:read(...)
+function api.proxy.close(h, ...)
+ return checked_invoke(h, "close", ...)
end
-function devfs.seek(h,...)
- if not h.seek then return nil, bfd end
- return h:seek(...)
+function api.proxy.write(h, ...)
+ return checked_invoke(h, "write", ...)
end
-function devfs.write(h,...)
- if not h.write then return nil, bfd end
- return h:write(...)
+function api.proxy.seek(h, ...)
+ return checked_invoke(h, "seek", ...)
end
-function devfs.close(h, ...)
- if not h.close then return nil, bfd end
- return h:close(...)
+function api.proxy.remove()
+ return nil, "cannot remove file or directory"
end
--- devfs.create creates a new dev point at path
--- devfs is mounted to /sys by default, and /dev is a symlink to /sys/dev. If you want a devfs point to show up in /dev, specify a path here as "/dev/your_path")
--- the handler can be a single file dev file (called a point), or a devfs dir [which allows it to list its own dynamic list of points and dirs]
--- note: devfs dirs that list their own child dirs will have to handle directory queries on their own, such as list() and open(path, mode)
-
--- A handler represents a directory IF it defines list(), which returns a string array of the point names
--- a directory handler acts like simplified filesystem of its own.
--- note: that when creating new devfs points or dirs, devfs.create will not traverse into dynamic directory children of dev mount points
--- Meaning, if you create a devfs dir, which returns dirs children of its own, devfs.create does not support creating dev points
--- on those children
-
--- see new_devfs_dir() -- it might work for you, /dev uses it
-
--- Also note, your own devfs dirs may implement open() however they like -- devfs points' open() is called by the devfs library but dynamic
--- dir act as their own library for their own points
-
--- ### devfs point methods ###
--- Required
- -- open(mode: string []) file: returns new file handle for point (see "devfs point handle methods")
--- Optional
- -- size(path) number
-
--- ### devfs point handle instance methods ###
--- Required
- -- + technicaly, one of the following is not required when the mode is for the other (e.g. no read when in write mode)
- -- write(self, value, ...) boolean: writes each value (params list) and returns success
- -- read(self, n: number) string: return string of n bytes, nil when no more bytes available
--- Optional
- -- seek(self, whence [string], offset [number]) number: move file handle from whence by offset, return offset result
- -- close(self) boolean: close the file handle. Note that if your open method allocated resources, you'll need to release them in close
-
--- ### devfs dir methods ###
--- Required
- -- list() string[]: return list of child point names
- -- if you use new_devfs_dir, set metatable on .points with __pairs and __index if you want a dynamic list of files
- -- open(path, mode) file (table): return a file handle to path (path is relative)
- -- it would be nice to make open() optional, but devfs doesn't know where you might store your point handlers, if you even have any
--- Optional
- -- size(path) number
- -- lastModified(path) number
- -- isDirectory(path) boolean -- default returns false. Having dynamic dirs is considered advanced
- -- remove(path) boolean
- -- rename(path) boolean
- -- exists(path) boolean -- default checks path against list() results
-
--- /dev is a special handler
-
-local function devfs_load(key)
- -- using loadfile to allow us to pass args
- -- load order complication: some dev points are dirs that want to use devfs api, but can't require devfs
- devfs.create(key, loadfile("/lib/tools/devfs/" .. key .. ".lua", "bt", _G)(devfs))
+function api.proxy.makeDirectory()
+ return nil, "use create in the devfs api"
end
-devfs_load("random")
-devfs_load("null")
-devfs_load("eeprom")
-devfs_load("eeprom-data")
---devfs_load("filesystems")
+function api.proxy.setLabel()
+ return nil, "cannot set label on devfs"
+end
-return devfs
+return api
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua
index f46c8212a6..8f7c553d09 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua
@@ -4,28 +4,18 @@ local unicode = require("unicode")
local filesystem, fileStream = {}, {}
local isAutorunEnabled = nil
local mtab = {name="", children={}, links={}}
+local fstab = {}
local function segments(path)
- path = path:gsub("\\", "/")
- repeat local n; path, n = path:gsub("//", "/") until n == 0
local parts = {}
- for part in path:gmatch("[^/]+") do
- table.insert(parts, part)
- end
- local i = 1
- while i <= #parts do
- if parts[i] == "." then
- table.remove(parts, i)
- elseif parts[i] == ".." then
- table.remove(parts, i)
- i = i - 1
- if i > 0 then
- table.remove(parts, i)
- else
- i = 1
+ for part in path:gmatch("[^\\/]+") do
+ local current, up = part:find("^%.?%.$")
+ if current then
+ if up == 2 then
+ table.remove(parts)
end
else
- i = i + 1
+ table.insert(parts, part)
end
end
return parts
@@ -35,7 +25,7 @@ local function saveConfig()
local root = filesystem.get("/")
if root and not root.isReadOnly() then
filesystem.makeDirectory("/etc")
- local f = io.open("/etc/filesystem.cfg", "w")
+ local f = filesystem.open("/etc/filesystem.cfg", "w")
if f then
f:write("autorun="..tostring(isAutorunEnabled))
f:close()
@@ -43,38 +33,59 @@ local function saveConfig()
end
end
-local function findNode(path, create, depth)
+local function findNode(path, create, resolve_links)
checkArg(1, path, "string")
- depth = depth or 0
- if depth > 100 then
- error("link cycle detected")
- end
+ local visited = {}
local parts = segments(path)
+ local ancestry = {}
local node = mtab
- while #parts > 0 do
- local part = parts[1]
+ local index = 1
+ while index <= #parts do
+ local part = parts[index]
+ ancestry[index] = node
if not node.children[part] then
- if node.links[part] then
- return findNode(filesystem.concat(node.links[part], table.concat(parts, "/", 2)), create, depth + 1)
- else
- if create then
- node.children[part] = {name=part, parent=node, children={}, links={}}
+ local link_path = node.links[part]
+ if link_path then
+ if not resolve_links and #parts == index then break end
+
+ if visited[path] then
+ return nil, string.format("link cycle detected '%s'", path)
+ end
+ -- the previous parts need to be conserved in case of future ../.. link cuts
+ visited[path] = index
+ local pst_path = "/" .. table.concat(parts, "/", index + 1)
+ local pre_path
+
+ if link_path:match("^[^/]") then
+ pre_path = table.concat(parts, "/", 1, index - 1) .. "/"
+ local link_parts = segments(link_path)
+ local join_parts = segments(pre_path .. link_path)
+ local back = (index - 1 + #link_parts) - #join_parts
+ index = index - back
+ node = ancestry[index]
else
- local vnode, vrest = node, table.concat(parts, "/")
- local rest = vrest
- while node and not node.fs do
- rest = filesystem.concat(node.name, rest)
- node = node.parent
- end
- return node, rest, vnode, vrest
+ pre_path = ""
+ index = 1
+ node = mtab
end
+
+ path = pre_path .. link_path .. pst_path
+ parts = segments(path)
+ part = nil -- skip node movement
+ elseif create then
+ node.children[part] = {name=part, parent=node, children={}, links={}}
+ else
+ break
end
end
- node = node.children[part]
- table.remove(parts, 1)
+ if part then
+ node = node.children[part]
+ index = index + 1
+ end
end
- local vnode, vrest = node, nil
- local rest = nil
+
+ local vnode, vrest = node, #parts >= index and table.concat(parts, "/", index)
+ local rest = vrest
while node and not node.fs do
rest = rest and filesystem.concat(node.name, rest) or node.name
node = node.parent
@@ -82,13 +93,6 @@ local function findNode(path, create, depth)
return node, rest, vnode, vrest
end
-local function removeEmptyNodes(node)
- while node and node.parent and not node.fs and not next(node.children) and not next(node.links) do
- node.parent.children[node.name] = nil
- node = node.parent
- end
-end
-
-------------------------------------------------------------------------------
function filesystem.isAutorunEnabled()
@@ -123,16 +127,12 @@ function filesystem.canonical(path)
end
end
-function filesystem.concat(pathA, pathB, ...)
- checkArg(1, pathA, "string")
- local function concat(n, a, b, ...)
- if not b then
- return a
- end
- checkArg(n, b, "string")
- return concat(n + 1, a .. "/" .. b, ...)
+function filesystem.concat(...)
+ local set = table.pack(...)
+ for index, value in ipairs(set) do
+ checkArg(index, value, "string")
end
- return filesystem.canonical(concat(2, pathA, pathB, ...))
+ return filesystem.canonical(table.concat(set, "/"))
end
function filesystem.get(path)
@@ -153,10 +153,29 @@ function filesystem.get(path)
return nil, "no such file system"
end
+function filesystem.realPath(path)
+ checkArg(1, path, "string")
+ local node, rest, vnode, vrest = findNode(path, false, true)
+ if not node then return nil, rest end
+ local parts = {rest or nil}
+ repeat
+ table.insert(parts, 1, node.name)
+ node = node.parent
+ until not node
+ return table.concat(parts, "/")
+end
+
function filesystem.isLink(path)
- local node, rest, vnode, vrest = findNode(filesystem.path(path))
- if not vrest and vnode.links[filesystem.name(path)] ~= nil then
- return true, vnode.links[filesystem.name(path)]
+ local name = filesystem.name(path)
+ local node, rest, vnode, vrest = findNode(filesystem.path(path), false, true)
+ if not node then return nil, rest end
+ local target = vnode.links[name]
+ -- having vrest here indicates we are not at the
+ -- owning vnode due to a mount point above this point
+ -- but we can have a target when there is a link at
+ -- the mount point root, with the same name
+ if not vrest and target ~= nil then
+ return true, target
end
return false
end
@@ -168,8 +187,19 @@ function filesystem.link(target, linkpath)
if filesystem.exists(linkpath) then
return nil, "file already exists"
end
+ local linkpath_parent = filesystem.path(linkpath)
+ if not filesystem.exists(linkpath_parent) then
+ return nil, "no such directory"
+ end
+ local linkpath_real, reason = filesystem.realPath(linkpath_parent)
+ if not linkpath_real then
+ return nil, reason
+ end
+ if not filesystem.isDirectory(linkpath_real) then
+ return nil, "not a directory"
+ end
- local node, rest, vnode, vrest = findNode(filesystem.path(linkpath), true)
+ local node, rest, vnode, vrest = findNode(linkpath_real, true)
vnode.links[filesystem.name(linkpath)] = target
return true
end
@@ -182,43 +212,62 @@ function filesystem.mount(fs, path)
assert(type(fs) == "table", "bad argument #1 (file system proxy or address expected)")
checkArg(2, path, "string")
- if path ~= "/" and filesystem.exists(path) then
- return nil, "file already exists"
+ local real
+ if not mtab.fs then
+ if path == "/" then
+ real = path
+ else
+ return nil, "rootfs must be mounted first"
+ end
+ else
+ local why
+ real, why = filesystem.realPath(path)
+ if not real then
+ return nil, why
+ end
+
+ if filesystem.exists(real) then
+ return nil, "file already exists"
+ end
end
- local node, rest, vnode, vrest = findNode(path, true)
- if vnode.fs then
+ local fsnode
+ if fstab[real] then
return nil, "another filesystem is already mounted here"
end
- vnode.fs = fs
+ for path,node in pairs(fstab) do
+ if node.fs.address == fs.address then
+ fsnode = node
+ break
+ end
+ end
+
+ if not fsnode then
+ fsnode = select(3, findNode(real, true))
+ -- allow filesystems to intercept their own nodes
+ fs.fsnode = fsnode
+ else
+ local pwd = filesystem.path(real)
+ local parent = select(3, findNode(pwd, true))
+ local name = filesystem.name(real)
+ fsnode = setmetatable({name=name,parent=parent},{__index=fsnode})
+ parent.children[name] = fsnode
+ end
+
+ fsnode.fs = fs
+ fstab[real] = fsnode
+
return true
end
function filesystem.mounts()
- local function path(node)
- local result = "/"
- while node and node.parent do
- for name, child in pairs(node.parent.children) do
- if child == node then
- result = "/" .. name .. result
- break
- end
- end
- node = node.parent
- end
- return result
+ local tmp = {}
+ for path,node in pairs(fstab) do
+ table.insert(tmp, {node.fs,path})
end
- local queue = {mtab}
return function()
- while #queue > 0 do
- local node = table.remove(queue)
- for _, child in pairs(node.children) do
- table.insert(queue, child)
- end
- if node.fs then
- return node.fs, path(node)
- end
- end
+ local next = table.remove(tmp)
+ if next then return table.unpack(next) end
end
end
@@ -258,42 +307,47 @@ end
function filesystem.umount(fsOrPath)
checkArg(1, fsOrPath, "string", "table")
+ local real
+ local fs
+ local addr
if type(fsOrPath) == "string" then
- local node, rest, vnode, vrest = findNode(fsOrPath)
- if not vrest and vnode.fs then
- vnode.fs = nil
- removeEmptyNodes(vnode)
- return true
- end
+ real = filesystem.realPath(fsOrPath)
+ addr = fsOrPath
+ else -- table
+ fs = fsOrPath
end
- local address = type(fsOrPath) == "table" and fsOrPath.address or fsOrPath
- local result = false
- for proxy, path in filesystem.mounts() do
- local addr = type(proxy) == "table" and proxy.address or proxy
- if string.sub(addr, 1, address:len()) == address then
- local node, rest, vnode, vrest = findNode(path)
- vnode.fs = nil
- removeEmptyNodes(vnode)
- result = true
+
+ local paths = {}
+ for path,node in pairs(fstab) do
+ if real == path or addr == node.fs.address or fs == node.fs then
+ table.insert(paths, path)
end
end
- return result
+ for _,path in ipairs(paths) do
+ local node = fstab[path]
+ fstab[path] = nil
+ node.fs = nil
+ node.parent.children[node.name] = nil
+ end
+ return #paths > 0
end
function filesystem.exists(path)
+ if not filesystem.realPath(filesystem.path(path)) then
+ return false
+ end
local node, rest, vnode, vrest = findNode(path)
if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link
return true
- end
- if node and node.fs then
+ elseif node and node.fs then
return node.fs.exists(rest)
end
return false
end
function filesystem.size(path)
- local node, rest, vnode, vrest = findNode(path)
- if not vnode.fs and (not vrest or vnode.links[vrest]) then
+ local node, rest, vnode, vrest = findNode(path, false, true)
+ if not node or not vnode.fs and (not vrest or vnode.links[vrest]) then
return 0 -- virtual directory or symlink
end
if node.fs and rest then
@@ -303,9 +357,11 @@ function filesystem.size(path)
end
function filesystem.isDirectory(path)
- local node, rest, vnode, vrest = findNode(path)
+ local real, reason = filesystem.realPath(path)
+ if not real then return nil, reason end
+ local node, rest, vnode, vrest = findNode(real)
if not vnode.fs and not vrest then
- return true -- virtual directory
+ return true -- virtual directory (mount point)
end
if node.fs then
return not rest or node.fs.isDirectory(rest)
@@ -314,8 +370,8 @@ function filesystem.isDirectory(path)
end
function filesystem.lastModified(path)
- local node, rest, vnode, vrest = findNode(path)
- if not vnode.fs and not vrest then
+ local node, rest, vnode, vrest = findNode(path, false, true)
+ if not node or not vnode.fs and not vrest then
return 0 -- virtual directory
end
if node.fs and rest then
@@ -325,37 +381,31 @@ function filesystem.lastModified(path)
end
function filesystem.list(path)
- local node, rest, vnode, vrest = findNode(path)
- if not vnode.fs and vrest and not (node and node.fs) then
- return nil, "no such file or directory"
- end
- local result, reason
- if node and node.fs then
- result, reason = node.fs.list(rest or "")
- end
- result = result or {}
- if not vrest then
- for k in pairs(vnode.children) do
- table.insert(result, k .. "/")
- end
- for k in pairs(vnode.links) do
- table.insert(result, k)
+ local node, rest, vnode, vrest = findNode(path, false, true)
+ local result = {}
+ if node then
+ result = node.fs and node.fs.list(rest or "") or {}
+ -- `if not vrest` indicates that vnode reached the end of path
+ -- in other words, vnode[children, links] represent path
+ if not vrest then
+ for k,n in pairs(vnode.children) do
+ if not n.fs or fstab[filesystem.concat(path, k)] then
+ table.insert(result, k .. "/")
+ end
+ end
+ for k in pairs(vnode.links) do
+ table.insert(result, k)
+ end
end
end
- table.sort(result)
- local i, f = 1, nil
- while i <= #result do
- if result[i] == f then
- table.remove(result, i)
- else
- f = result[i]
- i = i + 1
- end
+ local set = {}
+ for _,name in ipairs(result) do
+ set[filesystem.canonical(name)] = name
end
- local i = 0
return function()
- i = i + 1
- return result[i]
+ local key, value = next(set)
+ set[key or false] = nil
+ return value
end
end
@@ -378,98 +428,30 @@ function filesystem.makeDirectory(path)
end
function filesystem.remove(path)
- local function removeVirtual()
- local node, rest, vnode, vrest = findNode(filesystem.path(path))
- -- vrest represents the remaining path beyond vnode
- -- vrest is nil if vnode reaches the full path
- -- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
- if not vrest then
- local name = filesystem.name(path)
- if vnode.children[name] then
- vnode.children[name] = nil
- removeEmptyNodes(vnode)
- return true
- elseif vnode.links[name] then
- vnode.links[name] = nil
- removeEmptyNodes(vnode)
- return true
- end
- end
- -- return false even if vrest is nil because this means it was a expected
- -- to be a real file
- return false
- end
- local function removePhysical()
- node, rest = findNode(path)
- if node.fs and rest then
- return node.fs.remove(rest)
- end
- return false
- end
- local success = removeVirtual()
- success = removePhysical() or success -- Always run.
- if success then return true
- else return nil, "no such file or directory"
- end
+ return require("tools/fsmod").remove(path, findNode)
end
function filesystem.rename(oldPath, newPath)
- if filesystem.isLink(oldPath) then
- local node, rest, vnode, vrest = findNode(filesystem.path(oldPath))
- local target = vnode.links[filesystem.name(oldPath)]
- local result, reason = filesystem.link(target, newPath)
- if result then
- filesystem.remove(oldPath)
- end
- return result, reason
- else
- local oldNode, oldRest = findNode(oldPath)
- local newNode, newRest = findNode(newPath)
- if oldNode.fs and oldRest and newNode.fs and newRest then
- if oldNode.fs.address == newNode.fs.address then
- return oldNode.fs.rename(oldRest, newRest)
- else
- local result, reason = filesystem.copy(oldPath, newPath)
- if result then
- return filesystem.remove(oldPath)
- else
- return nil, reason
- end
- end
- end
- return nil, "trying to read from or write to virtual directory"
- end
+ return require("tools/fsmod").rename(oldPath, newPath, findNode)
end
function filesystem.copy(fromPath, toPath)
- if filesystem.isDirectory(fromPath) then
- return nil, "cannot copy folders"
- end
- local input, reason = io.open(fromPath, "rb")
- if not input then
- return nil, reason
- end
- local output, reason = io.open(toPath, "wb")
- if not output then
+ local data
+ local input, reason = filesystem.open(fromPath, "rb")
+ if input then
+ local output, reason = filesystem.open(toPath, "wb")
+ if output then
+ repeat
+ data, reason = input:read(1024)
+ if not data then break end
+ data, reason = output:write(data)
+ if not data then data, reason = false, "failed to write" end
+ until not data
+ output:close()
+ end
input:close()
- return nil, reason
end
- repeat
- local buffer, reason = input:read(1024)
- if not buffer and reason then
- return nil, reason
- elseif buffer then
- local result, reason = output:write(buffer)
- if not result then
- input:close()
- output:close()
- return nil, reason
- end
- end
- until not buffer
- input:close()
- output:close()
- return true
+ return data == nil, reason
end
function fileStream:close()
@@ -508,7 +490,10 @@ function filesystem.open(path, mode)
assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
"bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
- local node, rest = findNode(path)
+ local node, rest = findNode(path, false, true)
+ if not node then
+ return nil, rest
+ end
if not node.fs or not rest or (({r=true,rb=true})[mode] and not node.fs.exists(rest)) then
return nil, "file not found"
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/internet.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/internet.lua
index 0b252bb30b..c924a0dabc 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/internet.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/internet.lua
@@ -11,10 +11,10 @@ function internet.request(url, data, headers)
checkArg(2, data, "string", "table", "nil")
checkArg(3, headers, "table", "nil")
- local inet = component.internet
- if not inet then
+ if not component.isAvailable("internet") then
error("no primary internet card found", 2)
end
+ local inet = component.internet
local post
if type(data) == "string" then
@@ -31,23 +31,35 @@ function internet.request(url, data, headers)
error(reason, 2)
end
- return function()
- while true do
- local data, reason = request.read()
- if not data then
- request.close()
- if reason then
- error(reason, 2)
- else
- return nil -- eof
+ return setmetatable(
+ {
+ ["()"] = "function():string -- Tries to read data from the socket stream and return the read byte array.",
+ close = setmetatable({},
+ {
+ __call = request.close,
+ __tostring = function() return "function() -- closes the connection" end
+ })
+ },
+ {
+ __call = function()
+ while true do
+ local data, reason = request.read()
+ if not data then
+ request.close()
+ if reason then
+ error(reason, 2)
+ else
+ return nil -- eof
+ end
+ elseif #data > 0 then
+ return data
end
- elseif #data > 0 then
- return data
+ -- else: no data, block
+ os.sleep(0)
end
- -- else: no data, block
- os.sleep(0)
- end
- end
+ end,
+ __index = request,
+ })
end
-------------------------------------------------------------------------------
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua
index ec1de9ebff..d753917e7f 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua
@@ -39,7 +39,7 @@ function package.searchpath(name, path, sep, rep)
subPath = fs.concat(os.getenv("PWD") or "/", subPath)
end
if fs.exists(subPath) then
- local file = io.open(subPath, "r")
+ local file = fs.open(subPath, "r")
if file then
file:close()
return subPath
@@ -111,7 +111,7 @@ function require(module)
-- the pcall is mostly for out of memory errors
local ok, f, extra = pcall(package.searchers[i], module)
if not ok then
- table.insert(errorMsg, "\t" .. f)
+ table.insert(errorMsg, "\t" .. (f or "nil"))
elseif f and type(f) ~= "string" then
loader = f
value = extra
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua
index 1cca5e4459..b2f1bbba0f 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua
@@ -57,25 +57,15 @@ function process.load(path, env, init, name)
return 127
end
os.setenv("_", program)
- local f, reason = io.open(program)
- if not f then
- io.stderr:write("could not read '" .. program .. "': " .. tostring(reason) .. "\n")
- return 1
- end
- local shabang = f:read(2)
- local command = f:read()
+ local f, reason = fs.open(program)
+ local shebang = f and f:read(1024):match("^#!([^\n]+)")
f:close()
- if shabang == "#!" then
- if require("text").trim(command or "") == "" then
- return -- nothing to do
- end
- local result = table.pack(require("shell").execute(command, env, program, ...))
- if not result[1] then
- error(result[2])
- end
+ if shabang then
+ local result = table.pack(shell.execute(shebang:gsub("%s",""), env, program, ...))
+ assert(result[1], result[2])
return table.unpack(result)
end
- command, reason = loadfile(program, "bt", env)
+ local command, reason = loadfile(program, "bt", env)
if not command then
io.stderr:write(program..(reason or ""):gsub("^[^:]*", "").."\n")
return 128
@@ -102,7 +92,7 @@ function process.load(path, env, init, name)
if msg.reason ~= "terminated" then
io.stderr:write(msg.reason.."\n")
end
- return msg.code
+ return msg.code or 0
end
local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
io.stderr:write(string.format('%s:\n%s', msg or '', stack))
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua
index fe3e48f058..e7a441ca80 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua
@@ -38,8 +38,10 @@ function sh.internal.command_result_as_code(ec)
-- convert lua result to bash ec
if ec == false then
return 1
- elseif ec == nil or ec == true or type(ec) ~= "number" then
+ elseif ec == nil or ec == true then
return 0
+ elseif type(ec) ~= "number" then
+ return 2 -- illegal number
else
return ec
end
@@ -137,7 +139,8 @@ function sh.expand(value)
if sh.internal.isIdentifier(key) then
return sh.internal.expandKey(key)
end
- error("${" .. key .. "}: bad substitution")
+ io.stderr:write("${" .. key .. "}: bad substitution\n")
+ os.exit(1)
end)
if expanded:find('`') then
expanded = sh.internal.parse_sub(expanded)
@@ -291,6 +294,8 @@ function sh.internal.runThreads(threads)
-- in case this was the end of the line, args is returned
return args[2]
end
+ elseif not result[1] then
+ io.stderr:write(result[2])
end
end
end
@@ -383,7 +388,8 @@ function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(args, threa
else
local file, reason = io.open(shell.resolve(token), mode)
if not file then
- error("could not open '" .. token .. "': " .. reason)
+ io.stderr:write("could not open '" .. token .. "': " .. reason .. "\n")
+ os.exit(1)
end
table.insert(handles, file)
ios[from_io] = file
@@ -428,7 +434,7 @@ end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern)
local segments = text.split(glob_pattern, {"/"}, true)
- local hiddens = tx.select(segments,function(e)return e:match("^%%%.")==nil end)
+ local hiddens = tx.foreach(segments,function(e)return e:match("^%%%.")==nil end)
local function is_visible(s,i)
return not hiddens[i] or s:match("^%.") == nil
end
@@ -812,7 +818,8 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
if not self.redirect[1] and self.closed then
-- if next is dead, ignore all writes
if coroutine.status(self.next) ~= "dead" then
- error("attempt to use a closed stream")
+ io.stderr:write("attempt to use a closed stream\n")
+ os.exit(1)
end
elseif self.redirect[1] then
return self.redirect[1]:write(value)
@@ -823,7 +830,8 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
self:close()
end
if not result[1] then
- error(result[2], 0)
+ io.stderr:write(tostring(result[2]) .. "\n")
+ os.exit(1)
end
return self
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/shell.lua
index c5cefe5679..0b13e5f0a0 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/shell.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/shell.lua
@@ -19,7 +19,7 @@ function shell.getShell()
if shells[shellName] then
return shells[shellName]
end
- local sh, reason = loadfile(shellName, "t", env)
+ local sh, reason = loadfile(shellName, "t")
if sh then
shells[shellName] = sh
end
@@ -175,7 +175,9 @@ function shell.execute(command, env, ...)
if not sh then
return false, reason
end
- local result = table.pack(pcall(sh, env, command, ...))
+ local result = table.pack(coroutine.resume(process.load(function(...)
+ return sh(...)
+ end), env, command, ...))
if not result[1] and type(result[2]) == "table" and result[2].reason == "terminated" then
if result[2].code then
return true
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua
index 5c6b53d3de..b936ac261b 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua
@@ -14,15 +14,46 @@ function term.internal.window()
end
local W = term.internal.window
-
local local_env = {unicode=unicode,event=event,process=process,W=W,kb=kb}
+local gpu_intercept = {}
+local function update_viewport(window, width, height)
+ window = window or W()
+ local gpu = window.gpu
+ if not gpu then return end
+ if not gpu_intercept[gpu] then
+ gpu_intercept[gpu] = {} -- only override a gpu once
+ -- the gpu can change resolution before we get a chance to call events and handle screen_resized
+ -- unfortunately, we have to handle viewport changes by intercept
+ local setr, setv = gpu.setResolution, gpu.setViewport
+ gpu.setResolution = function(...)
+ gpu_intercept[gpu] = {}
+ return setr(...)
+ end
+ gpu.setViewport = function(...)
+ gpu_intercept[gpu] = {}
+ return setv(...)
+ end
+ end
+ if not width and not gpu_intercept[gpu][window] then
+ width, height = gpu.getViewport()
+ end
+ if width then
+ window:resize(width, height)
+ gpu_intercept[gpu][window] = true
+ end
+end
+
+local function resize(window, width, height)
+ window.w, window.h = width, height
+end
+
function term.internal.open(...)
local dx, dy, w, h = ...
- local window = {x=1,y=1,fullscreen=select("#",...)==0,dx=dx or 0,dy=dy or 0,w=w,h=h,blink=true}
+ local window = {x=1,y=1,fullscreen=select("#",...)==0,dx=dx or 0,dy=dy or 0,w=w,h=h,blink=true,resize=resize}
event.listen("screen_resized", function(_,addr,w,h)
if term.isAvailable(window) and term.screen(window) == addr and window.fullscreen then
- window.w,window.h = w,h
+ update_viewport(window, w, h)
end
end)
return window
@@ -30,6 +61,7 @@ end
function term.getViewport(window)
window = window or W()
+ update_viewport(window)
return window.w, window.h, window.dx, window.dy, window.x, window.y
end
@@ -42,8 +74,8 @@ function term.setViewport(w,h,dx,dy,x,y,window)
w,h = w or gw, h or gh
end
- window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh=
- w,h,dx,dy,x,y, gw, gh
+ window.dx,window.dy,window.x,window.y,window.gw,window.gh = dx, dy, x, y, gw, gh
+ update_viewport(window, w, h)
end
function term.gpu(window)
@@ -358,7 +390,13 @@ function term.drawText(value, wrap, window)
local function scroll(_sy,_y)
return _sy + term.internal.scroll(window,_y-h), math.min(_y,h)
end
+ local uptime = computer.uptime
+ local last_sleep = uptime()
while index <= vlen do
+ if uptime() - last_sleep > 4 then
+ os.sleep(0)
+ last_sleep = uptime()
+ end
local si,ei = value:find("[\t\r\n\a]", index)
si = si or vlen+1
if index==si then
@@ -369,7 +407,7 @@ function term.drawText(value, wrap, window)
x,y=1,y+1
sy,y = scroll(sy,y)
elseif delim=="\a" and not beeped then
- require("computer").beep()
+ computer.beep()
beeped = true
end
cr_last = delim == "\r"
@@ -632,21 +670,39 @@ end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] term.internal.tab(input,hints)
if not hints.handler then return end
- if not hints.cache then
- hints.cache = type(hints.handler)=="table" and hints.handler
- or hints.handler(input.data,input.index + 1) or {}
- hints.cache.i=-1
- end
- local c=hints.cache
local main_kb = term.keyboard()
-- term may not have a keyboard
-- in which case, we shouldn't be handling tab events
if not main_kb then
return
end
+ if not hints.cache then
+ local data = hints.handler
+ hints.handler = function(...)
+ if type(data) == "table" then
+ return data
+ else
+ return data(...) or {}
+ end
+ end
+ hints.cache = hints.handler(input.data, input.index + 1)
+ hints.cache.i = -1
+ end
+
+ local cache = hints.cache
+ local cache_size = #cache
+
+ if cache_size == 1 and cache.i == 0 then
+ -- there was only one solution, and the user is asking for the next
+ hints.cache = hints.handler(cache[1], input.index + 1)
+ hints.cache.i = -1
+ cache = hints.cache
+ cache_size = #cache
+ end
+
local change = kb.isShiftDown(main_kb) and -1 or 1
- c.i=(c.i+change)%math.max(#c,1)
- local next=c[c.i+1]
+ cache.i = (cache.i + change) % math.max(#cache, 1)
+ local next = cache[cache.i + 1]
if next then
local tail = unicode.len(input.data) - input.index
input:clear()
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua
index 5ab838d0c7..35278fa9c2 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua
@@ -288,43 +288,58 @@ function --[[@delayloaded-start@]] text.internal.normalize(words, omitQuotes)
return norms
end --[[@delayloaded-end@]]
-function --[[@delayloaded-start@]] text.internal.seeker(handle, whence, to)
- if not handle.txt then
- return nil, "bad file descriptor"
- end
- to = to or 0
- if whence == "cur" then
- handle.offset = handle.offset + to
- elseif whence == "set" then
- handle.offset = to
- elseif whence == "end" then
- handle.offset = handle.len + to
- end
- handle.offset = math.max(0, math.min(handle.offset, handle.len))
- return handle.offset
+function --[[@delayloaded-start@]] text.internal.stream_base(binary)
+ return
+ {
+ binary = binary,
+ plen = binary and string.len or unicode.len,
+ psub = binary and string.sub or unicode.sub,
+ seek = function (handle, whence, to)
+ if not handle.txt then
+ return nil, "bad file descriptor"
+ end
+ to = to or 0
+ local offset = handle:indexbytes()
+ if whence == "cur" then
+ offset = offset + to
+ elseif whence == "set" then
+ offset = to
+ elseif whence == "end" then
+ offset = handle.len + to
+ end
+ offset = math.max(0, math.min(offset, handle.len))
+ handle:byteindex(offset)
+ return offset
+ end,
+ indexbytes = function (handle)
+ return handle.psub(handle.txt, 1, handle.index):len()
+ end,
+ byteindex = function (handle, offset)
+ local sub = string.sub(handle.txt, 1, offset)
+ handle.index = handle.plen(sub)
+ end,
+ }
end --[[@delayloaded-end@]]
-function --[[@delayloaded-start@]] text.internal.reader(txt)
+function --[[@delayloaded-start@]] text.internal.reader(txt, mode)
checkArg(1, txt, "string")
- local reader =
+ local reader = setmetatable(
{
txt = txt,
- len = unicode.len(txt),
- offset = 0,
+ len = string.len(txt),
+ index = 0,
read = function(_, n)
checkArg(1, n, "number")
if not _.txt then
return nil, "bad file descriptor"
end
- if _.offset >= _.len then
+ if _.index >= _.plen(_.txt) then
return nil
end
- local last_offset = _.offset
- _:seek("cur", n)
- local next = unicode.sub(_.txt, last_offset + 1, _.offset)
+ local next = _.psub(_.txt, _.index + 1, _.index + n)
+ _.index = _.index + _.plen(next)
return next
end,
- seek = text.internal.seeker,
close = function(_)
if not _.txt then
return nil, "bad file descriptor"
@@ -332,38 +347,39 @@ function --[[@delayloaded-start@]] text.internal.reader(txt)
_.txt = nil
return true
end,
- }
+ }, {__index=text.internal.stream_base(mode:match("b"))})
return require("buffer").new("r", reader)
end --[[@delayloaded-end@]]
-function --[[@delayloaded-start@]] text.internal.writer(ostream, append_txt)
+function --[[@delayloaded-start@]] text.internal.writer(ostream, mode, append_txt)
if type(ostream) == "table" then
local mt = getmetatable(ostream) or {}
checkArg(1, mt.__call, "function")
end
checkArg(1, ostream, "function", "table")
checkArg(2, append_txt, "string", "nil")
- local writer =
+ local writer = setmetatable(
{
txt = "",
- offset = 0,
+ index = 0, -- last location of write
len = 0,
write = function(_, ...)
if not _.txt then
return nil, "bad file descriptor"
end
- local pre, vs, pos = unicode.sub(_.txt, 1, _.offset), {}, unicode.sub(_.txt, _.offset + 1)
+ local pre = _.psub(_.txt, 1, _.index)
+ local vs = {}
+ local pos = _.psub(_.txt, _.index + 1)
for i,v in ipairs({...}) do
table.insert(vs, v)
end
vs = table.concat(vs)
- _:seek("cur", unicode.len(vs))
+ _.index = _.index + _.plen(vs)
_.txt = pre .. vs .. pos
- _.len = unicode.len(_.txt)
+ _.len = string.len(_.txt)
return true
end,
- seek = text.internal.seeker,
close = function(_)
if not _.txt then
return nil, "bad file descriptor"
@@ -372,7 +388,7 @@ function --[[@delayloaded-start@]] text.internal.writer(ostream, append_txt)
_.txt = nil
return true
end,
- }
+ }, {__index=text.internal.stream_base(mode:match("b"))})
return require("buffer").new("w", writer)
end --[[@delayloaded-end@]]
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/advanced-buffering.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/advanced-buffering.lua
deleted file mode 100644
index b2753784c7..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/advanced-buffering.lua
+++ /dev/null
@@ -1,62 +0,0 @@
-local unicode = require("unicode")
-
---for k in pairs(buffer.reads) do print(k, #buffer.reads[k]) end
-local adv_buf = {}
-
-function adv_buf.readNumber(self, readChunk)
- local len, sub
- if self.mode.b then
- len = rawlen
- sub = string.sub
- else
- len = unicode.len
- sub = unicode.sub
- end
-
- local buffer = ""
- local white_done
-
- local function peek()
- if len(self.bufferRead) == 0 then
- local result, reason = readChunk()
- if not result then
- return result, reason
- end
- end
- return sub(self.bufferRead, 1, 1)
- end
-
- local function pop()
- local n = sub(self.bufferRead, 1, 1)
- self.bufferRead = sub(self.bufferRead, 2)
- return n
- end
-
- local function take()
- buffer = buffer .. pop()
- end
-
- while true do
- local peeked = peek()
- if not peeked then
- break
- end
-
- if peeked:match("[%s]") then
- if white_done then
- break
- end
- pop()
- else
- white_done = true
- if not tonumber(buffer .. peeked .. "0") then
- break
- end
- take() -- add pop to buffer
- end
- end
-
- return tonumber(buffer)
-end
-
-return adv_buf
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua
index 04cb238d2d..27c1095989 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua
@@ -1,7 +1,7 @@
-- called from /init.lua
local raw_loadfile = ...
-_G._OSVERSION = "OpenOS 1.6"
+_G._OSVERSION = "OpenOS 1.6.1"
local component = component
local computer = computer
@@ -83,11 +83,13 @@ status("Initializing package management...")
local package = dofile("/lib/package.lua")
do
- -- Unclutter global namespace now that we have the package module.
+ -- Unclutter global namespace now that we have the package module and a filesystem
_G.component = nil
_G.computer = nil
_G.process = nil
_G.unicode = nil
+ -- Inject the package modules into the global namespace, as in Lua.
+ _G.package = package
-- Initialize the package module with some of our own APIs.
package.loaded.component = component
@@ -96,8 +98,7 @@ do
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
- -- Inject the package and io modules into the global namespace, as in Lua.
- _G.package = package
+ -- Inject the io modules
_G.io = loadfile("/lib/io.lua")()
--mark modules for delay loaded api
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_read.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_read.lua
new file mode 100644
index 0000000000..c419d9970f
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_read.lua
@@ -0,0 +1,162 @@
+local unicode = require("unicode")
+local adv_buf = {}
+
+function adv_buf.readNumber(self, readChunk)
+ local len, sub
+ if self.mode.b then
+ len = rawlen
+ sub = string.sub
+ else
+ len = unicode.len
+ sub = unicode.sub
+ end
+
+ local buffer = ""
+ local white_done
+
+ local function peek()
+ if len(self.bufferRead) == 0 then
+ local result, reason = readChunk(self)
+ if not result then
+ return result, reason
+ end
+ end
+ return sub(self.bufferRead, 1, 1)
+ end
+
+ local function pop()
+ local n = sub(self.bufferRead, 1, 1)
+ self.bufferRead = sub(self.bufferRead, 2)
+ return n
+ end
+
+ local function take()
+ buffer = buffer .. pop()
+ end
+
+ while true do
+ local peeked = peek()
+ if not peeked then
+ break
+ end
+
+ if peeked:match("[%s]") then
+ if white_done then
+ break
+ end
+ pop()
+ else
+ white_done = true
+ if not tonumber(buffer .. peeked .. "0") then
+ break
+ end
+ take() -- add pop to buffer
+ end
+ end
+
+ return tonumber(buffer)
+end
+
+function adv_buf.readBytesOrChars(self, readChunk, n)
+ n = math.max(n, 0)
+ local len, sub
+ if self.mode.b then
+ len = rawlen
+ sub = string.sub
+ else
+ len = unicode.len
+ sub = unicode.sub
+ end
+ local buffer = ""
+ repeat
+ if len(self.bufferRead) == 0 then
+ local result, reason = readChunk(self)
+ if not result then
+ if reason then
+ return nil, reason
+ else -- eof
+ return #buffer > 0 and buffer or nil
+ end
+ end
+ end
+ local left = n - len(buffer)
+ buffer = buffer .. sub(self.bufferRead, 1, left)
+ self.bufferRead = sub(self.bufferRead, left + 1)
+ until len(buffer) == n
+ return buffer
+end
+
+function adv_buf.readAll(self, readChunk)
+ repeat
+ local result, reason = readChunk(self)
+ if not result and reason then
+ return nil, reason
+ end
+ until not result -- eof
+ local result = self.bufferRead
+ self.bufferRead = ""
+ return result
+end
+
+function adv_buf.read(self, readChunk, formats)
+ self.timeout = require("computer").uptime() + self.readTimeout
+ local function read(n, format)
+ if type(format) == "number" then
+ return adv_buf.readBytesOrChars(self, readChunk, format)
+ else
+ local first_char_index = 1
+ if type(format) ~= "string" then
+ error("bad argument #" .. n .. " (invalid option)")
+ elseif unicode.sub(format, 1, 1) == "*" then
+ first_char_index = 2
+ end
+ format = unicode.sub(format, first_char_index, first_char_index)
+ if format == "n" then
+ return adv_buf.readNumber(self, readChunk)
+ elseif format == "l" then
+ return self:readLine(true, self.timeout)
+ elseif format == "L" then
+ return self:readLine(false, self.timeout)
+ elseif format == "a" then
+ return adv_buf.readAll(self, readChunk)
+ else
+ error("bad argument #" .. n .. " (invalid format)")
+ end
+ end
+ end
+
+ local results = {}
+ for i = 1, formats.n do
+ local result, reason = read(i, formats[i])
+ if result then
+ results[i] = result
+ elseif reason then
+ return nil, reason
+ end
+ end
+ return table.unpack(results, 1, formats.n)
+end
+
+function adv_buf.seek(self, whence, offset)
+ whence = tostring(whence or "cur")
+ assert(whence == "set" or whence == "cur" or whence == "end",
+ "bad argument #1 (set, cur or end expected, got " .. whence .. ")")
+ offset = offset or 0
+ checkArg(2, offset, "number")
+ assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
+
+ if self.mode.w or self.mode.a then
+ self:flush()
+ elseif whence == "cur" then
+ offset = offset - #self.bufferRead
+ end
+ local result, reason = self.stream:seek(whence, offset)
+ if result then
+ self.bufferRead = ""
+ return result
+ else
+ return nil, reason
+ end
+end
+
+return adv_buf
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_write.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_write.lua
new file mode 100644
index 0000000000..fab12e99cd
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/buffered_write.lua
@@ -0,0 +1,50 @@
+local unicode = require("unicode")
+local adv_buf = {}
+
+function adv_buf.write(self, arg)
+ local result, reason
+ if self.bufferMode == "full" then
+ if self.bufferSize - #self.bufferWrite < #arg then
+ result, reason = self:flush()
+ if not result then
+ return nil, reason
+ end
+ end
+ if #arg > self.bufferSize then
+ result, reason = self.stream:write(arg)
+ else
+ self.bufferWrite = self.bufferWrite .. arg
+ result = self
+ end
+ else--if self.bufferMode == "line" then
+ local l
+ repeat
+ local idx = arg:find("\n", (l or 0) + 1, true)
+ if idx then
+ l = idx
+ end
+ until not idx
+ if l or #arg > self.bufferSize then
+ result, reason = self:flush()
+ if not result then
+ return nil, reason
+ end
+ end
+ if l then
+ result, reason = self.stream:write(arg:sub(1, l))
+ if not result then
+ return nil, reason
+ end
+ arg = arg:sub(l + 1)
+ end
+ if #arg > self.bufferSize then
+ result, reason = self.stream:write(arg)
+ else
+ self.bufferWrite = self.bufferWrite .. arg
+ result = self
+ end
+ end
+ return result, reason
+end
+
+return adv_buf
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/delayParse.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/delayParse.lua
index 50d89ae382..9f7412f7ee 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/delayParse.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/delayParse.lua
@@ -10,7 +10,7 @@ local delay_end_pattern = "^%s*end%s*%-%-%[%[@delayloaded%-end@%]%]%s*$"
local n,buffer,lib_name,current_method,open = 0,{}
while true do
- local line = file:read("*L")
+ local line = file:readLine(false)
if current_method then
local closed = not line or line:match(delay_end_pattern)
if closed then
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/01_hw.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/01_hw.lua
new file mode 100644
index 0000000000..dd50a6f501
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/01_hw.lua
@@ -0,0 +1,145 @@
+local comp = require("component")
+local fs = require("filesystem")
+local text = require("text")
+
+local dcache = {}
+local pcache = {}
+local adapter_pwd = "/lib/tools/devfs/adapters/"
+
+local adapter_api = {}
+
+function adapter_api.toArgsPack(input, pack)
+ local split = text.split(input, {"%s"}, true)
+ local req = pack[1]
+ local num = #split
+ if num < req then return nil, "insufficient args" end
+ local result = {n=num}
+ for index=1,num do
+ local typename = pack[index+1]
+ local token = split[index]
+ if typename == "boolean" then
+ if token ~= "true" and token ~= "false" then return nil, "bad boolean value" end
+ token = token == "true"
+ elseif typename == "number" then
+ token = tonumber(token)
+ if not token then return nil, "bad number value" end
+ end
+ result[index] = token
+ end
+ return result
+end
+
+function adapter_api.createWriter(callback, ...)
+ local types = table.pack(...)
+ return function(input)
+ local args, why = adapter_api.toArgsPack(input, types)
+ if not args then return why end
+ return callback(table.unpack(args, 1, args.n))
+ end
+end
+
+function adapter_api.create_toggle(read, write, switch)
+ return
+ {
+ read = function() return tostring(read()) end,
+ write = function(value)
+ value = text.trim(tostring(value))
+ local on = value == "1" or value == "true"
+ local off = value == "0" or value == "false"
+ if not on and not off then
+ return nil, "bad value"
+ end
+ if switch then
+ (off and switch or write)()
+ else
+ write(on)
+ end
+ end
+ }
+end
+
+function adapter_api.make_link(list, addr, prefix, bOmitZero)
+ prefix = prefix or ""
+ local zero = bOmitZero and "" or "0"
+ local id = 0
+ local name
+ repeat
+ name = string.format("%s%s", prefix, id == 0 and zero or tostring(id))
+ id = id + 1
+ until not list[name]
+ list[name] = {link=addr}
+end
+
+return
+{
+ components =
+ {
+ list = function()
+ local dirs = {}
+ local types = {}
+ local labels = {}
+ local ads = {}
+
+ dirs["by-type"] = {list=function()return types end}
+ dirs["by-label"] = {list=function()return labels end}
+ dirs["by-address"] = {list=function()return ads end}
+
+ -- first sort the addr, primaries first, then sorted by address lexigraphically
+ local hw_addresses = {}
+ for addr,type in comp.list() do
+ table.insert(hw_addresses, {addr,type})
+ end
+
+ table.sort(hw_addresses, function(a, b)
+ local aaddr, atype = table.unpack(a)
+ local baddr, btype = table.unpack(b)
+
+ if atype == btype then
+ local aprim = comp.isPrimary(aaddr)
+ local bprim = comp.isPrimary(baddr)
+ if aprim then return true end
+ if bprim then return false end
+ end
+
+ return aaddr < baddr
+ end)
+
+ for _,pair in ipairs(hw_addresses) do
+ local addr, type = table.unpack(pair)
+ if not dcache[type] then
+ local adapter_file = adapter_pwd .. type .. ".lua"
+ local loader = loadfile(adapter_file, "bt", _G)
+ dcache[type] = loader and loader(adapter_api)
+ end
+ local adapter = dcache[type]
+ if adapter then
+ local proxy = pcache[addr] or comp.proxy(addr)
+ pcache[addr] = proxy
+ ads[addr] =
+ {
+ list = function()
+ local devfs_proxy = adapter(proxy)
+ devfs_proxy.address = {proxy.address}
+ devfs_proxy.slot = {proxy.slot}
+ devfs_proxy.type = {proxy.type}
+ devfs_proxy.device = {device=proxy}
+ return devfs_proxy
+ end
+ }
+
+ -- by type building
+ local type_dir = types[type] or {list={}}
+ adapter_api.make_link(type_dir.list, "../../by-address/"..addr)
+ types[type] = type_dir
+
+ -- by label building (labels are only supported in filesystems
+ local label = require("devfs").getDeviceLabel(proxy)
+ if label then
+ adapter_api.make_link(labels, "../by-address/"..addr, label, true)
+ end
+ end
+ end
+ return dirs
+ end
+ },
+}
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/02_utils.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/02_utils.lua
new file mode 100644
index 0000000000..a34c49abc4
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/02_utils.lua
@@ -0,0 +1,50 @@
+return
+{
+ eeprom =
+ {
+ link = "components/by-type/eeprom/0/contents",
+ isAvailable = function()
+ local comp = require("component")
+ return comp.list("eeprom")()
+ end
+ },
+ ["eeprom-data"] =
+ {
+ link = "components/by-type/eeprom/0/data",
+ isAvailable = function()
+ local comp = require("component")
+ return comp.list("eeprom")()
+ end
+ },
+ null =
+ {
+ open = function(mode)
+ return
+ {
+ read = function() end,
+ write = function() end
+ }
+ end
+ },
+ random =
+ {
+ open = function(mode)
+ if mode and not mode:match("r") then
+ return nil, "read only"
+ end
+ return
+ {
+ read = function(self, n)
+ local chars = {}
+ for i=1,n do
+ table.insert(chars,string.char(math.random(0,255)))
+ end
+ return table.concat(chars)
+ end
+ }
+ end,
+ size = function()
+ return math.huge
+ end
+ },
+}
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/computer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/computer.lua
new file mode 100644
index 0000000000..166dc7d0ad
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/computer.lua
@@ -0,0 +1,9 @@
+local adapter_api = ...
+
+return function(proxy)
+ return
+ {
+ beep = {write=adapter_api.createWriter(proxy.beep, 0, "number", "number")},
+ running = adapter_api.create_toggle(proxy.isRunning, proxy.start, proxy.stop),
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/eeprom.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/eeprom.lua
new file mode 100644
index 0000000000..a34b439dba
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/eeprom.lua
@@ -0,0 +1,22 @@
+local cache = {}
+local function cload(callback)
+ local c = cache[callback]
+ if not c then
+ c = callback()
+ cache[callback] = c
+ end
+ return c
+end
+
+return function(proxy)
+ return
+ {
+ contents = {read=proxy.get, write=proxy.set},
+ data = {read=proxy.getData, write=proxy.setData},
+ checksum = {read=proxy.getChecksum,size=function() return 8 end},
+ size = {cload(proxy.getSize)},
+ dataSize = {cload(proxy.getDataSize)},
+ label = {write=proxy.setLabel,proxy.getLabel()},
+ makeReadonly = {write=proxy.makeReadonly}
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/filesystem.lua
new file mode 100644
index 0000000000..163d44eb60
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/filesystem.lua
@@ -0,0 +1,25 @@
+local fs = require("filesystem")
+local text = require("text")
+
+return function(proxy)
+ return
+ {
+ ["label"] =
+ {
+ read = function() return proxy.getLabel() or "" end,
+ write= function(v) proxy.setLabel(text.trim(v)) end
+ },
+ ["isReadOnly"] = {proxy.isReadOnly()},
+ ["spaceUsed"] = {proxy.spaceUsed()},
+ ["spaceTotal"] = {proxy.spaceTotal()},
+ ["mounts"] = {read = function()
+ local mounts = {}
+ for mproxy,mpath in fs.mounts() do
+ if mproxy.address == proxy.address then
+ table.insert(mounts, mpath)
+ end
+ end
+ return table.concat(mounts, "\n")
+ end}
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/gpu.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/gpu.lua
new file mode 100644
index 0000000000..9e8e6cf520
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/gpu.lua
@@ -0,0 +1,15 @@
+local adapter_api = ...
+
+return function(proxy)
+ return
+ {
+ viewport = {write = adapter_api.createWriter(proxy.setViewport, 2, "number", "number"), proxy.getViewport()},
+ resolution = {write = adapter_api.createWriter(proxy.setResolution, 2, "number", "number"), proxy.getResolution()},
+ maxResolution = {proxy.maxResolution()},
+ screen = {link="../"..proxy.getScreen(),isAvailable=proxy.getScreen},
+ depth = {write = adapter_api.createWriter(proxy.setDepth, 1, "number"), proxy.getDepth()},
+ maxDepth = {proxy.maxDepth()},
+ background = {write = adapter_api.createWriter(proxy.setBackground, 1, "number", "boolean"), proxy.getBackground()},
+ foreground = {write = adapter_api.createWriter(proxy.setForeground, 1, "number", "boolean"), proxy.getForeground()},
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/internet.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/internet.lua
new file mode 100644
index 0000000000..95c3f74e1f
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/internet.lua
@@ -0,0 +1,7 @@
+return function(proxy)
+ return
+ {
+ httpEnabled = {proxy.isHttpEnabled()},
+ tcpEnabled = {proxy.isTcpEnabled()},
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/modem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/modem.lua
new file mode 100644
index 0000000000..a9327bd353
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/modem.lua
@@ -0,0 +1,12 @@
+return function(proxy)
+ return
+ {
+ wakeMessage =
+ {
+ read = function() return proxy.getWakeMessage() or "" end,
+ write= function(msg) return proxy.setWakeMessage(msg) end,
+ },
+ wireless = {proxy.isWireless()},
+ maxPacketSize = {proxy.maxPacketSize()},
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/screen.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/screen.lua
new file mode 100644
index 0000000000..2c9ad67134
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/adapters/screen.lua
@@ -0,0 +1,18 @@
+local adapter_api = ...
+
+return function(proxy)
+ return
+ {
+ ["aspectRatio"] = {proxy.getAspectRatio()},
+ ["keyboards"] = {read=function()
+ local ks = {}
+ for _,ka in ipairs(proxy.getKeyboards()) do
+ table.insert(ks, ka)
+ end
+ return table.concat(ks, "\n")
+ end},
+ ["on"] = adapter_api.create_toggle(proxy.isOn, proxy.turnOn, proxy.turnOff), -- turnOn and turnOff
+ ["precise"] = adapter_api.create_toggle(proxy.isPrecise, proxy.setPrecise),
+ ["touchModeInverted"] = adapter_api.create_toggle(proxy.isTouchModeInverted, proxy.setTouchModeInverted),
+ }
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua
deleted file mode 100644
index f1980408a8..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua
+++ /dev/null
@@ -1,10 +0,0 @@
-local comp = require("component")
-local devfs = ...
-
--- eeprom get/set has to delayed because comp.eeprom may not be available
-local node = devfs.new_callback_proxy(function() return comp.eeprom.getData() end, function(...) comp.eeprom.setData(...) end)
-function node.isAvailable()
- return comp.list("eeprom", true)()
-end
-
-return node
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua
deleted file mode 100644
index fbf208f13c..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua
+++ /dev/null
@@ -1,10 +0,0 @@
-local comp = require("component")
-local devfs = ...
-
--- eeprom get/set has to delayed because comp.eeprom may not be available
-local node = devfs.new_callback_proxy(function() return comp.eeprom.get() end, function(...) comp.eeprom.set(...) end)
-function node.isAvailable()
- return comp.list("eeprom", true)()
-end
-
-return node
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/null.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/null.lua
deleted file mode 100644
index 6d46610ef5..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/null.lua
+++ /dev/null
@@ -1,10 +0,0 @@
-return
-{
- open = function(mode)
- return
- {
- read = function() end,
- write = function() end
- }
- end
-}
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/random.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/random.lua
deleted file mode 100644
index 15c08617a6..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/random.lua
+++ /dev/null
@@ -1,21 +0,0 @@
-return
-{
- open = function(mode)
- if mode and not mode:match("r") then
- return nil, "read only"
- end
- return
- {
- read = function(self, n)
- local chars = {}
- for i=1,n do
- table.insert(chars,string.char(math.random(0,255)))
- end
- return table.concat(chars)
- end
- }
- end,
- size = function()
- return math.huge
- end
-}
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/device_labeling.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/device_labeling.lua
new file mode 100644
index 0000000000..207778c597
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/device_labeling.lua
@@ -0,0 +1,101 @@
+local fs = require("filesystem")
+
+local lib = {}
+
+local rules_path = "/etc/udev/rules.d/"
+local auto_rules = "autogenerated.lua"
+
+local function fs_key(dir, filename)
+ local long_name = dir .. '/' .. filename
+ local segments = fs.segments(long_name)
+ local result = '/' .. table.concat(segments, '/')
+ return result
+end
+
+function lib.loadRules(root_dir)
+ checkArg(1, root_dir, "string", "nil")
+ root_dir = (root_dir or rules_path)
+ lib.rules = {}
+ lib.rules[fs_key(root_dir, auto_rules)] = {}
+
+ for file in fs.list(root_dir) do
+ if file:match("%.lua$") then
+ local path = fs_key(root_dir, file)
+ local file = io.open(path)
+ if file then
+ local load_rule = load("return {" .. file:read("*a") .. "}")
+ file:close()
+ if load_rule then
+ local ok, rule = pcall(load_rule)
+ if ok and type(rule) == "table" then
+ local irule = {}
+ lib.rules[path] = irule
+ for _,v in ipairs(rule) do
+ if type(v) == "table" then
+ table.insert(irule, v)
+ end
+ -- else invalid rule
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function lib.saveRule(rule_set, path)
+ checkArg(1, rule_set, "table")
+ checkArg(2, path, "string")
+ local file = io.open(path, "w")
+ if not file then return end -- fs may be read only, totally fine, this just won't persist
+ for index, irule in ipairs(rule_set) do
+ file:write(require("serialization").serialize(irule), ",\n")
+ end
+ file:close()
+end
+
+function lib.saveRules()
+ for path, rule_set in pairs(rules) do
+ lib.saveRule(rule_set, path)
+ end
+end
+
+local function getIRule(proxy)
+ checkArg(1, proxy, "table")
+ for path,rule_set in pairs(lib.rules) do
+ for index, irule in ipairs(rule_set) do
+ if irule.address == proxy.address then
+ return irule, index, rule_set, path
+ end
+ end
+ end
+end
+
+function lib.getDeviceLabel(proxy)
+ local irule = getIRule(proxy)
+ if irule and irule.label then
+ return irule.label
+ elseif proxy.getLabel then
+ return proxy.getLabel()
+ end
+end
+
+function lib.setDeviceLabel(proxy, label)
+ local irule, index, rule_set, path = getIRule(proxy)
+ if not irule then
+ -- if the device supports labels, use it instead
+ if proxy.setLabel then
+ return proxy.setLabel(label)
+ end
+ path = fs_key(rules_path, auto_rules)
+ rule_set = lib.rules[path]
+ index = #rule_set + 1
+ irule = {address=proxy.address}
+ table.insert(rule_set, irule)
+ end
+ irule.label = label
+ lib.saveRule(rule_set, path)
+end
+
+return lib
+
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua
new file mode 100644
index 0000000000..b59a314f83
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua
@@ -0,0 +1,68 @@
+local filesystem = require("filesystem")
+
+local lib = {}
+function lib.remove(path, findNode)
+ local function removeVirtual()
+ local node, rest, vnode, vrest = findNode(filesystem.path(path), false, true)
+ -- vrest represents the remaining path beyond vnode
+ -- vrest is nil if vnode reaches the full path
+ -- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
+ if not vrest then
+ local name = filesystem.name(path)
+ if vnode.children[name] or vnode.links[name] then
+ vnode.children[name] = nil
+ vnode.links[name] = nil
+ while vnode and vnode.parent and not vnode.fs and not next(vnode.children) and not next(vnode.links) do
+ vnode.parent.children[vnode.name] = nil
+ vnode = vnode.parent
+ end
+ return true
+ end
+ end
+ -- return false even if vrest is nil because this means it was a expected
+ -- to be a real file
+ return false
+ end
+ local function removePhysical()
+ node, rest = findNode(path)
+ if node.fs and rest then
+ return node.fs.remove(rest)
+ end
+ return false
+ end
+ local success = removeVirtual()
+ success = removePhysical() or success -- Always run.
+ if success then return true
+ else return nil, "no such file or directory"
+ end
+end
+
+function lib.rename(oldPath, newPath, findNode)
+ if filesystem.isLink(oldPath) then
+ local node, rest, vnode, vrest = findNode(filesystem.path(oldPath))
+ local target = vnode.links[filesystem.name(oldPath)]
+ local result, reason = filesystem.link(target, newPath)
+ if result then
+ filesystem.remove(oldPath)
+ end
+ return result, reason
+ else
+ local oldNode, oldRest = findNode(oldPath)
+ local newNode, newRest = findNode(newPath)
+ if oldNode.fs and oldRest and newNode.fs and newRest then
+ if oldNode.fs.address == newNode.fs.address then
+ return oldNode.fs.rename(oldRest, newRest)
+ else
+ local result, reason = filesystem.copy(oldPath, newPath)
+ if result then
+ return filesystem.remove(oldPath)
+ else
+ return nil, reason
+ end
+ end
+ end
+ return nil, "trying to read from or write to virtual directory"
+ end
+end
+
+return lib
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua
deleted file mode 100644
index 6142a45376..0000000000
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua
+++ /dev/null
@@ -1,284 +0,0 @@
-local fs = require("filesystem")
-local shell = require("shell")
-local term = require("term")
-
-local dirsArg, ops = shell.parse(...)
-if #dirsArg == 0 then
- table.insert(dirsArg, ".")
-end
-
-local ec = 0
-local gpu = term.gpu()
-local fOut = term.isAvailable() and io.output().tty
-local function perr(msg) io.stderr:write(msg,"\n") ec = 2 end
-local function _path(n,i) return n[i]:sub(1, 1) == '/' and "" or n.path end
-local function _name(n,i) return ops.p and n[i] or n[i]:gsub("/+$", "") end
-local function _sort_name(n,i) return _name(n,i):gsub("^%.","") end
-local function _fullPath(n,i) return fs.concat(_path(n,i),_name(n,i)) end
-local function _isLink(n,i) return (fs.isLink(_fullPath(n,i))) end
-local function _linkPath(n,i) return select(2,fs.isLink(_fullPath(n,i))) end
-local function _isDir(n,i) return fs.isDirectory(_fullPath(n,i)) end
-local function _size(n,i) return _isLink(n,i) and 0 or fs.size(_fullPath(n,i)) end
-local function _time(n,i) return fs.lastModified(_fullPath(n,i)) end
-local function _ext(n,i) return _name(n,i):match("(%.[^.]+)$") or "" end
-local function toArray(i) local r={} for n in i do r[#r+1]=n end return r end
-local restore_color = function() end
-local set_color = function() end
-local prev_color
-local function colorize(n,i) return prev_color end
-if fOut and not ops["no-color"] then
- local LSC = os.getenv("LS_COLORS")
- if type(LSC) == "string" then
- LSC = require("serialization").unserialize(LSC)
- end
- if not LSC then
- perr("ls: unparsable value for LS_COLORS environment variable")
- else
- prev_color = gpu.getForeground()
- restore_color = function() gpu.setForeground(prev_color) end
- colorize=function(n,i) return
- _isLink(n,i) and LSC.LINK or
- _isDir(n,i) and LSC.DIR or
- LSC['*'.._ext(n,i)] or LSC.FILE or prev_color
- end
- set_color=function(c)
- if gpu.getForeground() ~= c then
- io.stdout:flush()
- gpu.setForeground(c)
- end
- end
- end
-end
-local msft={reports=0,proxies={}}
-function msft.report(files, dirs, used, proxy)
- local free = proxy.spaceTotal() - proxy.spaceUsed()
- restore_color()
- local pattern = "%5i File(s) %11i bytes\n%5i Dir(s) %11s bytes free\n"
- io.write(string.format(pattern, files, used, dirs, tostring(free)))
-end
-function msft.tail(n)
- local x = fs.get(n.path)
- if not x then return end
- local u,f,d=0,0,0
- for i=1,#n do
- if _isDir(n,i) then d=d+1
- else f=f+1;u=u+_size(n,i) end
- end
- msft.report(f,d,u,x)
- local ps=msft.proxies
- ps[x]=ps[x]or{files=0,dirs=0,used=0}
- local p=ps[x]
- p.files=p.files+f
- p.dirs=p.dirs+d
- p.used=p.used+u
- msft.reports=msft.reports+1
-end
-function msft.final()
- if msft.reports < 2 then return end
- local g = {}
- for p,r in pairs(msft.proxies) do g[#g+1]={proxy=p,report=r} end
- restore_color()
- print("Total Files Listed:")
- for _,p in ipairs(g) do
- if #g>1 then print("As pertaining to: "..p.proxy.address) end
- msft.report(p.report.files, p.report.dirs, p.report.used, p.proxy)
- end
-end
-
-if not ops.M then
- msft.tail=function()end
- msft.final=function()end
-end
-
-local function nod(n)
- return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0"
-end
-
-local function formatSize(size)
- if not ops.h and not ops['human-readable'] and not ops.si then
- return tostring(size)
- end
- local sizes = {"", "K", "M", "G"}
- local unit = 1
- local power = ops.si and 1000 or 1024
- while size > power and unit < #sizes do
- unit = unit + 1
- size = size / power
- end
- return nod(math.floor(size*10)/10)..sizes[unit]
-end
-
-local function pad(txt)
- txt = tostring(txt)
- return #txt >= 2 and txt or "0"..txt
-end
-local day_names={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday",
- "Saturday"}
-local month_names={"January","February","March","April","May","June","July",
- "August","September","October","November","December"}
-local function formatDate(epochms)
- local d = os.date("*t", epochms)
- return ops['full-time'] and
- string.format("%s-%s-%s %s:%s:%s",d.year,pad(nod(d.month)),pad(nod(d.day)),pad(nod(d.hour)),pad(nod(d.min)),pad(nod(d.sec))) or
- string.format("%s %+2s %+2s:%+2s",month_names[d.month]:sub(1,3),nod(d.day),pad(nod(d.hour)),pad(nod(d.min)))
-end
-local function filter(n)
- if ops.a then return n end
- local s = {path=n.path}
- for i,x in ipairs(n) do
- if fs.name(_name(n,i)):sub(1,1) ~= "." then s[#s+1]=x end
- end
- return s
-end
-local function sort(n)
- local once=false
- local function ni(v) for i=1,#n do if n[i]==v then return i end end end
- local function sorter(_fp)
- once=true table.sort(n,function(a,b)return _fp(n,ni(a))>_fp(n,ni(b))end)
- end
- local rev = ops.r or ops.reverse
- if ops.t then sorter(_time) end
- if ops.X then sorter(_ext) end
- if ops.S then sorter(_size) end
- if not once then sorter(_sort_name) rev=not rev end
- if rev then
- for i=1,#n/2 do n[i],n[#n-i+1]=n[#n-i+1],n[i] end
- end
- return n
-end
-local function dig(n, dirs, dir)
- if ops.R then
- local di = 1
- for i=1,#n do
- if _isDir(n,i) then
- local p=dir..(dir:sub(-1) == "/" and "" or "/")
- table.insert(dirs,di,p.._name(n,i))
- di=di+1
- end
- end
- end
- return n
-end
-local function wide(n,i)
- local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f'
- local link_target = _isLink(n,i) and
- string.format(" -> %s", _linkPath(n, i):gsub("/+$", "") .. (_isDir(n, i) and "/" or "")) or ""
- local w = fs.get(_fullPath(n,i)).isReadOnly() and '-' or 'w'
- local size = formatSize(_size(n,i))
- local modDate = formatDate(_time(n,i))
- return string.format("%s-r%s %+7s %s ",t,w,size,modDate),_name(n,i)..link_target
-end
-
-local first_display = true
-local function display(n)
- local mt={}
- local lines=setmetatable({},mt)
- if ops.l then
- lines.n=#n
- mt.__index=function(tbl,index)local m,l=wide(n,index)return{{color=prev_color,name=m},{color=colorize(n,index),name=l}}end
- elseif ops["1"] or not fOut then
- lines.n=#n
- mt.__index=function(tbl,index)local m,l=wide(n,index)return{{color=colorize(n,index),name=_name(n,index)}}end
- else -- columns
- local cols,d,w=0,0,select(3,term.getGlobalArea())-1
- local function real(x, y)
- local index = y + ((x-1) * d)
- return index <= #n and index or nil
- end
- local function max_name(ci)
- local max=0 -- return the width of the max element in ci
- for r=1,d do
- local ri=real(ci,r)
- if not ri then break end
- max=math.max(max,_name(n,ri):len())
- end
- return max
- end
- local function measure(_cols)
- local t=0
- for c=1,_cols do t=t+max_name(c)+(c>1 and 2 or 0) end
- return t
- end
- while d<#n do d=d+1 cols=math.ceil(#n/d) if measure(cols) 1 or ops.R then
- header = function(path)
- if not first_display then print() end
- restore_color()
- io.write(path,":\n")
- end
-end
-local function splitDirsFromFileArgs(dirs)
- local trimmed = {}
- local files = {}
- for _,dir in ipairs(dirs) do
- local path = shell.resolve(dir)
- if not fs.exists(path) then
- perr("cannot access " .. tostring(path) .. ": No such file or directory")
- elseif fs.isDirectory(path) then
- table.insert(trimmed, dir)
- else -- file or link
- table.insert(files, dir)
- end
- end
- return files, trimmed
-end
-local function displayDirList(dirs)
- while #dirs > 0 do
- local dir = table.remove(dirs, 1)
- header(dir)
- local path = shell.resolve(dir)
- local list, reason = fs.list(path)
- if not list then
- perr(reason)
- else
- local n=toArray(list)
- n.path=path
- display(dig(sort(filter(n)),dirs,dir))
- end
- end
-end
-local tr,cp={},{path=shell.getWorkingDirectory()}
-for _,dir in ipairs(dirsArg) do
- local path = shell.resolve(dir)
- if not fs.exists(path) then
- perr("cannot access " .. tostring(path) .. ": No such file or directory")
- elseif fs.isDirectory(path) then
- tr[#tr+1]=dir
- else -- file or link
- cp[#cp+1]=dir
- end
-end
-io.output():setvbuf("line")
-if #cp > 0 then display(sort(cp)) end
-displayDirList(tr)
-msft.final()
-io.output():flush()
-io.output():setvbuf("no")
-restore_color()
-return ec
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full_ls.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full_ls.lua
new file mode 100644
index 0000000000..576e6bbc75
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full_ls.lua
@@ -0,0 +1,391 @@
+local fs = require("filesystem")
+local shell = require("shell")
+local term = require("term")
+local unicode = require("unicode")
+
+local dirsArg, ops = shell.parse(...)
+
+if ops.help then
+ print([[Usage: ls [OPTION]... [FILE]...
+ -a, --all do not ignore entries starting with .
+ --full-time with -l, print time in full iso format
+ -h, --human-readable with -l and/or -s, print human readable sizes
+ --si likewise, but use powers of 1000 not 1024
+ -l use a long listing format
+ -r, --reverse reverse order while sorting
+ -R, --recursive list subdirectories recursively
+ -S sort by file size
+ -t sort by modification time, newest first
+ -X sort alphabetically by entry extension
+ -1 list one file per line
+ -p append / indicator to directories
+ -M display Microsoft-style file and directory
+ count after listing
+ --no-color Do not colorize the output (default colorized)
+ --help display this help and exit
+For more info run: man ls]])
+ return 0
+end
+
+if #dirsArg == 0 then
+ table.insert(dirsArg, ".")
+end
+
+local ec = 0
+local gpu = term.gpu()
+local fOut = term.isAvailable() and io.output().tty
+local function perr(msg) io.stderr:write(msg,"\n") ec = 2 end
+local function stat(names, index)
+ local name = names[index]
+ if type(name) == "table" then
+ return name
+ end
+ local info = {}
+ info.key = name
+ info.path = name:sub(1, 1) == "/" and "" or names.path
+ info.name = ops.p and name or name:gsub("/+$", "")
+ info.sort_name = info.name:gsub("^%.","")
+ info.full_path = fs.concat(info.path, info.name)
+ info.isLink, info.link = fs.isLink(info.full_path)
+ info.isDir = fs.isDirectory(info.full_path)
+ info.size = info.isLink and 0 or fs.size(info.full_path)
+ info.time = fs.lastModified(info.full_path)
+ info.fs = fs.get(info.full_path)
+ info.ext = info.name:match("(%.[^.]+)$") or ""
+ names[index] = info
+ return info
+end
+local function toArray(i) local r={} for n in i do r[#r+1]=n end return r end
+local restore_color = function() end
+local set_color = function() end
+local prev_color
+local function colorize() return prev_color end
+if fOut and not ops["no-color"] then
+ local LSC = os.getenv("LS_COLORS")
+ if type(LSC) == "string" then
+ LSC = require("serialization").unserialize(LSC)
+ end
+ if not LSC then
+ perr("ls: unparsable value for LS_COLORS environment variable")
+ else
+ prev_color = gpu.getForeground()
+ restore_color = function() gpu.setForeground(prev_color) end
+ colorize = function(info)
+ return
+ info.isLink and LSC.LINK or
+ info.isDir and LSC.DIR or
+ LSC['*'..info.ext] or
+ LSC.FILE or
+ prev_color
+ end
+ set_color=function(c)
+ if gpu.getForeground() ~= c then
+ io.stdout:flush()
+ gpu.setForeground(c)
+ end
+ end
+ end
+end
+local msft={reports=0,proxies={}}
+function msft.report(files, dirs, used, proxy)
+ local free = proxy.spaceTotal() - proxy.spaceUsed()
+ restore_color()
+ local pattern = "%5i File(s) %11i bytes\n%5i Dir(s) %11s bytes free\n"
+ io.write(string.format(pattern, files, used, dirs, tostring(free)))
+end
+function msft.tail(names)
+ local fsproxy = fs.get(names.path)
+ if not fsproxy then
+ return
+ end
+ local totalSize, totalFiles, totalDirs = 0, 0, 0
+ for i=1,#names do
+ local info = stat(names, i)
+ if info.isDir then
+ totalDirs = totalDirs + 1
+ else
+ totalFiles = totalFiles + 1
+ end
+ totalSize = totalSize + info.size
+ end
+ msft.report(totalFiles, totalDirs, totalSize, fsproxy)
+ local ps = msft.proxies
+ ps[fsproxy] = ps[fsproxy] or {files=0,dirs=0,used=0}
+ local p = ps[fsproxy]
+ p.files = p.files + totalFiles
+ p.dirs = p.dirs + totalDirs
+ p.used = p.used + totalSize
+ msft.reports = msft.reports + 1
+end
+function msft.final()
+ if msft.reports < 2 then return end
+ local groups = {}
+ for proxy,report in pairs(msft.proxies) do
+ table.insert(groups, {proxy=proxy,report=report})
+ end
+ restore_color()
+ print("Total Files Listed:")
+ for _,pair in ipairs(groups) do
+ local proxy, report = pair.proxy, pair.report
+ if #groups>1 then
+ print("As pertaining to: "..proxy.address)
+ end
+ msft.report(report.files, report.dirs, report.used, proxy)
+ end
+end
+
+if not ops.M then
+ msft.tail=function()end
+ msft.final=function()end
+end
+
+local function nod(n)
+ return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0"
+end
+
+local function formatSize(size)
+ if not ops.h and not ops['human-readable'] and not ops.si then
+ return tostring(size)
+ end
+ local sizes = {"", "K", "M", "G"}
+ local unit = 1
+ local power = ops.si and 1000 or 1024
+ while size > power and unit < #sizes do
+ unit = unit + 1
+ size = size / power
+ end
+ return nod(math.floor(size*10)/10)..sizes[unit]
+end
+
+local function pad(txt)
+ txt = tostring(txt)
+ return #txt >= 2 and txt or "0"..txt
+end
+
+local function formatDate(epochms)
+ local day_names={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
+ local month_names={"January","February","March","April","May","June","July","August","September","October","November","December"}
+ if epochms == 0 then return "" end
+ local d = os.date("*t", epochms)
+ local day, hour, min, sec = nod(d.day), pad(nod(d.hour)), pad(nod(d.min)), pad(nod(d.sec))
+ if ops["full-time"] then
+ return string.format("%s-%s-%s %s:%s:%s ", d.year, pad(nod(d.month)), pad(day), hour, min, sec)
+ else
+ return string.format("%s %+2s %+2s:%+2s ", month_names[d.month]:sub(1,3), day, hour, pad(min))
+ end
+end
+
+local function filter(names)
+ if ops.a then
+ return names
+ end
+ local set = {}
+ for key, value in pairs(names) do
+ if type(key) == "number" then
+ local info = stat(names, key)
+ if fs.name(info.name):sub(1, 1) ~= "." then
+ table.insert(set, names[key])
+ end
+ else
+ set[key] = value
+ end
+ end
+ return set
+end
+
+local function sort(names)
+ local once = false
+ local function ni(v)
+ local vname = type(v) == "string" and v or v.key
+ for i=1,#names do
+ local info = stat(names, i)
+ if info.key == vname then
+ return i
+ end
+ end
+ end
+ local function sorter(key)
+ once = true
+ table.sort(names, function(a, b)
+ local ast = stat(names, ni(a))
+ local bst = stat(names, ni(b))
+ return ast[key] > bst[key]
+ end)
+ end
+ if ops.t then sorter("time") end
+ if ops.X then sorter("ext") end
+ if ops.S then sorter("size") end
+ local rev = ops.r or ops.reverse
+ if not once then sorter("sort_name") rev=not rev end
+ if rev then
+ for i=1,#names/2 do
+ names[i], names[#names - i + 1] = names[#names - i + 1], names[i]
+ end
+ end
+ return names
+end
+
+local function dig(names, dirs, dir)
+ if ops.R then
+ local di = 1
+ for i=1,#names do
+ local info = stat(names, i)
+ if info.isDir then
+ local path = dir..(dir:sub(-1) == "/" and "" or "/")
+ table.insert(dirs, di, path..info.name)
+ di = di + 1
+ end
+ end
+ end
+ return names
+end
+
+local first_display = true
+local function display(names)
+ local mt={}
+ local lines = setmetatable({}, mt)
+ if ops.l then
+ lines.n = #names
+ local max_size_width = 1
+ local max_date_width = 0
+ for i=1,lines.n do
+ local info = stat(names, i)
+ max_size_width = math.max(max_size_width, formatSize(info.size):len())
+ max_date_width = math.max(max_date_width, formatDate(info.time):len())
+ end
+ mt.__index = function(tbl, index)
+ local info = stat(names, index)
+ local file_type = info.isLink and 'l' or info.isDir and 'd' or 'f'
+ local link_target = info.isLink and string.format(" -> %s", info.link:gsub("/+$", "") .. (info.isDir and "/" or "")) or ""
+ local write_mode = info.fs.isReadOnly() and '-' or 'w'
+ local size = formatSize(info.size)
+ local modDate = formatDate(info.time)
+ local format = "%s-r%s %+"..tostring(max_size_width).."s %"..tostring(max_date_width).."s"
+ local meta = string.format(format, file_type, write_mode, size, modDate)
+ local item = info.name..link_target
+ return {{color = prev_color, name = meta}, {color = colorize(info), name = item}}
+ end
+ elseif ops["1"] or not fOut then
+ lines.n = #names
+ mt.__index = function(tbl, index)
+ local info = stat(names, index)
+ return {{color = colorize(info), name = info.name}}
+ end
+ else -- columns
+ local num_columns, items_per_column, width = 0, 0, term.getViewport() - 1
+ local function real(x, y)
+ local index = y + ((x-1) * items_per_column)
+ return index <= #names and index or nil
+ end
+ local function max_name(column_index)
+ local max = 0 -- return the width of the max element in column_index
+ for r=1,items_per_column do
+ local ri = real(column_index, r)
+ if not ri then break end
+ local info = stat(names, ri)
+ max = math.max(max, unicode.wlen(info.name))
+ end
+ return max
+ end
+ local function measure()
+ local total = 0
+ for column_index=1,num_columns do
+ total = total + max_name(column_index) + (column_index > 1 and 2 or 0)
+ end
+ return total
+ end
+ while items_per_column<#names do
+ items_per_column = items_per_column + 1
+ num_columns = math.ceil(#names/items_per_column)
+ if measure() < width then
+ break
+ end
+ end
+ lines.n = items_per_column
+ mt.__index=function(tbl, line_index)
+ return setmetatable({},{
+ __len=function()return num_columns end,
+ __index=function(tbl, column_index)
+ local ri = real(column_index, line_index)
+ if not ri then return end
+ local info = stat(names, ri)
+ local name = info.name
+ return {color = colorize(info), name = name .. string.rep(' ', max_name(column_index) - unicode.wlen(name) + (column_index < num_columns and 2 or 0))}
+ end,
+ })
+ end
+ end
+ for line_index=1,lines.n do
+ local line = lines[line_index]
+ for element_index=1,#line do
+ local e = line[element_index]
+ if not e then break end
+ first_display = false
+ set_color(e.color)
+ io.write(e.name)
+ end
+ print()
+ end
+ msft.tail(names)
+end
+local header = function() end
+if #dirsArg > 1 or ops.R then
+ header = function(path)
+ if not first_display then print() end
+ restore_color()
+ io.write(path,":\n")
+ end
+end
+local function splitDirsFromFileArgs(dirs)
+ local trimmed = {}
+ local files = {}
+ for _,dir in ipairs(dirs) do
+ local path = shell.resolve(dir)
+ if not fs.exists(path) then
+ perr("cannot access " .. tostring(path) .. ": No such file or directory")
+ elseif fs.isDirectory(path) then
+ table.insert(trimmed, dir)
+ else -- file or link
+ table.insert(files, dir)
+ end
+ end
+ return files, trimmed
+end
+local function displayDirList(dirs)
+ while #dirs > 0 do
+ local dir = table.remove(dirs, 1)
+ header(dir)
+ local path = shell.resolve(dir)
+ local list, reason = fs.list(path)
+ if not list then
+ perr(reason)
+ else
+ local names = toArray(list)
+ names.path = path
+ display(dig(sort(filter(names)), dirs, dir))
+ end
+ end
+end
+local dir_set, file_set = {}, {path=shell.getWorkingDirectory()}
+for _,dir in ipairs(dirsArg) do
+ local path = shell.resolve(dir)
+ local real, why = fs.realPath(path)
+ local access_msg = "cannot access " .. tostring(path) .. ": "
+ if not real then
+ perr(access_msg .. why)
+ elseif not fs.exists(path) then
+ perr(access_msg .. "No such file or directory")
+ elseif fs.isDirectory(path) then
+ table.insert(dir_set, dir)
+ else -- file or link
+ table.insert(file_set, dir)
+ end
+end
+io.output():setvbuf("line")
+if #file_set > 0 then display(sort(file_set)) end
+displayDirList(dir_set)
+msft.final()
+io.output():flush()
+io.output():setvbuf("no")
+restore_color()
+return ec
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_basics.lua
index 7f1b328e2c..9f17b6cab8 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_basics.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_basics.lua
@@ -17,7 +17,7 @@ options.source_label = args[1]
local root_exception
if options.help then
- print([[Usage: install [OPTION]...
+ write([[Usage: install [OPTION]...
--from=ADDR install filesystem at ADDR
default: builds list of
candidates and prompts user
@@ -29,7 +29,8 @@ if options.help then
--label override label from .prop
--nosetlabel do not label target
--nosetboot do not use target for boot
- --noreboot do not reboot after install]])
+ --noreboot do not reboot after install
+]])
return nil -- exit success
end
@@ -132,8 +133,10 @@ for dev, path in fs.mounts() do
prop = prop_data
end
candidate.prop = prop and load('return ' .. prop)() or {}
- if not options.source_label or options.source_label:lower() == (candidate.prop.label or dev.getLabel()):lower() then
- table.insert(options.sources, candidate)
+ if not candidate.prop.ignore then
+ if not options.source_label or options.source_label:lower() == (candidate.prop.label or (dev.getLabel() or "")):lower() then
+ table.insert(options.sources, candidate)
+ end
end
end
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_utils.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_utils.lua
index af48bef83a..d70a7b23f1 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_utils.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/install_utils.lua
@@ -5,7 +5,7 @@ local function select_prompt(devs, prompt)
local choice = devs[1]
if #devs > 1 then
- print(prompt)
+ io.write(prompt,'\n')
for i = 1, #devs do
local src = devs[i]
@@ -61,7 +61,7 @@ if cmd == 'select' then
local source = select_prompt(options.sources, "What do you want to install?")
if #options.sources > 1 and #options.targets > 1 then
- print()
+ io.write('\n')
end
local target = select_prompt(options.targets, "Where do you want to install to?")
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/lua_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/lua_shell.lua
new file mode 100644
index 0000000000..d36821b8ab
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/lua_shell.lua
@@ -0,0 +1,134 @@
+local package = require("package")
+local term = require("term")
+
+local gpu = term.gpu()
+
+local function optrequire(...)
+ local success, module = pcall(require, ...)
+ if success then
+ return module
+ end
+end
+
+local env -- forward declare for binding in metamethod
+env = setmetatable({}, {
+ __index = function(t, k)
+ _ENV[k] = _ENV[k] or optrequire(k)
+ return _ENV[k]
+ end,
+ __pairs = function(self)
+ local t = self
+ return function(_, key)
+ local k, v = next(t, key)
+ if not k and t == env then
+ t = _ENV
+ k, v = next(t)
+ end
+ if not k and t == _ENV then
+ t = package.loaded
+ k, v = next(t)
+ end
+ return k, v
+ end
+ end
+})
+env._PROMPT = tostring(env._PROMPT or "lua> ")
+
+local history = {}
+
+local function findTable(t, path)
+ if type(t) ~= "table" then return nil end
+ if not path or #path == 0 then return t end
+ local name = string.match(path, "[^.]+")
+ for k, v in pairs(t) do
+ if k == name then
+ return findTable(v, string.sub(path, #name + 2))
+ end
+ end
+ local mt = getmetatable(t)
+ if t == env then mt = {__index=_ENV} end
+ if mt then
+ return findTable(mt.__index, path)
+ end
+ return nil
+end
+
+local function findKeys(t, r, prefix, name)
+ if type(t) ~= "table" then return end
+ for k, v in pairs(t) do
+ if type(k) == "string" and string.match(k, "^"..name) then
+ local postfix = ""
+ if type(v) == "function" then postfix = "()"
+ elseif type(v) == "table" and getmetatable(v) and getmetatable(v).__call then postfix = "()"
+ elseif type(v) == "table" then postfix = "."
+ end
+ r[prefix..k..postfix] = true
+ end
+ end
+ local mt = getmetatable(t)
+ if t == env then mt = {__index=_ENV} end
+ if mt then
+ return findKeys(mt.__index, r, prefix, name)
+ end
+end
+
+local function hint(line, index)
+ line = (line or "")
+ local tail = line:sub(index)
+ line = line:sub(1, index - 1)
+ local path = string.match(line, "[a-zA-Z_][a-zA-Z0-9_.]*$")
+ if not path then return nil end
+ local suffix = string.match(path, "[^.]+$") or ""
+ local prefix = string.sub(path, 1, #path - #suffix)
+ local tbl = findTable(env, prefix)
+ if not tbl then return nil end
+ local keys = {}
+ local hints = {}
+ findKeys(tbl, keys, string.sub(line, 1, #line - #suffix), suffix)
+ for key in pairs(keys) do
+ table.insert(hints, key .. tail)
+ end
+ return hints
+end
+
+gpu.setForeground(0xFFFFFF)
+term.write(_VERSION .. " Copyright (C) 1994-2015 Lua.org, PUC-Rio\n")
+gpu.setForeground(0xFFFF00)
+term.write("Enter a statement and hit enter to evaluate it.\n")
+term.write("Prefix an expression with '=' to show its value.\n")
+term.write("Press Ctrl+D to exit the interpreter.\n")
+gpu.setForeground(0xFFFFFF)
+
+while term.isAvailable() do
+ local foreground = gpu.setForeground(0x00FF00)
+ term.write(env._PROMPT)
+ gpu.setForeground(foreground)
+ local command = term.read(history, nil, hint)
+ if not command then -- eof
+ return
+ end
+ local code, reason
+ if string.sub(command, 1, 1) == "=" then
+ code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env)
+ else
+ code, reason = load(command, "=stdin", "t", env)
+ end
+ if code then
+ local result = table.pack(xpcall(code, debug.traceback))
+ if not result[1] then
+ if type(result[2]) == "table" and result[2].reason == "terminated" then
+ os.exit(result[2].code)
+ end
+ io.stderr:write(tostring(result[2]) .. "\n")
+ else
+ for i = 2, result.n do
+ term.write(require("serialization").serialize(result[i], true) .. "\t", true)
+ end
+ if term.getCursor() > 1 then
+ term.write("\n")
+ end
+ end
+ else
+ io.stderr:write(tostring(reason) .. "\n")
+ end
+end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/ro_wrapper.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/ro_wrapper.lua
new file mode 100644
index 0000000000..c9f2f3d127
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/ro_wrapper.lua
@@ -0,0 +1,30 @@
+local lib = {}
+
+function lib.wrap(proxy)
+ checkArg(1, proxy, "table")
+ if proxy.isReadOnly() then
+ return proxy
+ end
+
+ local function roerr() return nil, "filesystem is readonly" end
+ return setmetatable({
+ rename = roerr,
+ open = function(path, mode)
+ checkArg(1, path, "string")
+ checkArg(2, mode, "string")
+ if mode:match("[wa]") then
+ return roerr()
+ end
+ return proxy.open(path, mode)
+ end,
+ isReadOnly = function()
+ return true
+ end,
+ write = roerr,
+ setLabel = roerr,
+ makeDirectory = roerr,
+ remove = roerr,
+ }, {__index=proxy})
+end
+
+return lib
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua
new file mode 100644
index 0000000000..f70751e01b
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua
@@ -0,0 +1,251 @@
+local fs = require("filesystem")
+local shell = require("shell")
+local lib = {}
+
+local function perr(ops, format, ...)
+ if format then
+ io.stderr:write(ops.cmd .. string.format(": " .. format, ...) .. "\n")
+ ops.exit_code = 1
+ return 1
+ end
+end
+
+local function contents_check(arg, options, bMustExist)
+ if arg == "" then
+ return perr(options, "cannot create regular file '' No such file or directory")
+ end
+ local path = shell.resolve(arg)
+ local content_pattern = "^(%.*)(.?)"
+ local contents_of, of_dir = arg:reverse():match(content_pattern)
+ of_dir = of_dir:match("^/?$")
+ local dots = contents_of and contents_of:len() or 0
+ contents_of = of_dir and ({true,true})[dots]
+
+ if (not bMustExist or fs.exists(path)) and of_dir and not fs.isDirectory(path) then
+ perr(options, "'%s' is not a directory", arg)
+ os.exit(1)
+ end
+
+ return contents_of, path
+end
+
+local function areEqual(path1, path2)
+ local f1, f2 = fs.open(path1, "rb")
+ if f1 then
+ f2 = fs.open(path2, "rb")
+ if f2 then
+ local result = true
+ local chunkSize = 4 * 1024
+ repeat
+ local s1, s2 = f1:read(chunkSize), f2:read(chunkSize)
+ if s1 ~= s2 then
+ result = false
+ break
+ end
+ until not s1 or not s2
+ f2:close()
+ end
+ f1:close()
+ end
+ assert(f1 and f2, "could not open files for reading: " .. path1 .. ", " .. path2)
+ return result
+end
+
+local function status(verbose, from, to)
+ if verbose then
+ to = to and (" -> " .. to) or ""
+ io.write(from .. to .. "\n")
+ end
+ os.sleep(0) -- allow interrupting
+end
+
+local function prompt(message)
+ io.write(message .. " [Y/n] ")
+ local result = io.read()
+ if not result then -- closed pipe
+ os.exit(1)
+ end
+ return result and (result == "" or result:sub(1, 1):lower() == "y")
+end
+
+local function stat(path, ops, P)
+ local real, reason = fs.realPath(path)
+ if not real and not P then
+ perr(ops, "cannot read '%s': '%s'", path, reason)
+ return false
+ end
+ local isLink, linkTarget = fs.isLink(path)
+ return true,
+ real,
+ reason,
+ isLink,
+ linkTarget,
+ fs.exists(path),
+ fs.get(path),
+ real and fs.isDirectory(real)
+end
+
+function lib.recurse(fromPath, toPath, options, origin, top)
+ local mv = options.cmd == "mv"
+ local function release(result, reason)
+ if result and mv and top then
+ local rm_result = not fs.get(fromPath).isReadOnly() and fs.remove(fromPath)
+ if not rm_result then
+ perr(options, "cannot remove '%s': filesystem is readonly", fromPath)
+ result = false
+ end
+ end
+ return result, reason
+ end
+
+ local
+ ok,
+ fromReal,
+ fromError,
+ fromIsLink,
+ fromLinkTarget,
+ fromExists,
+ fromFs,
+ fromIsDir = stat(fromPath, options, options.P)
+ if not ok then return nil end
+ local
+ ok,
+ toReal,
+ toError,
+ toIsLink,
+ toLinkTarget,
+ toExists,
+ toFs,
+ toIsDir = stat(toPath, options)
+ if not ok then os.exit(1) end
+ if toFs.isReadOnly() then
+ perr(options, "cannot create target '%s': filesystem is readonly", toPath)
+ return
+ end
+
+ local same_path = fromReal == toReal
+ local same_link = fromIsLink and toIsLink and same_path
+
+ local verbose = options.v
+ local same_fs = fromFs == toFs
+ local is_mount = origin[fromReal]
+
+ if mv and is_mount then
+ return false, string.format("cannot move '%s', it is a mount point", fromPath)
+ end
+
+ if fromIsLink and options.P and not (toExists and same_path and not toIsLink) then
+ if toExists and options.n then
+ return true
+ end
+ fs.remove(toPath)
+ if toExists then
+ status(verbose, string.format("removed '%s'\n", toPath))
+ end
+ status(verbose, fromPath, toPath)
+ return release(fs.link(fromLinkTarget, toPath))
+ elseif fromIsDir then
+ if not options.r then
+ status(true, string.format("omitting directory '%s'", fromPath))
+ options.exit_code = 1
+ return true
+ end
+ if toExists and not toIsDir then
+ -- my real cp always does this, even with -f, -n or -i.
+ return nil, "cannot overwrite non-directory '" .. toPath .. "' with directory '" .. fromPath .. "'"
+ end
+ if options.x and not top and is_mount then
+ return true
+ end
+ if same_fs then
+ if (toReal.."/"):find(fromReal.."/",1,true) then
+ return nil, "cannot write a directory, '" .. fromPath .. "', into itself, '" .. toPath .. "'"
+ elseif mv then
+ return os.rename(fromPath, toPath)
+ end
+ end
+ if not toExists then
+ status(verbose, fromPath, toPath)
+ fs.makeDirectory(toPath)
+ end
+ for file in fs.list(fromPath) do
+ local result, reason = lib.recurse(fs.concat(fromPath, file), fs.concat(toPath, file), options, origin, false) -- false, no longer top
+ if not result then
+ return false, reason
+ end
+ end
+ return release(true)
+ elseif fromExists then
+ if toExists then
+ if same_path then
+ return nil, "'" .. fromPath .. "' and '" .. toPath .. "' are the same file"
+ end
+ if options.n then
+ return true
+ end
+ if options.u and not toIsDir and areEqual(fromReal, toReal) then
+ return true
+ end
+ if options.i then
+ if not prompt("overwrite '" .. toPath .. "'?") then
+ return true
+ end
+ end
+ if toIsDir then
+ return nil, "cannot overwrite directory '" .. toPath .. "' with non-directory"
+ end
+ fs.remove(toReal)
+ end
+ status(verbose, fromPath, toPath)
+ return release(fs.copy(fromPath, toPath))
+ else
+ return nil, "'" .. fromPath .. "': No such file or directory"
+ end
+end
+
+function lib.batch(args, options)
+ options.exit_code = 0
+
+ -- standardized options
+ options.i = options.i and not options.f
+ options.P = options.P or options.r
+
+ local origin = {}
+ for dev,path in fs.mounts() do
+ origin[path] = dev
+ end
+
+ local toArg = table.remove(args)
+ local _, toPath = contents_check(toArg, options)
+ if not toPath then
+ return 1
+ end
+ local originalToIsDir = fs.isDirectory(toPath)
+
+ for _,arg in ipairs(args) do
+ -- a "contents of" copy is where src path ends in . or ..
+ -- a source path ending with . is not sufficient - could be the source filename
+ local contents_of, fromPath = contents_check(arg, options, true)
+ if fromPath then
+ -- we do not append fromPath name to toPath in case of contents_of copy
+ local nextPath = toPath
+ if contents_of and options.cmd == "mv" then
+ perr(options, "invalid move path '%s'", arg)
+ else
+ if not contents_of and originalToIsDir then
+ nextPath = fs.concat(nextPath, fs.name(fromPath))
+ end
+
+ local result, reason = lib.recurse(fromPath, nextPath, options, origin, true)
+
+ if not result then
+ perr(options, reason)
+ end
+ end
+ end
+ end
+
+ return options.exit_code
+end
+
+return lib
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/transforms.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/transforms.lua
index 97a53bd441..630d8332c8 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/transforms.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/transforms.lua
@@ -18,18 +18,6 @@ end
local adjust=lib.internal.range_adjust
local view=lib.internal.table_view
--- works like string.sub but on elements of an indexed table
-function lib.sub(tbl,f,l)
- checkArg(1,tbl,'table')
- local r,s={},#tbl
- f,l=adjust(f,l,s)
- l=math.min(l,s)
- for i=math.max(f,1),l do
- r[#r+1]=tbl[i]
- end
- return r
-end
-
-- first(p1,p2) searches for the first range in p1 that satisfies p2
function lib.first(tbl,pred,f,l)
checkArg(1,tbl,'table')
@@ -53,6 +41,31 @@ function lib.first(tbl,pred,f,l)
end
end
+-- returns true if p1 at first p3 equals element for element p2
+function lib.begins(tbl,v,f,l)
+ checkArg(1,tbl,'table')
+ checkArg(2,v,'table')
+ local vs=#v
+ f,l=adjust(f,l,#tbl)
+ if vs>(l-f+1)then return end
+ for i=1,vs do
+ if tbl[f+i-1]~=v[i] then return end
+ end
+ return true
+end
+
+-- works like string.sub but on elements of an indexed table
+function --[[@delayloaded-start@]] lib.sub(tbl,f,l)
+ checkArg(1,tbl,'table')
+ local r,s={},#tbl
+ f,l=adjust(f,l,s)
+ l=math.min(l,s)
+ for i=math.max(f,1),l do
+ r[#r+1]=tbl[i]
+ end
+ return r
+end --[[@delayloaded-end@]]
+
-- if value was made by lib.sub then find can find from whence
function --[[@delayloaded-start@]] lib.find(tbl, sub, first, last)
checkArg(1, tbl, 'table')
@@ -117,21 +130,8 @@ function --[[@delayloaded-start@]] lib.partition(tbl,partitioner,dropEnds,f,l)
return result
end --[[@delayloaded-end@]]
--- returns true if p1 at first p3 equals element for element p2
-function lib.begins(tbl,v,f,l)
- checkArg(1,tbl,'table')
- checkArg(2,v,'table')
- local vs=#v
- f,l=adjust(f,l,#tbl)
- if vs>(l-f+1)then return end
- for i=1,vs do
- if tbl[f+i-1]~=v[i] then return end
- end
- return true
-end
-
-- calls callback(e,i,tbl) for each ith element e in table tbl from first
-function lib.foreach(tbl,c,f,l)
+function --[[@delayloaded-start@]] lib.foreach(tbl,c,f,l)
checkArg(1,tbl,'table')
checkArg(2,c,'function','string')
local ck=c
@@ -148,17 +148,16 @@ function lib.foreach(tbl,c,f,l)
end
end
return r
-end
-lib.select=lib.foreach
+end --[[@delayloaded-end@]]
function --[[@delayloaded-start@]] lib.where(tbl,p,f,l)
return lib.foreach(tbl,
function(e,i,tbl)
return p(e,i,tbl)and e or nil
end,f,l)
-end --[[@delayloaded-end@]]
+end --[[@delayloaded-end@]]
-function lib.concat(...)
+function --[[@delayloaded-start@]] lib.concat(...)
local r,rn,k={},0
for _,tbl in ipairs({...})do
if type(tbl)~='table'then
@@ -172,7 +171,7 @@ function lib.concat(...)
end
r.n=k and rn or nil
return r
-end
+end --[[@delayloaded-end@]]
-- works with pairs on tables
-- returns the kv pair, or nil and the number of pairs iterated
diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/install b/src/main/resources/assets/opencomputers/loot/openos/usr/man/install
index 03f89af0ad..e1b60ed793 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/install
+++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/install
@@ -9,7 +9,7 @@ DESCRIPTION
OPTIONS
--from=ADDR
- Specifies the source filesystem or its root path. ADDR can be the device uuid or a directory path. If this is a directory path, it represents a root path to install from. This option can also be used to specify source paths that would otherwise be ignored, those being devfs, tmpfs, and the rootfs. e.g. --from=/tmp . Note that if both --from and a [name] is given, install expects the source path to have a .prop defining the same name. See .prop for more details.
+ Specifies the source filesystem or its root path. ADDR can be the device uuid or a directory path. If this is a directory path, it represents a root path to install from. This option can also be used to specify source paths that would otherwise be ignored, those being devfs, tmpfs, and the rootfs. e.g. --from=/tmp . Note that if both --from and --label are used, install expects the source path to have a .prop defining the same label. See .prop for more details.
--to=ADDR
Same as --from but specifies the target filesystem by uuid or its root path. This option can also be used to specify filesystems that are otherwise ignored including tmpfs. i.e. --to=ADDR where ADDR matches the tmpfs device address or its mount point path. e.g. --to=/tmp
@@ -28,8 +28,8 @@ OPTIONS
The following can override settings defined in .prop in the source filesystem.
- --label=NAME
- use NAME for label instead of any value specified by .prop, --name is deprecated
+ --label=LABEL
+ use LABEL for label instead of any value specified by .prop
--nosetlabel
do not set target label. --nolabelset is deprecated
@@ -41,51 +41,57 @@ The following can override settings defined in .prop in the source filesystem.
do not reboot after install
.prop
- .prop should have valid lua code that returns a table of keys and their values: e.g. "return {name='OpenOS'}"
+ .prop should have valid lua syntax for a table of keys and their values: e.g. "{label='OpenOS'}"
+ All fields are optional, as is the .prop file
- name=string
- Declares an identifying name of the installation. This is displayed by install during source selection and also can be used on the commandline: e.g. (where {name="tape"} is given) `install tape`. If setlabel is true, this value is used for the target filesystem label. --name overrides this value. Note that install uses a case insensitive search: e.g. install TAPE works the same as install tape.
+ label:string
+ Declares an identifying name of the installation. This is displayed by install during source selection and also can be used on the commandline: e.g. (where {label="tape"} is given) `install tape`. If setlabel is true, this value is used for the target filesystem label. --label overrides this value. Note that install uses a case insensitive search: e.g. install TAPE works the same as install tape.
- setlabel=boolean
- Determines whether the install should set the target filesystem's label. If .prop does not define a name key and the user does not define a command line --name=VALUE, setlabel has no action. --nosetlabel overrides this value
+ setlabel:boolean
+ Determines whether the install should set the target filesystem's label. If .prop does not define a label and the user does not define a command line --label=LABEL, setlabel has no action. --nosetlabel overrides this value
- setboot=boolean
+ setboot:boolean
Determines if the target filesystem should be set as the machine's default boot device. Default is false, overriden by --nosetboot
- reboot=boolean
+ reboot:boolean
Determines if the machine should reboot after the install completes. Overriden by --noreboot
+ ignore:boolean
+ If true, the installer will skip the source filesystem and not include it for selection
+
EXAMPLE:
- return {name='OpenOS', setlabel=true, setboot=true, reboot=true}
+ {label='OpenOS', setlabel=true, setboot=true, reboot=true}
.install ENVIRONMENT
- When .install is loaded and executed, a custom table is added to the environment: ENV_.install
- These are the keys of the table as populated by install
+ A loot disc can optionally provide a custom installation script at the root of the source filesytem selected for installation. The script must be named ".install"
+ When provided, the default install action is replaced by executation of this script. The default action is to copy all source files to the destination
+ An _ENV.install table is set in the environment of '.install' when loaded
+ These are the keys and their descriptions of that table
- ENV_.install.from:
+ _ENV.install.from:
This is the path of the selected source filesystem to install from. It should be the path to the executing .install
- example: /mnt/ABC
+ example: /mnt/ABC/.install is executing, thus _ENV.install.from is "/mnt/ABC/"
- ENV_.install.to:
+ _ENV.install.to:
This is the path of the selected target filesystem to install to.
- example: /
+ example: "/"
_ENV.install.fromdir
- This is the relative path to use in the source filesysterm as passed by command line to install. If unspecified to install it defaults to "."
- example: .
+ This is the relative path to use in the source filesystem as passed by command line to install. If unspecified to install it defaults to "."
+ example: Perhaps the user executed `install --fromDir="bin"` with the intention that only files under /mnt/ABC/bin would be copied to their rootfs
_ENV.install.root
This is the relative path to use in the target filesystem as passed by command line to install. If unspecified to install it defaults to "."
- example: .
+ example: The user prefers to install to usr/ and uses `install --root=usr` and here _ENV.install.root would be "usr"
_ENV.install.update
Assigned value of --update, see OPTIONS
_ENV.install.label
- Assigned value of --name or .prop's label, see OPTIONS
+ Assigned value of --label or .prop's label, see OPTIONS
_ENV.install.setlabel
- Assigned value of .prop's setlabel unless --nolabelset, see OPTIONS
+ Assigned value of .prop's setlabel unless --nosetlabel, see OPTIONS
_ENV.install.setboot
Assigned value of .prop's boot unless --nosetboot, see OPTIONS
@@ -98,6 +104,6 @@ EXAMPLES
Searches all non rootfs filesystems to install from, and all non tmpfs filesystems to install to. Prompts the user for a selection, and copies. If .prop is defined in source, sets label and will prompt for reboot when completed.
install openos
- Searches candidates source filesystems that have .prop's that define name="OpenOS" and prompts the user to confirm install to candidate target filesystems.
+ Searches candidates source filesystems that have .prop's that define label="OpenOS" and prompts the user to confirm install to candidate target filesystems.
diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount
index 8284557bfb..491f15777c 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount
+++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount
@@ -6,6 +6,9 @@ SYNOPSIS
mount LABEL PATH
mount ADDRESS PATH
+OPTIONS
+ -r mount filesystem readonly
+
DESCRIPTION
All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too).
@@ -17,4 +20,7 @@ EXAMPLES
Mounts the file system labeled `test` at `/home`.
mount 56f /var
- Mounts the file system of which the address starts with `56f` at `/var`.
\ No newline at end of file
+ Mounts the file system of which the address starts with `56f` at `/var`.
+
+ mount -r tmpfs /tmp_ro
+ Mounts a readonly access path of tmpfs to /tmp_ro
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mv b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mv
index b0aa2f6ef0..081740ba89 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mv
+++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mv
@@ -8,8 +8,11 @@ DESCRIPTION
Renames files - and folders, as long they remain on the same file system. Files that are 'renamed' to another file system will actually be copied, then deleted.
OPTIONS
- -f
- do not prompt before overwriting
+ -f overwrite without prompt
+ -i prompt before overwriting
+ unless -f
+ -v verbose
+ -h, --help show this help
EXAMPLES
mv a b
diff --git a/src/main/resources/assets/opencomputers/loot/oppm/.install b/src/main/resources/assets/opencomputers/loot/oppm/.install
index 3b017c71e8..a9c7b6b165 100644
--- a/src/main/resources/assets/opencomputers/loot/oppm/.install
+++ b/src/main/resources/assets/opencomputers/loot/oppm/.install
@@ -1,3 +1,8 @@
-require("shell").setWorkingDirectory(install.from)
-os.execute("oppm.lua")
-
+if not install.root or #install.root == 0 then
+ install.root = "usr"
+end
+if install.update then
+ io.stderr:write("Please run 'oppm update oppm' from an installed version\nof this program to update oppm.\n")
+ return
+end
+os.execute(install.from:gsub("//","/") .. "usr/bin/oppm install --iKnowWhatIAmDoing -f oppm " .. install.to:gsub("//","/") .. install.root:gsub("//","/"))
diff --git a/src/main/resources/assets/opencomputers/loot/oppm/.prop b/src/main/resources/assets/opencomputers/loot/oppm/.prop
new file mode 100644
index 0000000000..c35148e44e
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/oppm/.prop
@@ -0,0 +1 @@
+{label="OPPM", reboot=true}
diff --git a/src/main/resources/assets/opencomputers/loot/oppm/autorun.lua b/src/main/resources/assets/opencomputers/loot/oppm/autorun.lua
deleted file mode 100644
index a009b9a91d..0000000000
--- a/src/main/resources/assets/opencomputers/loot/oppm/autorun.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-local fs = require("filesystem")
-local shell = require("shell")
-fs.mount(...,"/op-manager")
-shell.setPath(shell.getPath() .. ":/op-manager")
diff --git a/src/main/resources/assets/opencomputers/loot/oppm/etc/oppm.cfg b/src/main/resources/assets/opencomputers/loot/oppm/etc/oppm.cfg
index 352f8e9c54..cd806c5741 100644
--- a/src/main/resources/assets/opencomputers/loot/oppm/etc/oppm.cfg
+++ b/src/main/resources/assets/opencomputers/loot/oppm/etc/oppm.cfg
@@ -1,7 +1,7 @@
{
- --default installation path
- path="/usr",
- --Additional repositories and packages go here, for correct package syntax, check https://github.com/OpenPrograms/Vexatos-Programs/blob/master/op-manager/etc/example-config.cfg
- repos={
- }
+ --default installation path
+ path="/usr",
+ --Additional repositories and packages go here, for correct package syntax, check https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/example-config.cfg
+ repos={
+ }
}
diff --git a/src/main/resources/assets/opencomputers/loot/oppm/oppm.lua b/src/main/resources/assets/opencomputers/loot/oppm/usr/bin/oppm.lua
similarity index 83%
rename from src/main/resources/assets/opencomputers/loot/oppm/oppm.lua
rename to src/main/resources/assets/opencomputers/loot/oppm/usr/bin/oppm.lua
index 64df24d0cd..624d486734 100644
--- a/src/main/resources/assets/opencomputers/loot/oppm/oppm.lua
+++ b/src/main/resources/assets/opencomputers/loot/oppm/usr/bin/oppm.lua
@@ -4,13 +4,12 @@ Author: Vexatos
Warning! This file is just an auto-installer for OPPM!
DO NOT EVER TRY TO INSTALL A PACKAGE WITH THIS!
-Once you ran OPPM once, you can remove the floppy disk
+Once you have installed OPPM, you can remove the floppy disk
and run the installed OPPM version just fine.
]]
local component = require("component")
local event = require("event")
local fs = require("filesystem")
-local process = require("process")
local serial = require("serialization")
local shell = require("shell")
local term = require("term")
@@ -58,22 +57,43 @@ local function getContent(url)
return sContent
end
-local function getRepos()
+local NIL = {}
+local function cached(f)
+ return options.nocache and f or setmetatable(
+ {},
+ {
+ __index=function(t,k)
+ local v = f(k)
+ t[k] = v
+ return v
+ end,
+ __call=function(t,k)
+ if k == nil then
+ k = NIL
+ end
+ return t[k]
+ end,
+ }
+ )
+end
+
+
+local getRepos = cached(function()
local success, sRepos = pcall(getContent,"https://raw.githubusercontent.com/OpenPrograms/openprograms.github.io/master/repos.cfg")
if not success then
io.stderr:write("Could not connect to the Internet. Please ensure you have an Internet connection.")
return -1
end
return serial.unserialize(sRepos)
-end
+end)
-local function getPackages(repo)
+local getPackages = cached(function(repo)
local success, sPackages = pcall(getContent,"https://raw.githubusercontent.com/"..repo.."/master/programs.cfg")
if not success or not sPackages then
return -1
end
return serial.unserialize(sPackages)
-end
+end)
--For sorting table values by alphabet
local function compare(a,b)
@@ -103,7 +123,7 @@ local function readFromFile(fNum)
elseif fNum == 2 then
path = "/etc/oppm.cfg"
if not fs.exists(path) then
- local tProcess = process.running()
+ local tProcess = os.getenv("_")
path = fs.concat(fs.path(shell.resolve(tProcess)),"/etc/oppm.cfg")
end
end
@@ -147,8 +167,8 @@ local function listPackages(filter)
io.stderr:write("Unable to connect to the Internet.\n")
return
elseif repos==nil then
- print("Error while trying to receive repository list")
- return
+ print("Error while trying to receive repository list")
+ return
end
for _,j in pairs(repos) do
if j.repo then
@@ -156,10 +176,9 @@ local function listPackages(filter)
local lPacks = getPackages(j.repo)
if lPacks==nil then
io.stderr:write("Error while trying to receive package list for " .. j.repo.."\n")
- return
elseif type(lPacks) == "table" then
- for k in pairs(lPacks) do
- if not k.hidden then
+ for k,kt in pairs(lPacks) do
+ if not kt.hidden then
table.insert(packages,k)
end
end
@@ -169,8 +188,8 @@ local function listPackages(filter)
local lRepos = readFromFile(2)
if lRepos and lRepos.repos then
for _,j in pairs(lRepos.repos) do
- for k in pairs(j) do
- if not k.hidden then
+ for k,kt in pairs(j) do
+ if not kt.hidden then
table.insert(packages,k)
end
end
@@ -188,7 +207,7 @@ local function listPackages(filter)
local lPacks = {}
for i,j in ipairs(packages) do
if (#j>=#filter) and string.find(j,filter,1,true)~=nil then
- table.insert(lPacks,j)
+ table.insert(lPacks,j)
end
end
packages = lPacks
@@ -250,7 +269,7 @@ local function parseFolders(pack, repo, info)
local newPath = v["download_url"]:gsub("https?://raw.githubusercontent.com/"..nonSpecial(repo).."(.+)$", "%1"):gsub("/*$",""):gsub("^/*","")
tFiles[newPath] = relPath
elseif v["type"] == "dir" then
- local newFiles = unserializeFiles(getFolderTable(repo, relPath.."/"..v["name"], branch), repo, branch, fs.concat(relPath, v["name"]))
+ local newFiles = unserializeFiles(getFolderTable(repo, namePath.."/"..v["name"], branch), repo, namePath, branch, fs.concat(relPath, v["name"]))
for p,q in pairs(newFiles) do
tFiles[p] = q
end
@@ -345,8 +364,14 @@ local function provideInfo(pack)
done = true
end
if info.files then
- print("Number of files: "..tostring(#info.files))
- done = true
+ local c = 0
+ for i in pairs(info.files) do
+ c = c + 1
+ end
+ if c > 0 then
+ print("Number of files: "..tostring(c))
+ done = true
+ end
end
if not done then
print("No information provided.")
@@ -360,13 +385,17 @@ local function installPackage(pack,path,update)
printUsage()
return
end
- if not path and not update then
+ if not path then
local lConfig = readFromFile(2)
path = lConfig.path or "/usr"
- print("Installing package to "..path.."...")
+ if not update then
+ print("Installing package to "..path.."...")
+ end
elseif not update then
path = shell.resolve(path)
- print("Installing package to "..path.."...")
+ if not update then
+ print("Installing package to "..path.."...")
+ end
end
pack = string.lower(pack)
@@ -384,9 +413,8 @@ local function installPackage(pack,path,update)
end
if update then
print("Updating package "..pack)
- path = nil
if not tPacks[pack] then
- io.stderr:write("error while checking update path")
+ io.stderr:write("error while checking update path\n")
return
end
for i,j in pairs(info.files) do
@@ -469,6 +497,8 @@ local function installPackage(pack,path,update)
return
end
end
+ saveToFile(tPacks)
+
if info.dependencies then
term.write("Done.\nInstalling Dependencies...\n")
for i,j in pairs(info.dependencies) do
@@ -476,12 +506,14 @@ local function installPackage(pack,path,update)
if string.find(j,"^//") then
nPath = string.sub(j,2)
else
- nPath = fs.concat(path,j,string.gsub(i,".+(/.-)$","%1"),nil)
+ nPath = fs.concat(path,j)
end
if string.lower(string.sub(i,1,4))=="http" then
+ nPath = fs.concat(nPath, string.gsub(i,".+(/.-)$","%1"),nil)
local success,response = pcall(downloadFile,i,nPath)
if success and response then
tPacks[pack][i] = nPath
+ saveToFile(tPacks)
else
response = response or "no error message"
term.write("Error while installing files for package '"..pack.."': "..response..". Reverting installation... ")
@@ -490,22 +522,26 @@ local function installPackage(pack,path,update)
fs.remove(p)
tPacks[pack][o]=nil
end
+ saveToFile(tPacks)
print("Done.\nPlease contact the package author about this problem.")
- return
+ return tPacks
end
else
local depInfo = getInformation(string.lower(i))
if not depInfo then
term.write("\nDependency package "..i.." does not exist.")
end
- installPackage(string.lower(i),fs.concat(path,j),update)
+ local tNewPacks = installPackage(string.lower(i),nPath,update)
+ if tNewPacks then
+ tPacks = tNewPacks
+ end
end
end
end
- term.write("Done.\n")
saveToFile(tPacks)
+ term.write("Done.\n")
print("Successfully installed package "..pack)
- return true
+ return tPacks
end
local function uninstallPackage(pack)
@@ -517,10 +553,10 @@ local function uninstallPackage(pack)
table.remove(tFiles,1)
end
if not tFiles[pack] then
- print("Package has not been installed.")
- print("If it has, the package could not be identified.")
- print("In this case you have to remove it manually.")
- return
+ print("Package has not been installed.")
+ print("If it has, the package could not be identified.")
+ print("In this case you have to remove it manually.")
+ return
end
term.write("Removing package files...")
for i,j in pairs(tFiles[pack]) do
@@ -566,7 +602,7 @@ if options.iKnowWhatIAmDoing then
provideInfo(args[2])
elseif args[1] == "install" then
if not getInternet() then return end
- return installPackage(args[2],args[3],false)
+ return installPackage(args[2], args[3], false)
elseif args[1] == "update" then
if not getInternet() then return end
updatePackage(args[2])
@@ -576,70 +612,6 @@ if options.iKnowWhatIAmDoing then
printUsage()
return
end
- return
-end
-
---Very much not stolen from Sangar's install.lua
-
-local computer = require("computer")
-local unicode = require("unicode")
-
-local candidates = {}
-for address in component.list("filesystem") do
- local dev = component.proxy(address)
- if not dev.isReadOnly() and dev.address ~= computer.tmpAddress() then
- table.insert(candidates, dev)
- end
-end
-
-if #candidates == 0 then
- print("No writable disks found, aborting.")
- return
-end
-
-for i = 1, #candidates do
- local label = candidates[i].getLabel()
- if label then
- label = label .. " (" .. candidates[i].address:sub(1, 8) .. "...)"
- else
- label = candidates[i].address
- end
- print(i .. ") " .. label)
-end
-
-print("To select the device to install to, please enter a number between 1 and " .. #candidates .. ".")
-print("Press 'q' to cancel the installation.")
-local choice
-while not choice do
- result = io.read()
- if result:sub(1, 1):lower() == "q" then
- return
- end
- local number = tonumber(result)
- if number and number > 0 and number <= #candidates then
- choice = candidates[number]
- else
- print("Invalid input, please try again.")
- end
-end
-candidates = nil
-
-print("Installing OPPM to device " .. (choice.getLabel() or choice.address))
-os.sleep(0.25)
-local mnt = choice.address:sub(1, 3)
-local result, reason = shell.execute("oppm", nil, "install", "-f", "oppm", "/mnt/" .. mnt .. "/usr/", "--iKnowWhatIAmDoing")
-if not result then
- error(reason, 0)
end
-if not reason then
- return
-end
-
-print("All done! Please remove the Floppy Disk used for installation! Reboot now? [Y/n]")
-local result = io.read()
-if not result or result == "" or result:sub(1, 1):lower() == "y" then
- print("\nRebooting now!")
- computer.shutdown(true)
-end
-print("Returning to shell.")
+io.stderr:write("Please install oppm by running /bin/install.lua")
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/arp.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/arp.lua
deleted file mode 100644
index 9857df60d3..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/arp.lua
+++ /dev/null
@@ -1,25 +0,0 @@
-local network = require "network"
-
-local function fillText(text, n)
- for k = 1, n - #text do
- text = text .. " "
- end
- return text
-end
-
-local maxlen = {8, 5}
-
-for interface in pairs(network.info.getInfo().interfaces) do
- maxlen[2] = maxlen[2] < #interface+1 and #interface+1 or maxlen[2]
- for _, host in ipairs(network.info.getArpTable(interface)) do
- maxlen[1] = maxlen[1] < #host+1 and #host+1 or maxlen[1]
- end
-end
-
-print(fillText("Address", maxlen[1])..fillText("Iface", maxlen[2]))
-
-for interface in pairs(network.info.getInfo().interfaces) do
- for _, host in ipairs(network.info.getArpTable(interface)) do
- print(fillText(host, maxlen[1])..fillText(interface, maxlen[2]))
- end
-end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/cat.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/cat.lua
index d1fd62ba59..0a8f2bff0e 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/cat.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/cat.lua
@@ -20,6 +20,6 @@ else
end
until not line
file:close()
- io.write("\n")
+ io.stderr:write("\n")
end
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/dd.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/dd.lua
index afcf298c03..e46510b7eb 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/dd.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/dd.lua
@@ -4,7 +4,7 @@ local filesystem = require "filesystem"
local args = {...}
local options = {}
options.count = math.huge
-options.bs = 1
+options.bs = 128
options["if"] = "-"
options.of = "-"
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/edit.lua
index c1943cd7c6..069a4b5fd2 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/edit.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/edit.lua
@@ -37,8 +37,8 @@ io.write("\x1b[999;999H\x1b6n\x1b2J\x1b[30m\x1b[47m\x1bKEdit: " .. file .. "| F1
local code = read("\x1b", "R")
local h, w = code:match("\x1b%[(%d+);(%d+)R")
-local edith = h - 1
-local editw = w
+local edith = tonumber(h) - 1
+local editw = tonumber(w)
local x, y = 1, 1
local atline = 1
@@ -54,10 +54,11 @@ function setcur()
io.write("\x1b[" .. (y - atline + 2) .. ";" .. (x) .. "H")
end
+local iw = io.write
local function render(startline, nlines)
--io.write("\x1b["..(startline - atline + 1)..";1H")
for n = 1, nlines do
- io.write("\x1b["..(startline - atline + n + 1)..";1H\x1b[K" .. unicode.sub(lines[n + startline - 1] or "", 1, editw))
+ iw("\x1b["..(startline - atline + n + 1)..";1H\x1b[K" .. unicode.sub(lines[n + startline - 1] or "", 1, editw))
end
setcur()
end
@@ -72,7 +73,7 @@ local charHandler
local code = ""
codeHandler = function(char)
if char == "[" then code = code .. char
- elseif char == "0" then code = code .. char
+ elseif char == "O" then code = code .. char
elseif char == "3" then code = code .. char
elseif code == "[" and char == "A" then
charHandler = baseHandler
@@ -128,10 +129,10 @@ codeHandler = function(char)
end
x = x - 1
setcur()
- elseif code == "[0" and char == "P" or char == "R" then
+ elseif (code == "O" and (char == "P" or char == "R")) or (code == "[[" and (char == "A" or char == "C")) then
run = false
- io.write("\x1b[2J")
- if char == "P" then
+ iw("\x1b[2J")
+ if char == "P" or char == "A" then
local out = io.open(file, "w")
local text = ""
for _, line in ipairs(lines) do
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/find.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/find.lua
new file mode 100644
index 0000000000..a15bb14a74
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/find.lua
@@ -0,0 +1,132 @@
+local shell = require("shell")
+local fs = require("filesystem")
+local text = require("text")
+
+local USAGE =
+[===[Usage: find [path] [--type=[dfs]] [--[i]name=EXPR]
+ --path if not specified, path is assumed to be current working directory
+ --type returns results of a given type, d:directory, f:file, and s:symlinks
+ --name specify the file name pattern. Use quote to include *. iname is
+ case insensitive
+ --help display this help and exit]===]
+
+local args, options = shell.parse(...)
+
+if (not args or not options) or options.help then
+ print(USAGE)
+ if not options.help then
+ return 1
+ else
+ return -- nil return, meaning no error
+ end
+end
+
+if #args > 1 then
+ io.stderr:write(USAGE..'\n')
+ return 1
+end
+
+local path = #args == 1 and args[1] or "."
+
+local bDirs = true
+local bFiles = true
+local bSyms = true
+
+local fileNamePattern = ""
+local bCaseSensitive = true
+
+if options.iname and options.name then
+ io.stderr:write("find cannot define both iname and name\n")
+ return 1
+end
+
+if options.type then
+ bDirs = false
+ bFiles = false
+ bSyms = false
+
+ if options.type == "f" then
+ bFiles = true
+ elseif options.type == "d" then
+ bDirs = true
+ elseif options.type == "s" then
+ bSyms = true
+ else
+ io.stderr:write(string.format("find: Unknown argument to type: %s\n", options.type))
+ io.stderr:write(USAGE..'\n')
+ return 1
+ end
+end
+
+if options.iname or options.name then
+ bCaseSensitive = options.iname ~= nil
+ fileNamePattern = options.iname or options.name
+
+ if type(fileNamePattern) ~= "string" then
+ io.stderr:write('find: missing argument to `name\'\n')
+ return 1
+ end
+
+ if not bCaseSensitive then
+ fileNamePattern = fileNamePattern:lower()
+ end
+
+ -- prefix any * with . for gnu find glob matching
+ fileNamePattern = text.escapeMagic(fileNamePattern)
+ fileNamePattern = fileNamePattern:gsub("%%%*", ".*")
+end
+
+local function isValidType(spath)
+ if not fs.exists(spath) then
+ return false
+ end
+
+ if fileNamePattern:len() > 0 then
+ local fileName = spath:gsub('.*/','')
+
+ if fileName:len() == 0 then
+ return false
+ end
+
+ local caseFileName = fileName
+
+ if not bCaseSensitive then
+ caseFileName = caseFileName:lower()
+ end
+
+ local s, e = caseFileName:find(fileNamePattern)
+ if not s or not e then
+ return false
+ end
+
+ if s ~= 1 or e ~= caseFileName:len() then
+ return false
+ end
+ end
+
+ if fs.isDirectory(spath) then
+ return bDirs
+ elseif fs.isLink(spath) then
+ return bSyms
+ else
+ return bFiles
+ end
+end
+
+local function visit(rpath)
+ local spath = shell.resolve(rpath)
+
+ if isValidType(spath) then
+ local result = rpath:gsub('/+$','')
+ print(result)
+ end
+
+ if fs.isDirectory(spath) then
+ local list_result = fs.list(spath)
+ for list_item in list_result do
+ visit(rpath:gsub('/+$', '') .. '/' .. list_item)
+ end
+ end
+end
+
+visit(path)
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/getty.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/getty.lua
index f298d3c8e5..8802ed78d7 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/getty.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/getty.lua
@@ -49,7 +49,7 @@ local function unblink()
end
local function reblink()
- if not blinkState then
+ if not blinkState and blink then
blinkState = not blinkState
local char, fg, bg = component.invoke(gpu, "get", x, y)
preblinkbg = blinkState and bg or preblinkbg
@@ -184,6 +184,15 @@ mcommands["49"] = function()component.invoke(gpu, "setBackground", 0x000000)end
local lcommands = {}
lcommands["4"] = function()end --Reset to replacement mode
+lcommands["?25"] = function()
+ blink = false
+end
+
+local hcommands = {}
+
+hcommands["?25"] = function()
+ blink = true
+end
local ncommands = {}
@@ -277,6 +286,20 @@ control["l"] = function(char)
end
end
+control["h"] = function(char)
+ commandList[#commandList + 1] = commandBuf
+ if not commandList[1] or commandList[1] == "" then
+ commandList[1] = "0"
+ end
+ for _, command in ipairs(commandList) do
+ if not hcommands[command] then
+ pipes.log("Unknown escape code: " .. tostring(command))
+ break
+ end
+ hcommands[command]()
+ end
+end
+
control["n"] = function(char)
commandList[#commandList + 1] = commandBuf
if not commandList[1] or commandList[1] == "" then
@@ -333,6 +356,23 @@ control["!"] = function(char) --Disable
end
end
+-- \x1b9[Row];[Col];[Height];[Width];[Dest Row];[Dest Col]c -- copy
+control["c"] = function(char)
+ if commandMode == "9" then
+ commandList[#commandList + 1] = commandBuf
+ if #commandList == 6 then
+ component.invoke(gpu,
+ "copy",
+ tonumber(commandList[2]),
+ tonumber(commandList[1]),
+ tonumber(commandList[4]),
+ tonumber(commandList[3]),
+ tonumber(commandList[6]),
+ tonumber(commandList[5]))
+ end
+ end
+end
+
control["r"] = function(char) --Set scroll region
commandList[#commandList + 1] = commandBuf
local nt, nb = tonumber(commandList[1]) or 1, tonumber(commandList[2]) or h
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/ifconfig.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/ifconfig.lua
deleted file mode 100644
index 42a9f48629..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/ifconfig.lua
+++ /dev/null
@@ -1,41 +0,0 @@
-local network = require "network"
-local computer = require "computer"
-local args = {...}
-
-local function formatSize(size)
- size = tonumber(size) or size
- if type(size) ~= "number" then
- return tostring(size)
- end
- local sizes = {"", "K", "M", "G"}
- local unit = 1
- local power = 1024
- while size > power and unit < #sizes do
- unit = unit + 1
- size = size / power
- end
- return math.floor(size * 10) / 10 .. sizes[unit]
-end
-
-local function align(txt)return txt .. (" "):sub(#txt+1)end
-
-if #args < 1 then
- print("Network interfaces:")
- local info = network.info.getInfo()
- for node, info in pairs(info.interfaces)do
- print(align(node).."Link encap:"..info.linkName)
- print(" HWaddr "..info.selfAddr)
- if node == "lo" then print(" HWaddr "..computer.address()) end
- local pktIn, pktOut, bytesIn, bytesOut = network.info.getInterfaceInfo(node)
- print(" RX packets:"..tostring(pktIn))
- print(" TX packets:"..tostring(pktOut))
- print(" RX bytes: ".. tostring(bytesIn) .. " (" ..formatSize(bytesIn).. ") TX bytes: " ..tostring(bytesOut) .. " (".. formatSize(bytesOut) .. ")")
- end
-elseif args[1] == "bind" and args[2] then
- print("Address attached")
- network.ip.bind(args[2])
-else
- print("Usage:")
- print(" ifconfig - view network summary")
- print(" ifconfig bind [addr] - 'attach' addnitional address to computer")
-end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/init.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/init.lua
index 2162153eb7..fe1a00c293 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/init.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/init.lua
@@ -1,6 +1,6 @@
--Plan9k userspace init for pipes kernel
---TODO: pcall all + emergency shell(or do it higher, in pipes)
+--TODO: pcall all + emergency shell(or do it lower, in pipes)
local pipes = require("pipes")
local filesystem = require("filesystem")
@@ -52,6 +52,8 @@ end
local sin, sout
+local services_ran = false
+
local free_gpus = {}
local free_screens = {}
local used_gpus = {}
@@ -69,7 +71,9 @@ local function runtty(gpu, screen)
local getty = os.spawnp("/bin/getty.lua", mi, mo, nil, gpu, screen)
local readkey = os.spawnp("/bin/readkey.lua", nil, mo, mo, screen, interruptHandler)
- if not sout then
+ if not services_ran then
+ services_ran = true
+
sin = si
sout = so
@@ -113,7 +117,7 @@ if not sout then
io.output(sout)
end
-pcall(services)
+--pcall(services)
local kout = io.popen(function()
if filesystem.exists("/kern.log") then
@@ -143,7 +147,6 @@ for address, ctype in component.list() do
end
computer.pushSignal("init")
-os.sleep(0)
local signal = {}
local on_component_add = {}
@@ -244,6 +247,16 @@ function signal.component_removed(_, addr, ctype, ...)
end
end
+function signal.init()
+ if not services_ran then
+ services_ran = true
+
+ pcall(services)
+ end
+end
+
+os.sleep(0)
+
while true do
local sig = {computer.pullSignal()}
if signal[sig[1]] then
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/lua.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/lua.lua
index aeac2e24a3..de4292b301 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/lua.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/lua.lua
@@ -106,7 +106,7 @@ if #args == 0 or options.i then
--component.gpu.setForeground(0xFFFF00)
term.write("Enter a statement and hit enter to evaluate it.\n")
term.write("Prefix an expression with '=' to show its value.\n")
- term.write("Press Ctrl+C to exit the interpreter.\n")
+ term.write("Type os.exit() to exit the interpreter.\n")
--component.gpu.setForeground(0xFFFFFF)
while term.isAvailable() do
@@ -117,8 +117,8 @@ if #args == 0 or options.i then
if command == nil then -- eof
return
end
- while #history > 10 do
- table.remove(history, 1)
+ while #history > 20 do
+ history[#history] = nil
end
local code, reason
if string.sub(command, 1, 1) == "=" then
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/mount.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/mount.lua
index 6f7c1f870d..8e580109f8 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/mount.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/mount.lua
@@ -10,7 +10,7 @@ if #args == 0 then
end
return
end
-if #args < 2 then
+--[[if #args < 2 then
io.write("Usage: mount [label|address path]\n")
io.write("Note that the address may be abbreviated.")
return
@@ -26,3 +26,11 @@ local result, reason = fs.mount(table.unpack(args))
if not result then
io.stderr:write(reason)
end
+]]
+
+local h = fs.open("/sys/mount")
+local r, err = h:write(table.concat(args, " "))
+h:close()
+if err then
+ print("Error: " .. tostring(err))
+end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/ping.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/ping.lua
deleted file mode 100644
index 57b7a7b654..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/ping.lua
+++ /dev/null
@@ -1,107 +0,0 @@
-local network = require "network"
-local event = require "event"
-local computer = require "computer"
-local shell = require "shell"
-
-local args, options = shell.parse(...)
-
-if #args < 1 or options.h or options.help then
- print("Usage: ping: [addr]")
- print(" --c= --count=[ping count] Amount of pings to send(default 6)")
- print(" --s= --size=[data size] Payload size(default 56 bytes)")
- print(" --i= --interval=[seconds] Ping interval(default 1s)")
- --print(" -d --duplicates Check for duplicate messages")
- print(" --t= --droptime=[seconds] Amount of time after which ping is")
- print(" Considered to be lost[default 8s]")
- print(" -v --verbose Output more details")
- return
-end
-
-local len = tonumber(options.s) or tonumber(options.size) or 56
-
-local function round(n,r) return math.floor(n*(10^r))/(10^r) end
-
-local function verbose(...)
- if options.v or options.verbose then
- print(...)
- end
-end
-
-local function generatePayload()
- local payload = ""
- for i = 1, len do
- local ch = string.char(math.random(0, 255))
- ch = ch == ":" and "_" or ch --Ping matcher derps hard when it finds ':' in payload
- payload = payload .. ch
- end
- return payload
-end
-
-
-print("PING "..args[1].." with "..tostring(len) .." bytes of data")
-
-local stats = {
- transmitted = 0,
- received = 0,
- malformed = 0
-}
-
-local function doSleep()
-
- local deadline = computer.uptime() + (tonumber(options.i) or tonumber(options.interval) or 1)
- repeat
- computer.pullSignal(deadline - computer.uptime())
- until computer.uptime() >= deadline
-end
-
-local function doPing()
-
- local payload = generatePayload()
- local icmp_seq = network.icmp.ping(args[1], payload)
- stats.transmitted = stats.transmitted + 1
- verbose(tostring(len).." bytes to "..args[1]..": icmp_seq="..tostring(icmp_seq))
- local start = computer.uptime()
-
- local deadline = start + (tonumber(options.t) or tonumber(options.droptime) or 8)
- local e, replier, id, inpayload
- repeat
- e, replier, id, inpayload = event.pull(deadline - computer.uptime(), "ping_reply")
- until computer.uptime() >= deadline or (e == "ping_reply" and id == icmp_seq)
-
- if computer.uptime() >= deadline and e ~= "ping_reply" then
- verbose(tostring(len).." bytes lost: icmp_seq="..tostring(icmp_seq))
- elseif inpayload == payload then
- stats.received = stats.received + 1
- print(tostring(len).." bytes from "..args[1]..": icmp_seq="..tostring(icmp_seq).." time="..tostring(round(computer.uptime()-start,2)).." s")
- else
- stats.malformed = stats.malformed + 1
- verbose(tostring(#inpayload).." bytes malformed: icmp_seq="..tostring(icmp_seq).." time="..tostring(round(computer.uptime()-start,2)).." s")
- end
-end
-
-local begin = computer.uptime()
-
-local function outputStats()
- print("--- "..args[1].." ping statistics ---")
- print(tostring(stats.transmitted) .. " packets transmitted, "
- ..tostring(stats.received) .. " received, "
- ..tostring(100 - math.floor((stats.received / stats.transmitted) * 100)) .. "% packet loss, time " .. tostring(round(computer.uptime()-begin,2)))
-end
-
-local state, reason = pcall(function()
- local c = 0
- repeat
- doPing()
- doSleep()
- c = c + 1
- until c == 0 or (tonumber(options.c) and c >= tonumber(options.c))
- or (tonumber(options.count) and c >= tonumber(options.count))
- or ((not tonumber(options.c)) and (not tonumber(options.count)) and c >= 8)
-end)
-
-if not state then
- verbose("Stopped by: "..tostring(reason))
-end
-
-outputStats()
-
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua
index 8a2b40dfde..089efce24f 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/readkey.lua
@@ -170,17 +170,17 @@ function on.key_down(_, source, ascii, keycode, user)
elseif keycode == 205 then io.stdout:write("\x1b[C")
elseif keycode == 203 then io.stdout:write("\x1b[D")
- elseif keycode == keyboard.keys.f1 then io.stdout:write("\x1b[0P")
- elseif keycode == keyboard.keys.f2 then io.stdout:write("\x1b[0Q")
- elseif keycode == keyboard.keys.f3 then io.stdout:write("\x1b[0R")
- elseif keycode == keyboard.keys.f4 then io.stdout:write("\x1b[0S")
+ elseif keycode == keyboard.keys.f1 then io.stdout:write("\x1bOP")
+ elseif keycode == keyboard.keys.f2 then io.stdout:write("\x1bOQ")
+ elseif keycode == keyboard.keys.f3 then io.stdout:write("\x1bOR")
+ elseif keycode == keyboard.keys.f4 then io.stdout:write("\x1bOS")
elseif keycode == keyboard.keys.delete then io.stdout:write("\x1b[3~")
- elseif keycode == keyboard.keys.insert then io.stdout:write("\x1b[2~")
+ --elseif keycode == keyboard.keys.insert then io.stdout:write("\x1b[2~")
elseif keycode == keyboard.keys.pageUp then io.stdout:write("\x1b[5~")
elseif keycode == keyboard.keys.pageDown then io.stdout:write("\x1b[6~")
- elseif keycode == keyboard.keys.home then io.stdout:write("\x1b0H")
- elseif keycode == keyboard.keys["end"] then io.stdout:write("\x1b0F")
+ elseif keycode == keyboard.keys.home then io.stdout:write("\x1bOH")
+ elseif keycode == keyboard.keys["end"] then io.stdout:write("\x1bOF")
elseif keycode == keyboard.keys.tab then io.stdout:write("\t")
--TODO: rest fX keys
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/route.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/route.lua
deleted file mode 100644
index 1a35e3954e..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/route.lua
+++ /dev/null
@@ -1,44 +0,0 @@
-local network = require "network"
-
-local function fillText(text, n)
- for k = 1, n - #text do
- text = text .. " "
- end
- return text
-end
-
-local function normLine(data)
- local res = ""
- for c in data:gmatch(".") do
- if c == "\n" or c == "\r" then c = "\x1b[31m.\x1b[39m" end
- res = res .. (c:match("[%g%s]") or "\x1b[31m.\x1b[39m")
- end
- return res
-end
-
-print("MCNET routing table")
-local routes = network.info.getRoutes()
-local maxlen = {12, 8, 6, 4, 5}
-
-for host, route in pairs(routes) do
- maxlen[1] = maxlen[1] < #normLine(host)+1 and #normLine(host)+1 or maxlen[1]
- maxlen[2] = maxlen[2] < #route.router+1 and #route.router+1 or maxlen[2]
- maxlen[3] = maxlen[3] < #route.interface+1 and #route.interface+1 or maxlen[3]
- maxlen[4] = maxlen[4] < #tostring(route.age)+1 and #tostring(route.age)+1 or maxlen[4]
- maxlen[5] = maxlen[5] < #tostring(route.dist)+1 and #tostring(route.dist)+1 or maxlen[5]
-end
-
-print(fillText("Destination", maxlen[1])..
- fillText("Gateway", maxlen[2])..
- fillText("Iface", maxlen[3])..
- fillText("Age", maxlen[4])..
- fillText("Dist", maxlen[5]))
-
-for host, route in pairs(routes) do
- print(fillText(normLine(host), maxlen[1])..
- fillText(route.router, maxlen[2])..
- fillText(route.interface, maxlen[3])..
- fillText(tostring(route.age), maxlen[4])..
- fillText(tostring(route.dist), maxlen[5]))
-end
-
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/plan9k/bin/sh.lua
index 8a245c2960..4560167ccd 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/bin/sh.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/bin/sh.lua
@@ -4,6 +4,8 @@ local text = require("text")
local shell = require("shell")
local fs = require("filesystem")
+local arg, opt = shell.parse(...)
+
local alias = {}
local builtin = {}
@@ -19,6 +21,7 @@ local function parse(tokens)
local nextin = "-"
local nextout = "-"
+ local nexterr = nil
local arg = {}
local skip = false
@@ -51,15 +54,20 @@ local function parse(tokens)
if not tokens[k + 1]:match("[%w%._%-/~]+") then error("Syntax error") end
nextout = tokens[k + 1]
skip = true
+ elseif token == "2>" then
+ if not tokens[k + 1]:match("[%w%._%-/~]+") then error("Syntax error") end
+ nexterr = tokens[k + 1]
+ skip = true
elseif token == ">>" then
if not tokens[k + 1]:match("[%w%._%-/~]+") then error("Syntax error") end
nextout = tokens[k + 1]
skip = true
print("APPEND MODE IS NOT IMPLEMENTED")
elseif token == "&" then
- res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout, nowait = true}
+ res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout, stderr = nexterr, nowait = true}
nextout = "-"
nextin = "-"
+ nexterr = nil
arg = {}
end
else
@@ -68,7 +76,7 @@ local function parse(tokens)
end
if #arg > 0 then
- res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout}
+ res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout, stderr = nexterr}
end
return res, pipes
@@ -118,9 +126,10 @@ local function execute(cmd)
for n, program in pairs(programs) do
local sin = type(program.stdin) == "number" and pipes[program.stdin][1] or program.stdin == "-" and io.input() or io.open(program.stdin, "r")
local sout = type(program.stdout) == "number" and pipes[program.stdout][2] or program.stdout == "-" and io.output() or io.open(program.stdout, "w")
+ local serr = program.stderr and (program.stderr == "-" and io.output() or io.open(program.stderr, "w")) or io.stderr
processes[n] = {
- pid = os.spawnp(program.arg[1], sin, sout, io.stderr, table.unpack(program.arg, 2)),
+ pid = os.spawnp(program.arg[1], sin, sout, serr, table.unpack(program.arg, 2)),
stdin = sin,
stdout = sout
}
@@ -149,6 +158,17 @@ local function expand(value)
return result
end
+local function script(file)
+ for line in io.lines(file) do
+ if line:sub(1,1) ~= "#" then
+ if opt.x then
+ print("> " .. line)
+ end
+ execute(text.trim(line))
+ end
+ end
+end
+
-------------------
-- Builtins
@@ -174,10 +194,18 @@ builtin.exit = function()
end
builtin.alias = function(what, ...)
+ if not what then
+ print("Usage: alias [command] [arguments...]")
+ return
+ end
alias[what] = {...}
end
builtin.unalias = function(what)
+ if not what then
+ print("Usage: unalias ")
+ return
+ end
alias[what] = nil
end
@@ -196,11 +224,15 @@ if fs.exists("~/.history") then
end
local function log(cmd)
- local hisfile = io.open("~/.history", "a")
- if #cmd > 0 then
- hisfile:write(cmd .. "\n")
+ local hisfile, err = io.open("~/.history", "a")
+ if hisfile then
+ if #cmd > 0 then
+ hisfile:write(cmd .. "\n")
+ end
+ hisfile:close()
+ else
+ io.stderr:write("Error writing to histfile: " .. tostring(err))
end
- hisfile:close()
end
-------------------
@@ -310,10 +342,13 @@ builtin.alias("....", "cd", "../../..")
-------------------
-- Main loop
+if arg[1] and fs.exists(arg[1]) then
+ script(arg[1])
+ return
+end
+
if fs.exists("~/.shrc") then
- for line in io.lines("~/.shrc") do
- execute(line)
- end
+ script(arg[1])
end
while run do
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/boot/kernel/pipes b/src/main/resources/assets/opencomputers/loot/plan9k/boot/kernel/pipes
index d9522217a7..51ffff9cbb 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/boot/kernel/pipes
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/boot/kernel/pipes
@@ -60,6 +60,9 @@ do
local y = 1
function kernel._println(msg)
+ for line in string.gmatch(tostring(msg), "([^\n]+)") do
+ pcall(function()kernel.io.debug(msg)end)
+ end
if gpu and screen then
for line in string.gmatch(tostring(msg), "([^\n]+)") do
kernel._K.component.invoke(gpu, "set", 1, y + 3, line)
@@ -84,6 +87,7 @@ do
end
kernel.io.println = kernel._println
+ kernel.io.debug = function()end
end
kernel.io.println(_OSVERSION .. " Starting")
@@ -97,7 +101,7 @@ local panicPull = kernel._K.computer.pullSignal
function kernel.panic()
kernel._println("--------------------------------------------------------")
pcall(function()
- for s in string.gmatch(debug.traceback(), "[^\r\n]+") do
+ for s in string.gmatch(tostring(debug.traceback()), "[^\r\n]+") do
kernel._println(s)
end
end)
@@ -184,8 +188,13 @@ kernel.io.println("Starting base modules")
for i = 1, #modules do
if kernel.modules[modules[i].name].start then
kernel.io.println("Start module " .. modules[i].name)
- local result = table.pack(pcall(kernel.modules[modules[i].name].start))
+ local result = table.pack(xpcall(kernel.modules[modules[i].name].start, function(e)
+ pcall(kernel._println, "Module tb: " .. debug.traceback())
+ pcall(kernel._println, "E: " .. tostring(e))
+ end))
+
if not result[1] then
+ pcall(kernel._println, "Module start failed: " .. modules[i].name)
pcall(kernel._println, result[2])
kernel.panic()
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/init.lua b/src/main/resources/assets/opencomputers/loot/plan9k/init.lua
index 049b36b656..5e20a5a806 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/init.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/init.lua
@@ -81,7 +81,7 @@ end
local function dofile(fs, file)
local program, reason = loadfile(fs, file)
if program then
- local result = table.pack(pcall(program))
+ local result = table.pack(true, program())
if result[1] then
return table.unpack(result, 2, result.n)
else
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/colors.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/colors.lua
new file mode 100644
index 0000000000..b2d8837ea2
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/colors.lua
@@ -0,0 +1,30 @@
+local colors = {
+ [0] = "white",
+ [1] = "orange",
+ [2] = "magenta",
+ [3] = "lightblue",
+ [4] = "yellow",
+ [5] = "lime",
+ [6] = "pink",
+ [7] = "gray",
+ [8] = "silver",
+ [9] = "cyan",
+ [10] = "purple",
+ [11] = "blue",
+ [12] = "brown",
+ [13] = "green",
+ [14] = "red",
+ [15] = "black"
+}
+
+do
+ local keys = {}
+ for k in pairs(colors) do
+ table.insert(keys, k)
+ end
+ for _, k in pairs(keys) do
+ colors[colors[k]] = k
+ end
+end
+
+return colors
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/internet.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/internet.lua
index 00fbf644c3..886ff6f4e4 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/internet.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/internet.lua
@@ -98,7 +98,15 @@ function internet.socket(address, port)
end
local stream = {inet = inet, socket = socket}
+
+ -- 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)
+ if not self.socket then return end
+ pcall(self.socket.close)
+ end
local metatable = {__index = socketStream,
+ __gc = cleanup,
__metatable = "socketstream"}
return setmetatable(stream, metatable)
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/01_util.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/01_util.lua
index 1cdcacc79e..c182f16aac 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/01_util.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/01_util.lua
@@ -24,6 +24,14 @@ function split(str, pat)
return t
end
+function cloneTab(t)
+ local n = {}
+ for k, v in pairs(t) do
+ n[k] = v
+ end
+ return n
+end
+
--------
function getAllocator()
@@ -47,6 +55,14 @@ end
--------
+function randomString(c)
+ local s = ""
+ for i = 1, c do
+ s = s .. string.char(math.random(0, 255))
+ end
+ return s
+end
+
function uuidBin(uuid)
local undashed = uuid:gsub("-","")
local high = tonumber(undashed:sub(1,16), 16)
@@ -58,6 +74,10 @@ function binUUID(uuid)
local raw = toHex(uuid)
return raw:sub(1, 8) .. "-" .. raw:sub(9, 12) .. "-" .. raw:sub(13, 16) .. "-" .. raw:sub(17, 20) .. "-" .. raw:sub(21)
end
+
+function shortUUID()
+ return toHex(randomString(2)):upper()
+end
--------
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/05_vfs.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/05_vfs.lua
index 14fff56284..67c6b72ac3 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/05_vfs.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/05_vfs.lua
@@ -528,7 +528,13 @@ function filesystem.open(path, mode)
end
local stream = {fs = node.fs, handle = handle}
+
+ local function cleanup(self)
+ if not self.handle then return end
+ pcall(self.fs.close, self.handle)
+ end
local metatable = {__index = fileStream,
+ __gc = cleanup,
__metatable = "filestream"}
return setmetatable(stream, metatable)
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/06_cowfs.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/06_cowfs.lua
index 789f169885..b5963493ef 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/06_cowfs.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/06_cowfs.lua
@@ -108,7 +108,7 @@ function new(readfs, writefs)
end
return result
end
- proxy.open = function(path, mode) --hnd = orig * 2 [+ 1]
+ proxy.open = function(path, mode)
if mode:sub(1, 1) == "w" then
if readfs.exists(path) and not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path)) then
if readfs.isDirectory(path) then
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/09_rootfs.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/09_rootfs.lua
index 8713f4f98e..3c17b635de 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/09_rootfs.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/09_rootfs.lua
@@ -1,9 +1,10 @@
function start()
- if component.invoke(computer.getBootAddress(), "isReadOnly") then
+ if component.invoke(computer.getBootAddress(), "isReadOnly") or (kernel.modules.special and kernel.modules.special.roroot) then
local cow = kernel.modules.cowfs.new(computer.getBootAddress(), computer.tmpAddress())
kernel.modules.vfs.mount(cow, "/")
else
kernel.modules.vfs.mount(computer.getBootAddress(), "/")
end
+
kernel.modules.vfs.mount(computer.tmpAddress(), "/tmp")
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/10_sysfs.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/10_sysfs.lua
new file mode 100644
index 0000000000..9324c09c4a
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/10_sysfs.lua
@@ -0,0 +1,113 @@
+proxy = {}
+data = {}
+
+proxy.address = "sysfs0000"
+proxy.spaceUsed = function() return 0 end
+proxy.spaceTotal = function() return 0 end
+proxy.makeDirectory = function() error("Permission Denied") end
+proxy.isReadOnly = function() return true end
+proxy.rename = function() error("Permission Denied") end
+proxy.remove = function() error("Permission Denied") end
+proxy.setLabel = function() error("Permission Denied") end
+proxy.size = function(path)
+ local seg = kernel.modules.vfs.segments(path)
+ local file = data
+ for _, d in pairs(seg) do
+ file = file[d]
+ end
+ return file.size and file.size() or 0
+end
+proxy.getLabel = function() return "sysfs" end
+
+local allocator, handles = kernel.modules.util.getAllocator()
+
+proxy.exists = function(path)
+ local seg = kernel.modules.vfs.segments(path)
+ local file = data
+ for _, d in pairs(seg) do
+ if not file[d] then
+ return false
+ end
+ file = file[d]
+ end
+ return file and true or false
+end
+proxy.open = function(path)
+ kernel.io.debug("Sysfs open: " .. tostring(path))
+ local seg = kernel.modules.vfs.segments(path)
+ local file = data
+ for _, d in pairs(seg) do
+ if not file[d] then
+ return nil, "File not found"
+ end
+ file = file[d]
+ end
+ local hnd = allocator:get()
+ hnd.file = file
+ if hnd.file.open then
+ hnd.file.open(hnd)
+ end
+ return hnd.id
+end
+proxy.read = function(h, ...)
+ return handles[h].file.read(handles[h], ...)
+end
+proxy.close = function(h)
+ if handles[h].file.close then
+ handles[h].file.close(handles[h])
+ end
+ allocator:unset(handles[h])
+end
+proxy.write = function(h, ...)
+ return handles[h].file.write(handles[h], ...)
+end
+proxy.seek = function(h, ...)
+ return handles[h].file.seek(handles[h], ...)
+end
+proxy.isDirectory = function(path)
+ local seg = kernel.modules.vfs.segments(path)
+ local dir = data
+ for _, d in pairs(seg) do
+ dir = dir[d]
+ end
+ if dir.__type then
+ return false
+ end
+ return true
+end
+proxy.list = function(path)
+ local seg = kernel.modules.vfs.segments(path)
+ local dir = data
+ for _, d in pairs(seg) do
+ dir = dir[d]
+ end
+ if dir.__type then
+ error("File is not a directory")
+ end
+ local list = {}
+ for f, node in pairs(dir) do
+ list[#list + 1] = f .. (node.__type and "" or "/")
+ end
+ return list
+end
+
+-----
+
+function roFile(data)
+ return {
+ __type = "f",
+ read = function(h)
+ if h.read then
+ return nil
+ end
+ h.read = true
+ return (type(data) == "function") and data() or tostring(data)
+ end
+ }
+end
+
+data.net = {}
+
+function start()
+ kernel.modules.vfs.mount(proxy, "/sys")
+end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/11_block.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/11_block.lua
new file mode 100644
index 0000000000..d109168444
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/11_block.lua
@@ -0,0 +1,29 @@
+local devices = {}
+local dfs = kernel.modules.devfs.data
+scanners = {}
+
+function register(uuid, name, device, scan)
+ if devices[uuid] then
+ return
+ end
+
+ if dfs[name] then
+ return
+ end
+
+ devices[uuid] = {name = name}
+ dfs[name] = device
+
+ if scan then
+ for k, v in pairs(scanners) do
+ v(device, uuid, name)
+ end
+ end
+end
+
+function unregister(uuid)
+ if not devices[uuid] then
+ return
+ end
+ dfs[devices[uuid].name] = nil
+end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/12_mount.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/12_mount.lua
new file mode 100644
index 0000000000..3126f4cd5f
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/12_mount.lua
@@ -0,0 +1,23 @@
+filesystems = {}
+
+kernel.modules.sysfs.data.mount = {
+ __type = "f",
+ write = function(h, data)
+ local args = kernel.modules.util.split(data, "%s")
+ kernel.io.println("Mount " .. table.concat(args, ","))
+ if #args < 3 then
+ return nil, "Invalid argument count"
+ end
+ if not filesystems[args[1]] then
+ return nil, "Invalid filesystem"
+ end
+
+ local proxy, err = filesystems[args[1]](table.unpack(args, 2, #args - 1))
+ if not proxy then
+ kernel.io.println("Error mounting " .. tostring(args[1]) .. ": " .. tostring(err))
+ return nil, err
+ end
+
+ return kernel.modules.vfs.mount(proxy, args[#args])
+ end
+}
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/15_userspace.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/15_userspace.lua
index 37246e14ea..494841d93a 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/15_userspace.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/15_userspace.lua
@@ -19,7 +19,8 @@ kernel.userspace.computer.getBootAddress = kernel._K.computer.getBootAddress
kernel.userspace.computer.shutdown = kernel.modules.gc.shutdown
kernel.userspace.computer.pullSignal = function(timeout)
- return coroutine.yield("signal", timeout)
+ kernel.modules.threading.currentThread.deadline = computer.uptime() + (timeout or math.huge)
+ return coroutine.yield("signal")
end
kernel.userspace.computer.hasSignal = function(sigType)
@@ -79,11 +80,12 @@ end
function kernel.userspace.os.exit()
kernel.modules.threading.kill(kernel.modules.threading.currentThread.pid)
- coroutine.yield("yield", 0)
+ coroutine.yield("yield")
end
function kernel.userspace.os.sleep(time)
- coroutine.yield("yield", computer.uptime() + (time or 0))
+ kernel.modules.threading.currentThread.deadline = computer.uptime() + (time or 0)
+ coroutine.yield("yield")
end
function kernel.userspace.os.getenv(name)
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_partition.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_partition.lua
new file mode 100644
index 0000000000..7681c7df67
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_partition.lua
@@ -0,0 +1,60 @@
+function buildDevice(parent, first, size)
+ kernel.io.println("Partition from "..first)
+ return {
+ __type = "f",
+ open = function(hnd)
+ hnd.pos = 0
+ hnd.h = {}
+ parent.open(hnd.h)
+ parent.seek(hnd.h, "set", first)
+ end,
+ size = function()
+ return size
+ end,
+ write = function(h, data)
+ parent.seek(h.h, "set", first + h.pos)
+ parent.write(h.h, data:sub(1, math.min(#data, size - h.pos)))
+
+ h.pos = h.pos + #data
+
+ return not (h.pos >= size)
+ end,
+ read = function(h, len)
+ len = math.ceil(len)
+ if h.pos >= size then
+ return
+ end
+
+ -- 0 1 2 3 4 5 6 7 8
+ -- - - - x x x - - -
+
+ parent.seek(h.h, "set", first + h.pos)
+ local data = parent.read(h.h, math.min(len, size - h.pos))
+
+ h.pos = h.pos + len
+ return data
+ end,
+ seek = function(h, whence, offset)
+ offset = offset or 0
+ if whence == "end" then
+ h.pos = math.min(size, math.max(0, size - offset))
+ return h.pos
+ elseif whence == "set" then
+ h.pos = math.min(size, math.max(0, offset))
+ return h.pos
+ elseif whence == "cur" then
+ h.pos = math.min(size, math.max(0, h.pos + offset))
+ return h.pos
+ else
+ error("Invalid whence")
+ end
+ return math.floor(h.pos)
+ end,
+ close = function(h)
+ if parent.close then
+
+ end
+ end
+ }
+end
+
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_require.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_require.lua
index 5e925dbf0c..eaca39f7f0 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_require.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/16_require.lua
@@ -94,6 +94,7 @@ function start()
kernel.userspace.package.preload.computer = setmetatable({}, {__index = kernel.userspace.computer})
kernel.userspace.package.preload.io = setmetatable({}, {__index = kernel.modules.io.io})
kernel.userspace.package.preload.unicode = setmetatable({}, {__index = kernel.userspace.unicode})
+ kernel.userspace.package.preload.os = setmetatable({}, {__index = kernel.userspace.os})
--TODO: fix override.. maybe
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_chatbox.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_chatbox.lua
index b0f74a192c..0647aa7d68 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_chatbox.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_chatbox.lua
@@ -1,47 +1,31 @@
-chatboxes = {}
+local block = kernel.modules.block
-local function buildDevfs()
- for file in pairs(kernel.modules.devfs.data) do
- if file:match("^chatbox") then
- kernel.modules.devfs.data[file] = nil
- end
- end
- for k, chatbox in ipairs(chatboxes) do
- kernel.modules.devfs.data["chatbox" .. chatbox:sub(1,4):upper()] = {
- __type = "f",
- open = function(hnd, mode)
- if mode == "r" then error("Invalid mode") end
- hnd.chatbox = chatbox
- end,
- size = function()
- return 2048
- end,
- write = function(h, data)
- component.invoke(chatbox, "say", data)
- return true
- end,
- }
- end
+local function buildDevice()
+ return {
+ __type = "f",
+ open = function(hnd, mode)
+ if mode == "r" then error("Invalid mode") end
+ hnd.chatbox = chatbox
+ end,
+ size = function()
+ return 2048
+ end,
+ write = function(h, data)
+ component.invoke(chatbox, "say", data)
+ return true
+ end,
+ }
end
local function onComponentAdded(_, address, componentType)
if componentType == "chat_box" then
- chatboxes[#chatboxes + 1] = address
- buildDevfs()
+ block.register(address, "chatbox" .. address:sub(1,4):upper(), buildDevice(address))
end
end
local function onComponentRemoved(_, address, componentType)
if componentType == "chat_box" then
- local t
- for i, chatbox in ipairs(chatboxes) do
- if chatbox == address then
- t = i
- break
- end
- end
- table.remove(chatboxes, t)
- buildDevfs()
+ block.unregister(address)
end
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_data.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_data.lua
index 4676746f46..c1e25d4630 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_data.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_data.lua
@@ -1,47 +1,25 @@
-cards = {}
+local block = kernel.modules.block
-
-local function buildDevfs()
- for file in pairs(kernel.modules.devfs.data) do
- if file == "urandom" then
- kernel.modules.devfs.data[file] = nil
- end
- end
-
- kernel.modules.devfs.data["urandom"] = {
+local function buildDevice(addr)
+ return {
__type = "f",
read = function(h, len)
- return component.invoke(cards[1], "random", len)
+ return component.invoke(addr, "random", len)
end
}
end
local function onComponentAdded(_, address, componentType)
if componentType == "data" then
- cards[#cards + 1] = address
- buildDevfs()
+ block.register(address, "urandom", buildDevice(address))
end
end
local function onComponentRemoved(_, address, componentType)
if componentType == "data" then
- local t
- for i, card in ipairs(cards) do
- if card == address then
- t = i
- break
- end
- end
- table.remove(cards, t)
- buildDevfs()
+ block.unregister(address)
end
end
---function start()
- --for card, t in component.list("data") do
- -- onComponentAdded(_, card, t)
- --end
---end
-
kernel.modules.keventd.listen("component_added", onComponentAdded)
kernel.modules.keventd.listen("component_removed", onComponentRemoved)
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_drive.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_drive.lua
index 24cb3ae64e..234ffd04f9 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_drive.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_drive.lua
@@ -1,6 +1,6 @@
-drives = {}
+local block = kernel.modules.block
-function writeSectors(drive, data, at)
+local function writeSectors(drive, data, at)
local sectorSize = component.invoke(drive, "getSectorSize")
repeat
local atSector = math.floor((at - 1) / sectorSize) + 1
@@ -16,21 +16,21 @@ function writeSectors(drive, data, at)
local toWrite = before .. data:sub(1, writable) .. after
data = data:sub(writable + 1)
- kernel.io.println("Wd: " .. atSector .. "/" .. #toWrite .. ": "..inSectorStart.." [ " .. writable .. " ] "..(inSectorStart + writable) .. " #old="..#old)
+ --kernel.io.println("Wd: " .. atSector .. "/" .. #toWrite .. ": "..inSectorStart.." [ " .. writable .. " ] "..(inSectorStart + writable) .. " #old="..#old)
component.invoke(drive, "writeSector", atSector, toWrite)
at = at + writable
until #data < 1
end
-function readSectors(drive, at, len)
+local function readSectors(drive, at, len)
local data = ""
local sectorSize = component.invoke(drive, "getSectorSize")
repeat
local atSector = math.floor(at / sectorSize) + 1
sector = component.invoke(drive, "readSector", atSector)
- kernel.io.println("Rsect " .. atSector .. ": " .. tostring((at - 1) % sectorSize + 1) .. " -> " .. tostring(math.min((at - 1) % sectorSize + len - #data, sectorSize)))
+ --kernel.io.println("Rsect " .. atSector .. ": " .. tostring((at - 1) % sectorSize + 1) .. " -> " .. tostring(math.min((at - 1) % sectorSize + len - #data, sectorSize)))
local read = sector:sub((at - 1) % sectorSize + 1, math.min((at - 1) % sectorSize + len - #data, sectorSize))
data = data .. read
@@ -39,79 +39,64 @@ function readSectors(drive, at, len)
return data
end
-local function buildDevfs()
- for file in pairs(kernel.modules.devfs.data) do
- if file:match("^sd") then
- kernel.modules.devfs.data[file] = nil
- end
- end
- for k, drive in ipairs(drives) do
- kernel.modules.devfs.data["sd" .. drive:sub(1, 4):upper()] = {
- __type = "f",
- open = function(hnd)
- --component.invoke(drive, "seek", -math.huge)
- hnd.drive = drive
- hnd.pos = 1
- --kernel.io.println("Od: " .. hnd.pos .. "/" .. component.invoke(drive, "getCapacity"))
- end,
- size = function()
- return component.invoke(drive, "getCapacity")
- end,
- write = function(h, data)
-
- writeSectors(drive, data, h.pos)
- --kernel.io.println("Wd: " .. h.pos .. "(+" .. #data .. ")/" .. component.invoke(drive, "getCapacity"))
- h.pos = h.pos + #data
- return not (h.pos >= component.invoke(drive, "getCapacity"))
- --TODO: do this correctly
- end,
- read = function(h, len)
- len = math.ceil(len)
- kernel.io.println("Rd " .. tostring(len) .. ": " .. h.pos .. "/" .. component.invoke(drive, "getCapacity"))
- if h.pos >= component.invoke(drive, "getCapacity") then
- return
- end
- local data = readSectors(drive, h.pos, len)
- h.pos = h.pos + len
- return data
- end,
- seek = function(h, whence, offset)
- offset = offset or 0
- if whence == "end" then
- h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, component.invoke(drive, "getCapacity") - offset))
- return h.pos - 1
- elseif whence == "set" then
- h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, 1 + offset))
- return h.pos - 1
- else
- h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, h.pos + offset))
- return h.pos - 1
- end
- return math.floor(h.pos)
+local function buildDevice(drive)
+ return {
+ __type = "f",
+ open = function(hnd)
+ --component.invoke(drive, "seek", -math.huge)
+ hnd.drive = drive
+ hnd.pos = 1
+ --kernel.io.println("Od: " .. hnd.pos .. "/" .. component.invoke(drive, "getCapacity"))
+ end,
+ size = function()
+ return component.invoke(drive, "getCapacity")
+ end,
+ write = function(h, data)
+
+ writeSectors(drive, data, h.pos)
+ --kernel.io.println("Wd: " .. h.pos .. "(+" .. #data .. ")/" .. component.invoke(drive, "getCapacity"))
+ h.pos = h.pos + #data
+ return not (h.pos >= component.invoke(drive, "getCapacity"))
+ --TODO: do this correctly
+ end,
+ read = function(h, len)
+ len = math.ceil(len)
+ --kernel.io.println("Rd " .. tostring(len) .. ": " .. h.pos .. "/" .. component.invoke(drive, "getCapacity"))
+ if h.pos >= component.invoke(drive, "getCapacity") then
+ return
end
- }
- end
+ local data = readSectors(drive, h.pos, len)
+ h.pos = h.pos + len
+ return data
+ end,
+ seek = function(h, whence, offset)
+ offset = offset or 0
+ if whence == "end" then
+ h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, component.invoke(drive, "getCapacity") - offset + 1))
+ return h.pos - 1
+ elseif whence == "set" then
+ h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, 1 + offset))
+ return h.pos - 1
+ else
+ h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, h.pos + offset))
+ return h.pos - 1
+ end
+ return math.floor(h.pos)
+ end
+ }
end
local function onComponentAdded(_, address, componentType)
if componentType == "drive" then
- drives[#drives + 1] = address
- buildDevfs()
+ block.register(address, "sd" .. address:sub(1,4):upper(), buildDevice(address), true)
end
end
local function onComponentRemoved(_, address, componentType)
if componentType == "drive" then
- local t
- for i, drive in ipairs(drives) do
- if drive == address then
- t = i
- break
- end
- end
- table.remove(drives, t)
- buildDevfs()
+ block.unregister(address)
end
end
+
kernel.modules.keventd.listen("component_added", onComponentAdded)
kernel.modules.keventd.listen("component_removed", onComponentRemoved)
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_gpt.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_gpt.lua
new file mode 100644
index 0000000000..5ee7b4a905
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_gpt.lua
@@ -0,0 +1,30 @@
+local block = kernel.modules.block
+local util = kernel.modules.util
+
+local lba = 512
+
+local function scan(device, duuid, dname)
+ local h = {}
+ device.open(h)
+ device.seek(h, "set", 1 * lba)
+ local head = device.read(h, 92)
+ local sig, rev, hsize, crc, clb, blb, fusablelba, lusablelba,
+ uuid, arraystart, partitions, entrysz, partcrc = string.unpack("\60c8c4I4I4xxxxI8I8I8I8c16I8I4I4I4", head)
+ if sig ~= "EFI PART" or rev ~= "\0\0\1\0" then
+ return
+ end
+ for i = 1, partitions do
+ device.seek(h, "set", arraystart * lba + (i - 1) * entrysz)
+ local entry = device.read(h, entrysz)
+ local ptype, puuid, pstart, pend, attr, name = string.unpack("\60c16c16I8I8I8c72", entry)
+ if ptype ~= ("\0"):rep(16) then
+ local size = (pend - pstart + 1) * 512
+ block.register(util.binUUID(puuid), dname .. "p" .. (i - 1), kernel.modules.partition.buildDevice(device, pstart * lba, size))
+ end
+ end
+ if device.close then
+ device.close(h)
+ end
+end
+
+kernel.modules.block.scanners.gpt = scan
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_io.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_io.lua
index 050a4c8ae9..99e6f3f710 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_io.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_io.lua
@@ -1,5 +1,5 @@
local io = {}
-
+local threading
-------------------------------------------------------------------------------
local function buffWrap(b)
@@ -37,9 +37,9 @@ function io.input(file)
elseif not io.type(file) then
error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2)
end
- kernel.modules.threading.currentThread.io_input = file
+ threading.currentThread.io_input = file
end
- return kernel.modules.threading.currentThread.io_input
+ return threading.currentThread.io_input
end
function io.lines(filename, ...)
@@ -95,10 +95,10 @@ function io.popen(prog, mode, ...)
if mode == "w" then
local newin, sink = kernel.modules.buffer.pipe()
- local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
+ local thread = threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
- thread.io_output = kernel.modules.threading.currentThread.io_output
- thread.io_error = kernel.modules.threading.currentThread.io_error
+ thread.io_output = threading.currentThread.io_output
+ thread.io_error = threading.currentThread.io_error
thread.io_input = newin
sink.thread = thread.pid
@@ -106,10 +106,10 @@ function io.popen(prog, mode, ...)
elseif mode == "r" or mode == nil then
local out, newout = kernel.modules.buffer.pipe()
- local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
+ local thread = threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
thread.io_output = newout
- thread.io_error = kernel.modules.threading.currentThread.io_error
- thread.io_input = kernel.modules.threading.currentThread.io_input
+ thread.io_error = threading.currentThread.io_error
+ thread.io_input = threading.currentThread.io_input
out.thread = thread.pid
return out
@@ -117,9 +117,9 @@ function io.popen(prog, mode, ...)
local newin, sink = kernel.modules.buffer.pipe()
local out, newout = kernel.modules.buffer.pipe()
- local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
+ local thread = threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
thread.io_output = newout
- thread.io_error = kernel.modules.threading.currentThread.io_error
+ thread.io_error = threading.currentThread.io_error
thread.io_input = newin
sink.thread = thread.pid
@@ -127,11 +127,11 @@ function io.popen(prog, mode, ...)
return sink, out
elseif mode == "" then
- local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
+ local thread = threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow
- thread.io_output = kernel.modules.threading.currentThread.io_output
- thread.io_error = kernel.modules.threading.currentThread.io_error
- thread.io_input = kernel.modules.threading.currentThread.io_input
+ thread.io_output = threading.currentThread.io_output
+ thread.io_error = threading.currentThread.io_error
+ thread.io_input = threading.currentThread.io_input
return thread.pid
end
@@ -153,9 +153,9 @@ function io.output(file)
elseif not io.type(file) then
error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2)
end
- kernel.modules.threading.currentThread.io_output = file
+ threading.currentThread.io_output = file
end
- return kernel.modules.threading.currentThread.io_output
+ return threading.currentThread.io_output
end
function io.read(...)
@@ -198,3 +198,7 @@ setmetatable(io, {__index = function(_, k)
end})
_G.io = io
+
+function start()
+ threading = kernel.modules.threading
+end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_network.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_network.lua
deleted file mode 100644
index bad9559977..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_network.lua
+++ /dev/null
@@ -1,483 +0,0 @@
---local network = require "network"
-
-local cfg = {
- routereq_retry = 20, --Seconds betwen route seek retries
- routereq_drop = 115, --Seconds to stop seeking route
- routereq_relayage = 3, --Timeout added to each request re-sent
- route_relayage = 3, --Age added to each route sent
- route_exp = 300/2, --Remove route after being not used for this time
- route_recheck = 320/2, --Recheck each route after this amount of time
- route_age_max = 660, -- Max age of route
- ttl = 32 --Time to live
-}
-kernel.modules.procfs.data.sys.net = cfg
-
-local _rawSend
-local isAccessible
-local getNodes
---local getInterfaceInfo
-local startNetwork
-local resetRouting
-
-local dataHandler --Layer 2 data handler
-
-accessibleHosts = {}
-nodes = {}
-
-------------------------
---Layer 1
-
-local initated = false
-
-function start()
- if initated then return end
- initated = true
-
- --local filesystem = require "filesystem"
-
- local drivers = {}
-
- for file in kernel.modules.vfs.list("/lib/modules/network") do
-
- --print("Loading driver:", file)
- local ld, reason = kernel.userspace.loadfile("/lib/modules/network/"..file, nil, _G)
- if not ld then
- kernel.io.println("Network driver loading failed: " .. tostring(reason))
- end
- drivers[file] = {driver = ld()}
-
- local eventHandler = {}--EVENT HANDLERS FOR DRIVER EVENTS
- --eventHandler.debug = print
- eventHandler.debug = function()end
-
- function eventHandler.newHost(node, address)--New interface in net node
- --print("New host: ",node, address)
- accessibleHosts[address] = {driver = drivers[file], node = node}
- nodes[node].hosts[address] = address--mark host in node
- end
-
- function eventHandler.newInterface(interface, selfAddr, linkName)--New node
- --print("New interface: ",interface, selfaddr)
- nodes[interface] = {hosts={}, driver = drivers[file], selfAddr = selfAddr, linkName = linkName}
- end
-
- function eventHandler.delInterface(interface)
- nodes[interface] = nil
- for addr, host in pairs(accessibleHosts) do
- if host.node == interface then
- accessibleHosts[addr] = nil
- end
- end
- resetRouting()
- end
-
- function eventHandler.recvData(data, node, origin)
- dataHandler(data, node, origin)
- end
-
- function eventHandler.setListener(evt, listener)
- return kernel.modules.keventd.listen(evt, function(...)
- local args = {...}
- local res = {pcall(function()listener(table.unpack(args))end)}
- if not res[1] then
- kernel.io.println("ERROR IN NET EVENTHANDLER["..file.."]:"..tostring(res[2]))
- end
- return table.unpack(res,2)
- end)
- end
-
- drivers[file].handle = drivers[file].driver.start(eventHandler)
- end
-
- _rawSend = function(addr, node, data)
- --print("TrySend:",node,addr,":",data)
- if accessibleHosts[addr] then
- accessibleHosts[addr].driver.driver.send(accessibleHosts[addr].driver.handle, node, addr, data)
- end
- end
-
- isAccessible = function(addr)
- if not accessibleHosts[addr] then return end
- return accessibleHosts[addr].node, accessibleHosts[addr].driver
- end
-
- getNodes = function()
- return nodes
- end
-
- getInterfaceInfo = function(interface)
- if nodes[interface] then
- return nodes[interface].driver.driver.info(interface)
- end
- end
-
- kernel.io.println("Link Control initated")
- startNetwork()
- kernel.io.println("Network initated")
-end
-
-------------------------
---Layer 2
-
-startNetwork = function()
- local rawSend
- --local send
-
- local routeRequests = {} -- Table by dest addressed of tables {type = T[, data=..]}, types: D(own waiting data), R(route request for someone), E(routed data we should be able to route..)
- local routes = {} --Table of pairs -> [this or route] / {thisHost=true} / {router = [addr]}
-
- resetRouting = function()
- for k, route in pairs(routes) do
- if not route.thisHost then
- routes[k] = nil
- end
- end
- end
-
- routes[computer.address()] = {thisHost=true}
-
- -----Data out
-
- local function onRecv(origin, data)
- --computer.pushSignal("network_message", origin, data)
- kernel.modules.libnetwork.handleData(origin, data)
- end
-
- -----Sending
-
- local function sendDirectData(addr, data)--D[ttl-byte][data]
- return rawSend(addr, string.pack("c1B", "D", cfg.ttl)..data)
- end
-
- local function sendRoutedData(addr, data)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message
- local nodes = getNodes()
- local msg = string.pack("c1Bs1s1", "E", cfg.ttl, addr, nodes[routes[addr].node].selfAddr) .. data
- local node, driver = isAccessible(addr)
- _rawSend(node and addr or routes[addr].router, node or routes[addr].node, msg)
- end
-
- local function sendRoutedDataAs(addr, origin, data, ottl)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message
- local msg = string.pack("c1Bs1s1", "E", ottl - 1, addr, origin) .. data
- local node, driver = isAccessible(addr)
- _rawSend(node and addr or routes[addr].router, node or routes[addr].node, msg)
- end
-
- local function sendRouteRequest(addr, age)--R[ttl-byte][Addr len][Requested addr][age]
- local request = string.pack(">c1Bs1H", "R", cfg.ttl, addr, math.floor(age))
- local nodes = getNodes()
- local sent = {}
- for node, n in pairs(nodes) do
- for host in pairs(n.hosts)do
- if not sent[host]then
- sent[host] = true
- --_rawSend(host, node, base..toByte(n.selfAddr:len())..n.selfAddr)
- _rawSend(host, node, request)
- end
- end
- end
- sent = nil
- end
-
- local function sendHostFound(dest, age, addr, dist)--H[ttl-byte][age-short][distance][Found host]
- --kernel.io.println("found "..addr.." for "..dest)
- if dest ~= "localhost" then
- return rawSend(dest, "H"..string.pack(">BHB", cfg.ttl, math.floor(age + cfg.route_relayage), dist)..addr)
- end
- end
-
- rawSend = function(addr, data)
- local node, driver = isAccessible(addr)
- if node then
- _rawSend(addr, node, data)
- return true
- end
- return false
- end
-
- send = function(addr, data)
- if type(addr) ~= "string" then error("Address must be string!!") end
- if not sendDirectData(addr, data) then--Try send directly
- if routes[addr] then
- if routes[addr].thisHost then
- onRecv("localhost", data)--it's this host, use loopback
- else
- sendRoutedData(addr, data)--We know route, try to send it that way
- routes[addr].active = computer.uptime()
- end
- else
- --route is unknown, we have to request it if we haven't done so already
- if not routeRequests[addr] then
- routeRequests[addr] = {update = computer.uptime(), timeout = computer.uptime()}
- routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data}
- sendRouteRequest(addr, 0)
- else
- routeRequests[addr].timeout = computer.uptime()
- routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data}
- end
- end
- end
- end
-
- local function processRouteRequests(host, age, dist)
- if routeRequests[host] then
- age = age or (computer.uptime() - routeRequests[host].timeout)
- for t, request in pairs(routeRequests[host]) do
- if type(request) == "table" then
- if request.type == "D" then
- sendRoutedData(host, request.data)
- elseif request.type == "E" then
- if request.ttl-1 > 1 then
- sendRoutedDataAs(host, request.origin, request.data, request.ttl)
- end
- elseif request.type == "R" then
- sendHostFound(request.host, age, host, dist or 1)
- end
- end
- end
- routeRequests[host] = nil
- end
- end
-
- local function checkRouteDest(dest, origin, node, data)
- local nodes = getNodes()
- if dest == nodes[node].selfAddr then
- return true
- elseif routes[dest] and routes[dest].thisHost then
- return true
- end
- return false
- end
-
- bindAddr = function(addr)
- routes[addr] = {thisHost=true, dist = 0}
- processRouteRequests(addr, 0, 1)
- end
-
- kernel.modules.keventd.listen("hostname", function(_, name) bindAddr(name)end)
-
- dataHandler = function(data, node, origin)
- if data:sub(1,1) == "D" then --Direct data
- onRecv(origin, data:sub(3))
- elseif data:sub(1,1) == "E" then --Routed data
- --local ttl = data:byte(2)
- --local dest, destlen = readSizeStr(data, 3)
- --local orig, origlen = readSizeStr(data, 3+destlen)
- local ttl, dest, orig, dstart = string.unpack(">Bs1s1", data, 2)
- local dat = data:sub(dstart)
-
-
- if checkRouteDest(dest, orig, node, dat) then
- onRecv(orig, dat)
- else
- local _node, driver = isAccessible(dest)
- if _node then --Have direct route
- if ttl-1 > 0 then
- kernel.io.println("Direct hop orig="..orig.." -> dest="..dest)
- sendRoutedDataAs(dest, orig, dat, ttl)
- end
- else
- if routes[dest] then --Have route
- if ttl-1 > 0 then
- sendRoutedDataAs(dest, orig, dat, ttl)
- routes[dest].active = computer.uptime()
- end
- else --Need to find route
- if not routeRequests[dest] then
- routeRequests[dest] = {update = computer.uptime(), timeout = computer.uptime()}
- end
- routeRequests[dest].timeout = computer.uptime()
- if not routeRequests[dest] then routeRequests[dest] = {update = computer.uptime(), timeout = computer.uptime()} end
- routeRequests[dest][#routeRequests[dest]+1] = {type = "E", origin = orig, ttl = ttl, data = dat}
- sendRouteRequest(dest, 0)
- end
- end
- end
- elseif data:sub(1,1) == "R" then --Route request
- --local dest, l = readSizeStr(data, 3)
- local nttl, dest, age = string.unpack(">Bs1H", data, 2)
- if age > cfg.routereq_drop then
- return
- end
- if not routeRequests[dest] then
-
- --check if accessible interface
- local nodes = getNodes()
- for _node, n in pairs(nodes) do
- if _node ~= node then --requested host won't ever be in same node
- for host in pairs(n.hosts)do
- if host == dest then
- --Found it!
- sendHostFound(origin, 0, dest, 1)
- return
- end
- end
- end
- end
-
- --check if route known
- if routes[dest] then
- if routes[dest].thisHost then
- --sendHostFound(origin, nodes[node].selfAddr)
- sendHostFound(origin, 0 , dest, 1)
- elseif routes[dest].router ~= origin then--Router might have rebooted and is asking about route
- sendHostFound(origin, computer.uptime() - routes[dest].age, dest, routes[dest].dist + 1)
- --routes[dest].active = computer.uptime()
- end
- return
- end
-
- if isAccessible(dest) then
- kernel.io.println("Attempt to seek route to direct host(0x1WAT)")
- kernel.io.println("seeker " .. origin .. " to " .. dest)
- return
- end
-
- routeRequests[dest] = {update = computer.uptime(), timeout = computer.uptime() - age}
- routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin}
-
- --local nttl = data:byte(2)-1
- if nttl > 1 then
- local sent = {}
- --Bcast request
- for _node, n in pairs(nodes) do
- if _node ~= node then --We mustn't send it to origin node
- for host in pairs(n.hosts) do
- if not sent[host] then
- sent[host] = true
- --resend route request
- local msg = string.pack(">c1Bs1H", "R", nttl - 1, dest, age + cfg.routereq_relayage)
- _rawSend(host, _node, msg)
- end
- end
- end
- end
- end
- sent = nil
- else
- if isAccessible(dest) then
- kernel.io.println("Attempt to seek route to direct host(0x2WAT)")
- kernel.io.println("seeker " .. origin .. " to " .. dest)
- return
- end
- --we've already requested this addr so if we get the route
- --we'll respond. TODO: Duplicates?
- if computer.uptime() - routeRequests[dest].timeout > age then
- routeRequests[dest].timeout = computer.uptime() - age
- end
- routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin}
- end
- elseif data:sub(1,1) == "H" then --Host found
- local nttl, age, dist, n = string.unpack(">BHB", data, 2)
- local host = data:sub(n)
-
- if not isAccessible(host) then
- if not routes[host] then
- routes[host] = {
- router = origin,
- node = node,
- age = computer.uptime() - age,
- active = computer.uptime(),
- dist = dist
- }
- processRouteRequests(host, age, dist + 1)
- else
- if (routes[host].dist > dist) or
- ((routes[host].age < computer.uptime() - age) and (routes[host].dist >= dist)) then
- routes[host] = {
- router = origin,
- node = node,
- age = computer.uptime() - age,
- active = routes[host].active,
- dist = dist
- }
- end
- end
- end
- end
- end
-
- --network.core.setCallback("send", send)
- --network.core.setCallback("bind", bindAddr)
-
- ---------------
- --Network stats&info
-
- function getInfo()
- local res = {}
-
- res.interfaces = {}
- for k, node in pairs(getNodes())do
- res.interfaces[k] = {selfAddr = node.selfAddr, linkName = node.linkName}
- end
- return res
- end
-
- function getRoutingTable()
- local res = {}
- local now = computer.uptime()
-
- for k,v in pairs(routeRequests) do
- res[k] = {router = "", interface = "", age = now - v.timeout, dist = 0}
- end
-
- for k,v in pairs(routes) do
- if v.router then
- res[k] = {router = v.router, interface = v.node, age = now - v.age, dist = v.dist}
- elseif v.thisHost then
- res[k] = {router = computer.address(), interface = "lo", age = 0, dist = 0}
- end
- end
- return res
- end
-
- function getArpTable(interface)
- local res = {}
- for k in pairs(nodes[interface].hosts)do
- table.insert(res, k)
- end
- return res
- end
-
- --network.core.setCallback("netstat", getInfo)
- --network.core.setCallback("intstat", getInterfaceInfo)
- --network.core.setCallback("routetab", getRoutingTable)
- --network.core.setCallback("arptab", getArpTable)
-
- --network.core.lockCore()
-
- kernel.modules.timer.add(function()
- local now = computer.uptime()
- --Route request timeouts, re-requests
- for host, request in pairs(routeRequests) do
- if now - request.update >= cfg.routereq_retry then
- if routes[host] then
- processRouteRequests(host, now - routes[host].age, routes[host].dist + 1)
- else
- sendRouteRequest(host, now - request.update)
- request.update = computer.uptime()
- end
- end
- if now - request.timeout >= cfg.routereq_drop then
- routeRequests[host] = nil
- end
- end
-
- --Route timeouts, rechecks,
- for host, route in pairs(routes) do
- if not route.thisHost then
- local age = now - route.age
- if age >= cfg.route_recheck then
- sendRouteRequest(host, 0)
- end
- if age >= cfg.route_age_max then
- routes[host] = nil
- else
- if now - route.active >= cfg.route_exp then
- routes[host] = nil
- end
- end
- end
- end
- end, 5)
-end
-
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_nfc.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_nfc.lua
index 109e59fa26..737004cc75 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_nfc.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_nfc.lua
@@ -1,50 +1,34 @@
-programmers = {}
+local block = kernel.modules.block
-local function buildDevfs()
- for file in pairs(kernel.modules.devfs.data) do
- if file:match("^nfc") then
- kernel.modules.devfs.data[file] = nil
- end
- end
- for k, nfc in ipairs(programmers) do
- kernel.modules.devfs.data["nfc" .. nfc:sub(1,4):upper()] = {
- __type = "f",
- open = function(hnd, mode)
- if mode == "r" then error("Invalid mode") end
- hnd.nfc = nfc
- end,
- size = function()
- return 2048
- end,
- write = function(h, data)
- if component.invoke(nfc, "isDataWaiting") then
- component.invoke(nfc, "clearNFCData")
- end
- component.invoke(nfc, "writeNFCData", data)
- return true
- end,
- }
- end
+local function buildDevice(uuid)
+ return {
+ __type = "f",
+ open = function(hnd, mode)
+ if mode == "r" then error("Invalid mode") end
+ hnd.nfc = uuid
+ end,
+ size = function()
+ return 2048
+ end,
+ write = function(h, data)
+ if component.invoke(uuid, "isDataWaiting") then
+ component.invoke(uuid, "clearNFCData")
+ end
+ component.invoke(uuid, "writeNFCData", data)
+ return true
+ end,
+ }
end
local function onComponentAdded(_, address, componentType)
if componentType == "NFCProgrammer" then
- programmers[#programmers + 1] = address
- buildDevfs()
+ block.register(address, "nfc" .. address:sub(1,4):upper(), buildDevice(address))
end
end
local function onComponentRemoved(_, address, componentType)
if componentType == "NFCProgrammer" then
- local t
- for i, nfc in ipairs(programmers) do
- if nfc == address then
- t = i
- break
- end
- end
- table.remove(programmers, t)
- buildDevfs()
+ block.unregister(address)
end
end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_tape.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_tape.lua
index 7d8518b84a..f343cc01a6 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_tape.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/17_tape.lua
@@ -1,78 +1,56 @@
-tapes = {}
+local block = kernel.modules.block
-local function buildDevfs()
- for file in pairs(kernel.modules.devfs.data) do
- if file:match("^tape") then
- kernel.modules.devfs.data[file] = nil
- end
- end
- for k, tape in ipairs(tapes) do
- kernel.modules.devfs.data["tape" .. tape:sub(1,4):upper()] = {
- __type = "f",
- open = function(hnd)
- if not component.invoke(tape, "isReady") then
- error("Tape drive is not ready")
- end
- component.invoke(tape, "seek", -math.huge)
- hnd.tape = tape
- hnd.pos = 0
- end,
- size = function()
- return component.invoke(tape, "getSize")
- end,
- write = function(h, data)
- component.invoke(tape, "write", data)
- h.pos = h.pos + #data
- return not (h.pos >= component.invoke(tape, "getSize"))
- --TODO: do this correctly
- end,
- read = function(h, len)
- if h.pos >= component.invoke(tape, "getSize") then
- return
- end
- h.pos = h.pos + len
- return component.invoke(tape, "read", len)
- end,
- seek = function(h, whence, offset)
- if whence == "end" then
- h.pos = h.pos + component.invoke(tape, "seek", component.invoke(tape, "getSize") - h.pos - (offset or 0))
- elseif whence == "set" then
- h.pos = h.pos + component.invoke(tape, "seek", (offset or 0) - h.pos)
- else
- h.pos = h.pos + component.invoke(tape, "seek", offset or 0)
- end
- return math.floor(h.pos)
+local function buildDevice(tape)
+ return {
+ __type = "f",
+ open = function(hnd)
+ if not component.invoke(tape, "isReady") then
+ error("Tape drive is not ready")
end
- }
- end
+ component.invoke(tape, "seek", -math.huge)
+ hnd.tape = tape
+ hnd.pos = 0
+ end,
+ size = function()
+ return component.invoke(tape, "getSize")
+ end,
+ write = function(h, data)
+ component.invoke(tape, "write", data)
+ h.pos = h.pos + #data
+ return not (h.pos >= component.invoke(tape, "getSize"))
+ --TODO: do this correctly
+ end,
+ read = function(h, len)
+ if h.pos >= component.invoke(tape, "getSize") then
+ return
+ end
+ h.pos = h.pos + len
+ return component.invoke(tape, "read", len)
+ end,
+ seek = function(h, whence, offset)
+ if whence == "end" then
+ h.pos = h.pos + component.invoke(tape, "seek", component.invoke(tape, "getSize") - h.pos - (offset or 0))
+ elseif whence == "set" then
+ h.pos = h.pos + component.invoke(tape, "seek", (offset or 0) - h.pos)
+ else
+ h.pos = h.pos + component.invoke(tape, "seek", offset or 0)
+ end
+ return math.floor(h.pos)
+ end
+ }
end
local function onComponentAdded(_, address, componentType)
if componentType == "tape_drive" then
- tapes[#tapes + 1] = address
- buildDevfs()
+ block.register(address, "tape" .. address:sub(1,4):upper(), buildDevice(address))
end
end
local function onComponentRemoved(_, address, componentType)
if componentType == "tape_drive" then
- local t
- for i, tape in ipairs(tapes) do
- if tape == address then
- t = i
- break
- end
- end
- table.remove(tapes, t)
- buildDevfs()
+ block.unregister(address)
end
end
---function start()
--- for tape, t in component.list("tape_drive") do
--- onComponentAdded(_, tape, t)
--- end
---end
-
kernel.modules.keventd.listen("component_added", onComponentAdded)
kernel.modules.keventd.listen("component_removed", onComponentRemoved)
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/19_libnetwork.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/19_libnetwork.lua
deleted file mode 100644
index 49184da7cd..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/19_libnetwork.lua
+++ /dev/null
@@ -1,279 +0,0 @@
---local event = require "event"
-
-local driver
-
-network = {}
-internal = {}
-
-
-------------
---ICMP
-
-network.icmp = {}
-internal.icmp = {}
-
-local pingid = 0
-function network.icmp.ping(addr, payload)
- pingid = pingid + 1
- driver.send(addr, "IP"..computer.address()..":"..tostring(pingid)..":"..payload)
- return pingid
-end
-
-function internal.icmp.handle(origin, data)
- if data:sub(2,2) == "P" then
- local matcher = data:sub(3):gmatch("[^:]+")
- local compid = matcher()
- local id = tonumber(matcher())
- local payload = matcher()
- if compid == computer.address() then
- computer.pushSignal("ping_reply", origin, tonumber(id), payload)
- else
- driver.send(origin, data)
- end
- end
-end
-
-------------
---Datagrams - UDP like protocol
-
-network.udp = {}
-internal.udp = {ports = {}}
-
-function internal.udp.checkPortRange(port)
- if port < 0 or port > 65535 then error("Wrong port!")end
-end
-
-function network.udp.open(port)
- if not port then
- port = 49151 + math.floor(math.random() * 16384)
- --TODO: check allocated port
- end
- internal.udp.checkPortRange(port)
- if kernel.modules.threading.currentThread then
- internal.udp.ports[port] = kernel.modules.threading.currentThread.pid
- else
- internal.udp.ports[port] = true
- end
- return port
-end
-
-function network.udp.close(port)
- internal.udp.checkPortRange(port)
- internal.udp.ports[port] = nil
-end
-
-function network.udp.send(addr, port, data)
- internal.udp.checkPortRange(port)
- driver.send(addr, "D".. string.char(math.floor(port/256))..string.char(port%256)..data)
-end
-
-function internal.udp.handle(origin, data)
- local port = data:byte(2)*256 + data:byte(3)
- if internal.udp.ports[port] then
- computer.pushSignal("datagram", origin, port, data:sub(4))
- end
-end
-
-function internal.udp.cleanProcess(thread)
- for port, pid in pairs(internal.udp.ports) do
- if pid == thread.pid then
- internal.udp.ports[port] = nil
- end
- end
-end
-
------------
---TCP - TCP like protocol
-
---O[port,2B][openers channel,2B] --Try open connection
---A[opened channel,2B][openers channel,2B] --Accept connection
---R[openers channel,2B] --Reject connection i.e. closed port
---C[remote channel,2B] --Close connection(user request or adta at closed/wrong channel)
---D[remote channel,2B][data] --Data
-
-network.tcp = {}
-internal.tcp = {ports = {}, channels = {}, freeCh = 1}
-
-function network.tcp.listen(port)
- internal.udp.checkPortRange(port)
- if kernel.modules.threading.currentThread then
- internal.tcp.ports[port] = kernel.modules.threading.currentThread.pid
- else
- internal.tcp.ports[port] = true
- end
-end
-
-function network.tcp.unlisten(port)
- internal.udp.checkPortRange(port)
- internal.tcp.ports[port] = nil
-end
-
-function network.tcp.open(addr, port)
- if not port then
- port = 49151 + math.floor(math.random() * 16384)
- --TODO: check allocated port
- end
- internal.udp.checkPortRange(port)
- local ch = internal.tcp.freeCh
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then
- internal.tcp.freeCh = internal.tcp.channels[ch].next
- else
- internal.tcp.freeCh = #internal.tcp.channels+2
- end
- --kernel.io.println("TCP: use ch " .. ch)
- internal.tcp.channels[ch] = {open = false, waiting = true, addr = addr, port = port}--mark openning
- if kernel.modules.threading.currentThread then
- internal.tcp.channels[ch].owner = kernel.modules.threading.currentThread.pid
- end
- driver.send(addr, "TO".. string.char(math.floor(port/256))..string.char(port%256).. string.char(math.floor(ch/256))..string.char(ch%256))
- return ch
-end
-
-function network.tcp.close(channel)
- if internal.tcp.channels[channel] then
- if internal.tcp.channels[channel].open or internal.tcp.channels[channel].waiting then
- driver.send(internal.tcp.channels[channel].addr, "TC".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256))
- end
- internal.tcp.channels[channel] = {next = internal.tcp.freeCh}
- internal.tcp.freeCh = channel
- --computer.pushSignal("tcp_close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port)
- end
-end
-
-function network.tcp.send(channel, data)
- if internal.tcp.channels[channel] and internal.tcp.channels[channel].open then
- driver.send(internal.tcp.channels[channel].addr, "TD".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256)..data)
- return true
- end
- return false
-end
-
-function internal.tcp.handle(origin, data)
- if data:sub(2,2) == "O" then
- local port = data:byte(3)*256 + data:byte(4)
- local rchan = data:byte(5)*256 + data:byte(6)
-
- if internal.tcp.ports[port] then
- local ch = internal.tcp.freeCh
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then
- internal.tcp.freeCh = internal.tcp.channels[ch].next
- else
- internal.tcp.freeCh = #internal.tcp.channels+2
- end
- --kernel.io.println("TCP: use ch " .. ch)
- internal.tcp.channels[ch] = {open = true, remote = rchan, addr = origin, port = port}
- if type(internal.tcp.ports[port]) == "number" then
- internal.tcp.channels[ch].owner = internal.tcp.ports[port]
- end
- driver.send(origin, "TA".. string.char(math.floor(ch/256))..string.char(ch%256) .. string.char(math.floor(rchan/256)) .. string.char(rchan%256))
- computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port, "incoming")
- else
- driver.send(origin, "TR".. string.char(math.floor(rchan/256))..string.char(rchan%256))
- end
- elseif data:sub(2,2) == "R" then
- local ch = data:byte(3)*256 + data:byte(4)
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then
- computer.pushSignal("tcp" ,"close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port)
- internal.tcp.channels[ch] = {next = internal.tcp.freeCh}
- internal.tcp.freeCh = ch
- end
- elseif data:sub(2,2) == "A" then
- local remote = data:byte(3)*256 + data:byte(4)
- local ch = data:byte(3)*256 + data:byte(4)
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then
- internal.tcp.channels[ch].waiting = nil
- internal.tcp.channels[ch].open = true
- internal.tcp.channels[ch].remote = remote
- computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port)
- end
- elseif data:sub(2,2) == "C" then
- local ch = data:byte(3)*256 + data:byte(4)
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then
- internal.tcp.channels[ch] = {next = internal.tcp.freeCh}
- internal.tcp.freeCh = ch
- computer.pushSignal("tcp" ,"close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port)
- end
- elseif data:sub(2,2) == "D" then --TODO: check source
- local ch = data:byte(3)*256 + data:byte(4)
- if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then
- computer.pushSignal("tcp", "message", ch, data:sub(5), internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port)
- end
- end
-end
-
-function internal.tcp.cleanup()
- for channel, _ in pairs(internal.tcp.channels) do
- if internal.tcp.channels[channel].open or internal.tcp.channels[channel].waiting then
- if internal.tcp.channels[channel].open or internal.tcp.channels[channel].waiting then
- driver.send(internal.tcp.channels[channel].addr, "TC".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256))
- end
- internal.tcp.channels[channel] = {next = internal.tcp.freeCh}
- internal.tcp.freeCh = channel
- end
- end
-end
-
-function internal.tcp.cleanProcess(thread)
- for channel, _ in pairs(internal.tcp.channels) do
- if internal.tcp.channels[channel].owner == thread.pid then
- network.tcp.close(channel)
- end
- end
- for port, pid in pairs(internal.tcp.ports) do
- if pid == thread.pid then
- internal.tcp.ports[port] = nil
- end
- end
-end
-
------------
---IP
-
-network.ip = {}
-
-function network.ip.bind(addr)
- driver.bind(addr)
-end
-
-------------
---Data processing
-
-function handleData(origin, data)
- if data:sub(1,1) == "I" then internal.icmp.handle(origin, data)
- elseif data:sub(1,1) == "T" then internal.tcp.handle(origin, data)
- elseif data:sub(1,1) == "D" then internal.udp.handle(origin, data) end
-end
-
-
-------------
---Info
-
-network.info = {}
-network.info.getInfo = function(...)return driver.netstat(...) end
-network.info.getInterfaceInfo = function(...)return driver.intstat(...) end
-network.info.getRoutes = function(...)return driver.routetab(...) end
-network.info.getArpTable = function(...)return driver.arptab(...) end
-
-------------
-
-kernel.userspace.package.preload.network = network
-
-function start()
- driver = {
- send = kernel.modules.network.send,
- bind = kernel.modules.network.bindAddr,
- netstat = kernel.modules.network.getInfo,
- intstat = kernel.modules.network.getInterfaceInfo,
- routetab = kernel.modules.network.getRoutingTable,
- arptab = kernel.modules.network.getArpTable,
- }
-
- kernel.modules.gc.onShutdown(function()
- internal.tcp.cleanup()
- end)
-
- kernel.modules.gc.onProcessKilled(function(thread)
- internal.tcp.cleanProcess(thread)
- internal.udp.cleanProcess(thread)
- end)
-end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/20_threading.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/20_threading.lua
index b146b86383..2df005a80a 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/20_threading.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/20_threading.lua
@@ -1,36 +1,39 @@
threads = {}
currentThread = nil
+eventFilters = {signal = {}}
-local threadMode = {
- top = 0,
- child = 1
-}
-
-local function getPendingThreads()
- local res = {}
- for _, thread in ipairs(threads) do
- if thread.coro and #thread.eventQueue > 0 then
- res[#res + 1] = thread
+function countThreadSignals(thread, signal)
+ local n = 0
+ local first = 0
+ for i, sig in ipairs(thread.eventQueue) do
+ if sig[1] == signal then
+ n = n + 1
+ if first < 1 then
+ first = i
+ end
end
end
- return res
+ return n, first
end
-local function getResumableThreads(threads_)
- local res = {}
- for _,thread in ipairs(threads_) do
- for n,event in ipairs(thread.eventQueue) do
- if event[1] == thread.currentHandler then
- table.remove(thread.eventQueue, n)
- thread.currentEvent = event
- res[#res + 1] = thread
- break
- end
+local deadline = math.huge
+local pullSignal = computer.pullSignal
+computer.pullSignal = function()
+ pcall(kernel._println, "Attempted to use non threaded signal pulling")
+ pcall(kernel._println, debug.traceback())
+ kernel.panic()
+end
+
+function eachThread(func)
+ for _, thread in ipairs(threads) do
+ if thread.coro then
+ func(thread)
end
end
- return res
end
+--------------
+
local firstFree = 1
local nextUid = 1
@@ -77,6 +80,7 @@ function spawn(exec, child, name, isthread, _, ...)
end)}
return table.unpack(r, 2)
end),
+ deadline = computer.uptime(),
sandbox = isthread and currentThread.sandbox or kernel.modules.manageg.newsandbox(),
currentHandler = "arg",
currentHandlerArg = nil,
@@ -113,76 +117,106 @@ function spawn(exec, child, name, isthread, _, ...)
terminate = kill
}
+ kernel.io.debug("Spawn thread " .. tostring(name))
+
return thread
end
-function countThreadSignals(thread, signal)
- local n = 0
- local first = 0
- for i, sig in ipairs(thread.eventQueue) do
- if sig[1] == signal then
- n = n + 1
- if first < 1 then
- first = i
- end
+---
+
+local function getPendingThreads()
+ local res = {}
+ for _, thread in ipairs(threads) do
+ if thread.coro then
+ res[#res + 1] = thread
end
end
- return n, first
+ return res
end
-local deadline = math.huge
-local pullSignal = computer.pullSignal
-computer.pullSignal = function()
- pcall(kernel._println, "Attempted to use non threaded signal pulling")
- pcall(kernel._println, debug.traceback())
- kernel.panic()
+local function getResumableThreads(threads_)
+ local res = {}
+ for _,thread in ipairs(threads_) do
+ thread.currentEvent = nil
+ for n,event in ipairs(thread.eventQueue) do
+ if event[1] == thread.currentHandler then
+ table.remove(thread.eventQueue, n)
+ thread.currentEvent = event
+ res[#res + 1] = thread
+ break
+ end
+ end
+ if not thread.currentEvent and thread.deadline <= computer.uptime() then
+ thread.currentEvent = {"timeout"}
+ res[#res + 1] = thread
+ end
+ end
+ return res
end
-
local function processSignals()
deadline = math.huge
for _, thread in ipairs(threads) do
- if (thread.currentHandler == "yield" or thread.currentHandler == "signal")
- and deadline > (tonumber(thread.currentHandlerArg) or math.huge) then
- deadline = thread.currentHandlerArg or math.huge
+ if deadline > (thread.deadline or math.huge) then
+ if not thread.deadline then
+ kernel.io.println("Nil deadline for " .. thread.name .. " on " .. tostring(thread.currentHandler))
+ end
+ deadline = thread.deadline
end
end
- --kernel.io.println("Deadline: "..(deadline - computer.uptime()))
+ --kernel.io.debug("Pull deadline: "..(deadline - computer.uptime()))
local sig = {"signal", pullSignal(deadline - computer.uptime())}
+ local function filt(f, signal, ...)
+ if not signal then
+ return true
+ end
+ if type(f[signal]) == "table" then
+ return filt(f[signal], ...)
+ end
+ return (not f[signal]) and true or (function(...)
+ local s, e = xpcall(f[signal], function(err)
+ kernel._println("Signal filter error:")
+ kernel._println(tostring(e))
+ kernel._println(debug.traceback())
+ kernel.panic("Signal filtering failed")
+ end, ...)
+ return s and e
+ end)(...)
+ end
+ if not filt(eventFilters, table.unpack(sig)) then
+ sig = {}
+ end
+
for _, thread in ipairs(threads) do
if thread.coro then
- local nsig, oldest = countThreadSignals(thread, "signal")
- if nsig > thread.maxPendingSignals then --TODO: make it a bit more intelligent
- table.remove(thread.eventQueue, oldest)
- end
- if thread.currentHandler == "yield" then
+ --[[if thread.currentHandler == "yield" then
--kernel.io.println("yield ck: "..tostring((thread.currentHandlerArg or math.huge) - computer.uptime()))
if (thread.currentHandlerArg or math.huge) <= computer.uptime() then
thread.eventQueue[#thread.eventQueue + 1] = {"yield"}
end
- end
- if thread.cgroups.signal.global then
+ end]]--
+ if thread.cgroups.signal.global and sig[2] then
+ local nsig, oldest = countThreadSignals(thread, "signal")
+ if nsig > thread.maxPendingSignals then --TODO: make it a bit more intelligent
+ table.remove(thread.eventQueue, oldest)
+ end
+
thread.eventQueue[#thread.eventQueue + 1] = sig
end
end
end
end
-function eachThread(func)
- for _, thread in ipairs(threads) do
- if thread.coro then
- func(thread)
- end
- end
-end
+----
local lastYield = computer.uptime()
+yieldTime = 3
function checkTimeout()
local uptime = computer.uptime()
- if uptime - lastYield > 3 then
+ if uptime - lastYield > yieldTime then
return true
end
return false
@@ -190,27 +224,8 @@ end
function start()
while true do
- -- pending = getPendingThreads()
- --local resumable = getResumableThreads(pending)
-
- local pending = {}
- for _, thread in ipairs(threads) do
- if thread.coro and #thread.eventQueue > 0 then
- pending[#pending + 1] = thread
- end
- end
-
- local resumable = {}
- for _,thread in ipairs(pending) do
- for n,event in ipairs(thread.eventQueue) do
- if event[1] == thread.currentHandler then
- table.remove(thread.eventQueue, n)
- thread.currentEvent = event
- resumable[#resumable + 1] = thread
- break
- end
- end
- end
+ local pending = getPendingThreads()
+ local resumable = getResumableThreads(pending)
lastYield = computer.uptime()
while #resumable > 0 do
@@ -218,7 +233,8 @@ function start()
--kernel.io.println("Resume " .. tostring(thread.name) .. " with "
-- .. tostring(type(thread.currentEvent) == "table" and thread.currentEvent[1] or "unknown")
-- ..(thread.currentEvent[2] and (", " .. tostring(thread.currentEvent[2])) or ""))
-
+
+ thread.deadline = math.huge
kernel.modules.manageg.protect(thread.sandbox)
currentThread = thread
local state, reason, arg = coroutine.resume(thread.coro, table.unpack(thread.currentEvent, 2))
@@ -228,11 +244,12 @@ function start()
if not state or coroutine.status(thread.coro) == "dead" then
kill(thread.pid)
if reason then
- kernel.io.println("Thread " .. tostring(thread.name) .. "(" .. tostring(thread.pid) .. ") dead: "
+ kernel.io.println("Thread " .. tostring(thread.name) .. "(" .. tostring(thread.pid) .. ") died: "
.. tostring(reason or "unknown/done") .. ", after "
.. tostring(type(thread.currentEvent) == "table" and thread.currentEvent[1] or "unknown"))
end
else
+ --kernel.io.println("Yield arg from " .. tostring(thread.name) .. ": " .. tostring(arg))
thread.currentEvent = nil
thread.currentHandler = reason
thread.currentHandlerArg = arg
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_threadUtil.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_threadUtil.lua
index 1491697461..3531536fe9 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_threadUtil.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_threadUtil.lua
@@ -43,6 +43,16 @@ function userKill(pid, signal, ...)
return true
end
+function select(timeout, ...)
+ checkArg(1, timeout, "number")
+ local funcs = {}
+ for n, f in ipairs(...) do
+ checkArg(n + 1, f, "function")
+ funcs[n] = coroutine.create(f)
+ end
+
+end
+
function setKillHandler(signal, handler) --WAT
if not kernel.modules.threading.threads[pid]
or not kernel.modules.threading.threads[pid].coro then
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_timer.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_timer.lua
index 2a159166ff..10b8720697 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_timer.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/base/21_timer.lua
@@ -14,7 +14,7 @@ function add(func, time)
if deadline > timer.next then
deadline = timer.next
if thread.currentHandler == "yield" then
- thread.currentHandlerArg = deadline
+ thread.deadline = deadline
end
end
@@ -66,9 +66,9 @@ thread = kernel.modules.threading.spawn(function()
end
end
end
- local dl = deadline
+ thread.deadline = deadline
deadline = math.huge
- coroutine.yield("yield", dl)
+ coroutine.yield("yield")
end
end, 0, "[timer]")
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/loopback.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/loopback.lua
deleted file mode 100644
index 254b151f41..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/loopback.lua
+++ /dev/null
@@ -1,26 +0,0 @@
-local driver = {}
-
-local pktIn,pktOut,bytesIn,bytesOut = 0,0,0,0
-
-function driver.start(eventHandler)
- eventHandler.newInterface("lo", "localhost", "Local Loopback")
- eventHandler.newHost("lo", "localhost")
- return {send = function(data)eventHandler.recvData(data, "lo", "localhost")end}
-end
-
-function driver.send(handle, interface, destination, data)
- if interface == "lo" and destination == "localhost" then
- pktIn, pktOut = pktIn+1,pktOut+1
- bytesIn,bytesOut = bytesIn + data:len(), bytesOut + data:len()
- handle.send(data)
- end
-end
-
-function driver.info(interface)
- if interface == "lo" then
- return pktIn,pktOut,bytesIn,bytesOut
- end
- return 0,0,0,0
-end
-
-return driver
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/modem.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/modem.lua
deleted file mode 100644
index 57e9bb7c4b..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/modem.lua
+++ /dev/null
@@ -1,94 +0,0 @@
---[[
-Communication on port 1!
-Node protocol:
-Hello/broadcast(sent by new host in node): \0 (modem addersses are in event)
-Hi/direct(sent by hosts to new host): \1 (^)
-OHAI/direct(Ack of Hi(\1)) \2
-Host quitting/broadcast \3 (^)
-Data/direct \4[data] (origin from event)
-]]
-local driver = {}
-
-local nodes = {}
-local eventHnd
-
-local PKT_BEACON = "\32"
-local PKT_REGISTER = "\1"
-local PKT_REGISTER_ACK = "\2"
-local PKT_QUIT = "\3"
-local PKT_DATA = "\4"
-
-function driver.start(eventHandler)
- eventHnd = eventHandler
-
- eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data)
- if not nodes[interface] then return end --other kind of modem(possibly tunnel)
-
- eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data)
-
- nodes[interface].pktIn = nodes[interface].pktIn + 1
- nodes[interface].bytesIn = nodes[interface].bytesIn + data:len()
-
- if data:sub(1,1) == PKT_BEACON then
- component.invoke(interface, "send", origin, 1, PKT_REGISTER )
- elseif data:sub(1,1) == PKT_REGISTER then
- eventHandler.newHost(nodes[interface].name, origin)
- component.invoke(interface, "send", origin, 1, PKT_REGISTER_ACK)
- elseif data:sub(1,1) == PKT_REGISTER_ACK then
- eventHandler.newHost(nodes[interface].name, origin)
- elseif data:sub(1,1) == PKT_QUIT then
- eventHandler.delHost(nodes[interface].name, origin)
- elseif data:sub(1,1) == PKT_DATA then
- eventHandler.recvData(data:sub(2), nodes[interface].name, origin)
- end
-
- end)
-
- eventHandler.setListener("component_added", function(_, int, ctype)
- if ctype ~= "modem" then return end
- local name = "eth" .. int:sub(1, 4):upper()
- eventHandler.newInterface(name, int, "Ethernet")
-
- nodes[name] = {modem = int, name = name, pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1}
- nodes[int] = nodes[name]
-
- component.invoke(int, "open", 1)
- component.invoke(int, "broadcast", 1, PKT_BEACON)
-
- eventHandler.newHost(name, int)--register loopback
- end)
-
- eventHandler.setListener("component_removed", function(_, int, ctype)
- if ctype ~= "modem" then return end
- local name = "eth" .. int:sub(1, 4):upper()
- nodes[name] = nil
- nodes[int] = nil
- eventHnd.delInterface(int)
- end)
- return {}
-end
-
-function driver.send(handle, interface, destination, data)
- if nodes[interface] then
- if nodes[interface].modem == destination then
- nodes[interface].pktOut = nodes[interface].pktOut + 1
- nodes[interface].bytesOut = nodes[interface].bytesOut + data:len()
- nodes[interface].pktIn = nodes[interface].pktIn + 1
- nodes[interface].bytesIn = nodes[interface].bytesIn + data:len()
- eventHnd.recvData(data, interface, destination)
- else
- nodes[interface].pktOut = nodes[interface].pktOut + 1
- nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len()
- component.invoke(nodes[interface].modem, "send", destination, 1, PKT_DATA..data)
- end
- end
-end
-
-function driver.info(interface)
- if nodes[interface] then
- return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut
- end
- return 0,0,0,0
-end
-
-return driver
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/tunnel.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/tunnel.lua
deleted file mode 100644
index a6d59401b4..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/modules/network/tunnel.lua
+++ /dev/null
@@ -1,72 +0,0 @@
---For protocol info look to at modem driver
-
-local driver = {}
-
-local nodes = {}
-local eventHnd
-
-function driver.start(eventHandler)
- eventHnd = eventHandler
-
- eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data)
- if not nodes[interface] then return end --other kind of modem(possibly modem)
-
- eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data)
-
- nodes[interface].pktIn = nodes[interface].pktIn + 1
- nodes[interface].bytesIn = nodes[interface].bytesIn + data:len()
-
- if data:sub(1,1) == "H" then
- eventHandler.newHost(nodes[interface].name, origin)
- component.invoke(interface, "send", "I")
- eventHandler.debug("REPL:",interface,origin)
- elseif data:sub(1,1) == "I"then
- eventHandler.newHost(nodes[interface].name, origin)
- elseif data:sub(1,1) == "Q"then
- eventHandler.delHost(nodes[interface].name, origin)
- elseif data:sub(1,1) == "D"then
- eventHandler.recvData(data:sub(2), nodes[interface].name, origin)
- end
-
- end)
-
- for int in component.list("tunnel", true)do
- eventHandler.newInterface("tun" .. int:sub(1, 4):upper(), int, "Tunnel")
-
- nodes["tun" .. int:sub(1, 4):upper()] = {modem = int, name = "tun" .. int:sub(1, 4):upper(), pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1}
- nodes[int] = nodes["tun" .. int:sub(1, 4):upper()]
-
- component.invoke(int, "send", "H")
-
- eventHandler.newHost("tun" .. int:sub(1, 4):upper(), int)--register loopback
- end
-
- --eventHandler.newInterface("lo", "localhost", "Local Loopback")
- --eventHandler.newHost("lo", "localhost")
- return {}
-end
-
-function driver.send(handle, interface, destination, data)
- if nodes[interface] then
- if nodes[interface].modem == destination then
- nodes[interface].pktOut = nodes[interface].pktOut + 1
- nodes[interface].bytesOut = nodes[interface].bytesOut + data:len()
- nodes[interface].pktIn = nodes[interface].pktIn + 1
- nodes[interface].bytesIn = nodes[interface].bytesIn + data:len()
- eventHnd.recvData(data, interface, destination)
- else
- nodes[interface].pktOut = nodes[interface].pktOut + 1
- nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len()
- component.invoke(nodes[interface].modem, "send", "D"..data)
- end
- end
-end
-
-function driver.info(interface)
- if nodes[interface] then
- return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut
- end
- return 0,0,0,0
-end
-
-return driver
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/sides.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/sides.lua
new file mode 100644
index 0000000000..a17d3f66b8
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/sides.lua
@@ -0,0 +1,62 @@
+local sides = {
+ [0] = "bottom",
+ [1] = "top",
+ [2] = "back",
+ [3] = "front",
+ [4] = "right",
+ [5] = "left",
+ [6] = "unknown",
+
+ bottom = 0,
+ top = 1,
+ back = 2,
+ front = 3,
+ right = 4,
+ left = 5,
+ unknown = 6,
+
+ down = 0,
+ up = 1,
+ north = 2,
+ south = 3,
+ west = 4,
+ east = 5,
+
+ negy = 0,
+ posy = 1,
+ negz = 2,
+ posz = 3,
+ negx = 4,
+ posx = 5,
+
+ forward = 3
+}
+
+local metatable = getmetatable(sides) or {}
+
+-- sides[0..5] are mapped to itertable[1..6].
+local itertable = {
+ sides[0],
+ sides[1],
+ sides[2],
+ sides[3],
+ sides[4],
+ sides[5]
+}
+
+-- Future-proofing against the possible introduction of additional
+-- logical sides (e.g. [7] = "all", [8] = "none", etc.).
+function metatable.__len(sides)
+ return #itertable
+end
+
+-- Allow `sides` to be iterated over like a normal (1-based) array.
+function metatable.__ipairs(sides)
+ return ipairs(itertable)
+end
+
+setmetatable(sides, metatable)
+
+-------------------------------------------------------------------------------
+
+return sides
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/lib/term.lua b/src/main/resources/assets/opencomputers/loot/plan9k/lib/term.lua
index b4f0e35c16..dfda3aef45 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/lib/term.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/lib/term.lua
@@ -1,3 +1,5 @@
+local component = require "component"
+
local term = {}
term.escape = "\x1b"
@@ -278,6 +280,10 @@ function term.getInfo()
return gpu, screen
end
+function term.gpu()
+ local addr = term.getInfo()
+ return component.proxy(addr)
+end
function term.clear()
@@ -317,7 +323,15 @@ function term.isAvailable()
end
function term.setCursorBlink(enabled)
+ if enabled then
+ io.write("\x1b[?25h")
+ else
+ io.write("\x1b[?25l")
+ end
+end
+function term.getCursorBlink(enabled)
+ return true
end
function term.read(history, dobreak, hint, pwchar)
@@ -436,7 +450,7 @@ function term.read(history, dobreak, hint, pwchar)
--x = x
io.write("\x1b[K" .. after .. "\x1b[" .. unicode.len(after) .. "D")
end
- elseif mode == "0" then
+ elseif mode == "O" then
local act = io.read(1)
if act == "H" then
io.write("\x1b["..(x - 1).."D")
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/go.lua b/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/go.lua
new file mode 100644
index 0000000000..d8513bbde3
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/go.lua
@@ -0,0 +1,47 @@
+--[[ go, makes the robot go a specified number of blocks in a certain direction or turn around.
+Author: Vexatos
+]]
+local robot=require("robot")
+local shell=require("shell")
+local args = shell.parse(...)
+if #args<1 then
+ print("'go' - Makes the robot go in a certain direction")
+ print("Usage:")
+ print("'go forward [number]' to make the robot go forward a number of blocks (defaults to 1)")
+ print("'go back [number]' to make the robot go backwards")
+ print("'go up [number]' to make the robot go upwards")
+ print("'go down [number]' to make the robot go downwards")
+ print("'go left [number]' to make the robot turn left a number of times")
+ print("'go right [number]' to make the robot turn right a number of times")
+ return
+end
+local distance = args[2] or 1
+
+if not tonumber(distance) or tonumber(distance) <= 0 then
+ io.stderr:write(distance..": not a positive number!\n")
+ return
+end
+
+distance = math.floor(tonumber(distance))
+local action
+
+if args[1] == "forward" then
+ action = robot.forward
+elseif args[1] == "back" then
+ action = robot.back
+elseif args[1] == "left" then
+ action = robot.turnLeft
+elseif args[1] == "right" then
+ action = robot.turnRight
+elseif args[1] == "up" then
+ action = robot.up
+elseif args[1] == "down" then
+ action = robot.down
+else
+ io.stderr:write(args[1]..": not a valid direction!\n")
+ return
+end
+
+for i = 1,distance do
+ action()
+end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/mpt.lua b/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/mpt.lua
index 16fc90a1fb..68527a1999 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/mpt.lua
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/mpt.lua
@@ -82,13 +82,13 @@ ocBackend = {
--print(" -U, --upgrade")
--print(" Upgrade or add package(s) to the system and install the required")
--print(" dependencies from sync repositories. Either a URL or file path can be")
- --print(" specified. This is a ?remove-then-add? process.")
+ --print(" specified. This is a “remove-then-add” process.")
print(" -y, --update")
print(" Update package lists for backends that require such action")
print(" -u, --upgrades")
print(" Upgrade all packages that are out-of-date on the")
print(" local system. Only package versions are used to find outdated packages;")
- print(" replacements are not checked here. This is a ?remove-then-add? process.")
+ print(" replacements are not checked here. This is a “remove-then-add” process.")
print(" -f, --force")
print(" Force operation, in case of upgrade it redownloads all packages")
print(" --root='/some/dir'")
@@ -289,7 +289,13 @@ mptFrontend = {
end
local updateResp = backend.getText(config.frontend.mpt.api.."update", toCheck)
if updateResp then
- local updateList = load("return "..updateResp)() or {}
+ local upd, err = load("return "..updateResp)
+ if not upd then
+ print("Update error: " .. tostring(err))
+ print("Data: " .. tostring(updateResp))
+ os.exit(1)
+ end
+ local updateList = upd() or {}
local res = {}
for _, entry in ipairs(updateList) do
res[entry.package] = {checksum = entry.checksum}
@@ -338,7 +344,7 @@ mirrorFrontend = {
local todo = {}
for pack, data in pairs(base.installed) do
if data.frontend == mirrorFrontend.name then
- if mirrorFrontend.base.installed[pack] and
+ if mirrorFrontend.base and mirrorFrontend.base.installed[pack] and
mirrorFrontend.base.installed[pack].data.checksum ~= base.installed[pack].data.checksum .. (core.data.force and "WAT" or "") then
todo[pack] = {}
end
@@ -390,7 +396,7 @@ oppmFrontend = {
if packages then
for name, package in pairs(packages) do
local metadata = {
- files = expandOppmFiles(package.files),
+ files = expandOppmFiles(package.files or {}),
dependencies = keys(package.dependencies or {}),
repo = repoid,
version = package.version
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/nc.lua b/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/nc.lua
deleted file mode 100644
index 0cdf67a2c0..0000000000
--- a/src/main/resources/assets/opencomputers/loot/plan9k/usr/bin/nc.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-local network = require "network"
-local event = require "event"
-
-local args = {...}
-
-local listen = false
-local port = -1
-local addr
-
-for _,par in ipairs(args) do
- if par == "-l" then
- listen = true
- elseif port < 1 then
- local p = tonumber(par)
- if p then
- port = p
- end
- else
- addr = par
- end
-end
-
-if port < 0 then error("Unspecified port")end
-if not listen and not addr then error("Unspecified address")end
-
-local chanel
-local function handleTcp()
- while true do
- while io.input().remaining() ~= 0 do
- local data = io.read(math.min(io.input().remaining(), 7000))
- if not data then
-
- end
- network.tcp.send(chanel, data)
- end
- local e = {event.pull()}
- if e[1] then
- if e[1] == "tcp" then
- if e[2] == "connection" then
- if listen and e[5] == port and e[6] == "incoming" then
- network.tcp.unlisten(port)
- print("connected")
- elseif not listen and e[3] == chanel and e[6] ~= "incoming" then
- chanel = e[3]
- print("connected")
- end
- elseif e[2] == "close" and e[3] == chanel then
- print("Connection closed")
- return
- elseif e[2] == "message" and e[3] == chanel then
- io.write(e[4])
- end
- end
- end
- end
-end
-
-if listen then
- network.tcp.listen(port)
- handleTcp()
-else
- chanel = network.tcp.open(addr, port)
- handleTcp()
-end
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/usr/lib/robot.lua b/src/main/resources/assets/opencomputers/loot/plan9k/usr/lib/robot.lua
new file mode 100644
index 0000000000..a1c86751c4
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/usr/lib/robot.lua
@@ -0,0 +1,281 @@
+local component = require("component")
+local sides = require("sides")
+
+local robot = {}
+
+-------------------------------------------------------------------------------
+-- General
+
+function robot.name()
+ return component.robot.name()
+end
+
+function robot.level()
+ if component.isAvailable("experience") then
+ return component.experience.level()
+ else
+ return 0
+ end
+end
+
+function robot.getLightColor()
+ return component.robot.getLightColor()
+end
+
+function robot.setLightColor(value)
+ return component.robot.setLightColor(value)
+end
+
+-------------------------------------------------------------------------------
+-- World
+
+function robot.detect()
+ return component.robot.detect(sides.front)
+end
+
+function robot.detectUp()
+ return component.robot.detect(sides.up)
+end
+
+function robot.detectDown()
+ return component.robot.detect(sides.down)
+end
+
+-------------------------------------------------------------------------------
+-- Inventory
+
+function robot.inventorySize()
+ return component.robot.inventorySize()
+end
+
+
+function robot.select(...)
+ return component.robot.select(...)
+end
+
+function robot.count(...)
+ return component.robot.count(...)
+end
+
+function robot.space(...)
+ return component.robot.space(...)
+end
+
+function robot.compareTo(...)
+ return component.robot.compareTo(...)
+end
+
+function robot.transferTo(...)
+ return component.robot.transferTo(...)
+end
+
+-------------------------------------------------------------------------------
+-- Inventory + World
+
+function robot.compare()
+ return component.robot.compare(sides.front)
+end
+
+function robot.compareUp()
+ return component.robot.compare(sides.up)
+end
+
+function robot.compareDown()
+ return component.robot.compare(sides.down)
+end
+
+function robot.drop(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drop(sides.front, count)
+end
+
+function robot.dropUp(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drop(sides.up, count)
+end
+
+function robot.dropDown(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drop(sides.down, count)
+end
+
+function robot.place(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.place(sides.front, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.placeUp(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.place(sides.up, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.placeDown(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.place(sides.down, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.suck(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.suck(sides.front, count)
+end
+
+function robot.suckUp(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.suck(sides.up, count)
+end
+
+function robot.suckDown(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.suck(sides.down, count)
+end
+
+-------------------------------------------------------------------------------
+-- Tool
+
+function robot.durability()
+ return component.robot.durability()
+end
+
+
+function robot.swing(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.swing(sides.front, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.swingUp(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.swing(sides.up, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.swingDown(side, sneaky)
+ checkArg(1, side, "nil", "number")
+ return component.robot.swing(sides.down, side, sneaky ~= nil and sneaky ~= false)
+end
+
+function robot.use(side, sneaky, duration)
+ checkArg(1, side, "nil", "number")
+ checkArg(3, duration, "nil", "number")
+ return component.robot.use(sides.front, side, sneaky ~= nil and sneaky ~= false, duration)
+end
+
+function robot.useUp(side, sneaky, duration)
+ checkArg(1, side, "nil", "number")
+ checkArg(3, duration, "nil", "number")
+ return component.robot.use(sides.up, side, sneaky ~= nil and sneaky ~= false, duration)
+end
+
+function robot.useDown(side, sneaky, duration)
+ checkArg(1, side, "nil", "number")
+ checkArg(3, duration, "nil", "number")
+ return component.robot.use(sides.down, side, sneaky ~= nil and sneaky ~= false, duration)
+end
+
+-------------------------------------------------------------------------------
+-- Movement
+
+function robot.forward()
+ return component.robot.move(sides.front)
+end
+
+function robot.back()
+ return component.robot.move(sides.back)
+end
+
+function robot.up()
+ return component.robot.move(sides.up)
+end
+
+function robot.down()
+ return component.robot.move(sides.down)
+end
+
+
+function robot.turnLeft()
+ return component.robot.turn(false)
+end
+
+function robot.turnRight()
+ return component.robot.turn(true)
+end
+
+function robot.turnAround()
+ local turn = math.random() < 0.5 and robot.turnLeft or robot.turnRight
+ return turn() and turn()
+end
+
+-------------------------------------------------------------------------------
+-- Tank
+
+function robot.tankCount()
+ return component.robot.tankCount()
+end
+
+
+function robot.selectTank(tank)
+ return component.robot.selectTank(tank)
+end
+
+function robot.tankLevel(...)
+ return component.robot.tankLevel(...)
+end
+
+function robot.tankSpace(...)
+ return component.robot.tankSpace(...)
+end
+
+function robot.compareFluidTo(...)
+ return component.robot.compareFluidTo(...)
+end
+
+function robot.transferFluidTo(...)
+ return component.robot.transferFluidTo(...)
+end
+
+-------------------------------------------------------------------------------
+-- Tank + World
+
+function robot.compareFluid()
+ return component.robot.compareFluid(sides.front)
+end
+
+function robot.compareFluidUp()
+ return component.robot.compareFluid(sides.up)
+end
+
+function robot.compareFluidDown()
+ return component.robot.compareFluid(sides.down)
+end
+
+function robot.drain(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drain(sides.front, count)
+end
+
+function robot.drainUp(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drain(sides.up, count)
+end
+
+function robot.drainDown(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.drain(sides.down, count)
+end
+
+function robot.fill(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.fill(sides.front, count)
+end
+
+function robot.fillUp(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.fill(sides.up, count)
+end
+
+function robot.fillDown(count)
+ checkArg(1, count, "nil", "number")
+ return component.robot.fill(sides.down, count)
+end
+
+-------------------------------------------------------------------------------
+
+return robot
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/config.db
index 1297bda91e..d1636c61e5 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/config.db
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/config.db
@@ -1 +1 @@
-{database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}},cacheDir="/var/lib/mpt/cache/"}
\ No newline at end of file
+{frontend={mpt={api="http://mpt.magik6k.net/api/"}},cacheDir="/var/lib/mpt/cache/",database="/var/lib/mpt/base.db"}
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/mpt.db
index 937fb20b16..aa5361a733 100644
--- a/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/mpt.db
+++ b/src/main/resources/assets/opencomputers/loot/plan9k/var/lib/mpt/mpt.db
@@ -1 +1 @@
-{installed={["plan9k-drivers"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua","/lib/modules/base/17_nfc.lua","/lib/modules/base/17_chatbox.lua","/lib/modules/base/17_data.lua","/lib/modules/base/17_drive.lua"},checksum="-3f839e5e5c38f81bbd03e6a6438ac15f",name="plan9k-drivers"},deps={}},["plan9k-filesystems"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/lib/msdosfs.lua","/bin/mount.msdos.lua","/usr/bin/mkdosfs.lua"},checksum="-50d8cc1c5928bec760f210185cccfafd",name="plan9k-filesystems"},deps={}},["plan9k-installer"]={frontend="MPT",data={repo="plan9k",dependencies={"plan9k","mpt","plan9k-extra"},files={"/bin/install.lua"},checksum="-31228542b8cc1190aa9d8be7ea4ff56c",name="plan9k-installer"},deps={"plan9k","mpt","plan9k-extra"}},["plan9k-network"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},checksum="-3310268a428bf449c821cbc8ac140ca5",name="plan9k-network"},deps={}},["plan9k-coreutil"]={frontend="MPT",data={repo="plan9k",dependencies={"plan9k-corelibs","plan9k-fsutil"},files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua","/bin/label.lua","/bin/uptime.lua","/bin/resolution.lua","/bin/watch.lua","/bin/passwd.lua"},checksum="-2e3257235e131ffaf5ba1a9fd77ba946",name="plan9k-coreutil"},deps={"plan9k-corelibs","plan9k-fsutil"}},mpt={frontend="MPT",data={repo="mpt",dependencies={},files={"/usr/bin/mpt.lua"},checksum="-4bef26a6a1a0b78056600ce9487fe735",name="mpt"},deps={}},["plan9k-core"]={frontend="MPT",data={repo="plan9k",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua"},checksum="6950ff30af0231e69b1ae8d0b5b8ad77",name="plan9k-core"},deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-containers"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/bin/sandbox.lua"},checksum="-5ff36b0bde938782f3821bca52931c06",name="plan9k-containers"},deps={}},["openloader-init"]={frontend="MPT",data={repo="disks",dependencies={},files={"/init.lua"},checksum="-45e6d7b1e41468c1d335952ee3b89e13",name="openloader-init"},deps={}},["plan9k-ssh"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/etc/rc.d/sshd.lua","/bin/sshd.lua","/usr/sbin/sshsession.lua","/usr/bin/ssh.lua"},checksum="-2fc28a9bdd608182c931a126ca8150d",name="plan9k-ssh"},deps={}},["plan9k-shell"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/bin/sh.lua"},checksum="-7a6e50038841f4529feab7ebf0971fcb",name="plan9k-shell"},deps={}},["plan9k-extra"]={frontend="MPT",data={repo="plan9k",dependencies={"plan9k-ssh","plan9k-containers","plan9k-filesystems","plan9k","mpt"},files={"/etc/rc.d/autoupdate.lua"},checksum="42acf5b6b26058654c4ef359a6e19d14",name="plan9k-extra"},deps={"plan9k-ssh","plan9k-containers","plan9k-filesystems","plan9k","mpt"}},["plan9k-fsutil"]={frontend="MPT",data={repo="plan9k",dependencies={"plan9k-corelibs"},files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua","/bin/pwd.lua","/bin/more.lua","/bin/du.lua"},checksum="-3e5ba058562df4c95858b2992e90365d",name="plan9k-fsutil"},deps={"plan9k-corelibs"}},["plan9k-corelibs"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},checksum="-6d48e158051e4ffa3ab82ed97dffa830",name="plan9k-corelibs"},deps={}},plan9k={frontend="MPT",data={repo="plan9k",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"},files={},checksum="-2d8f4b84ea60b0c9d5846f57e9f1691c",name="plan9k"},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"}},pipes={frontend="MPT",data={repo="plan9k",dependencies={"openloader-init"},files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua","/lib/modules/base/01_gc.lua","/lib/modules/base/17_ipc.lua","/lib/modules/base/19_cgroups.lua","/lib/modules/base/02_cmd.lua"},name="pipes",checksum="-1db1c9d0e65ad1a3467e849d8b1d2709"},deps={"openloader-init"}},["plan9k-data"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/usr/bin/base64.lua","/usr/lib/data.lua","/usr/bin/deflate.lua","/usr/bin/inflate.lua","/usr/bin/md5sum.lua","/usr/bin/sha256sum.lua","/usr/bin/gpg.lua"},checksum="480a898a741b2bf424f9e0e86e5072ba",name="plan9k-data"},deps={}},["plan9k-edit"]={frontend="MPT",data={repo="plan9k",dependencies={},files={"/bin/edit.lua"},checksum="34b1046ac9b7a87a1cdd74f8c03f27ea",name="plan9k-edit"},deps={}}},oppm={repos={},packages={}}}
\ No newline at end of file
+{oppm={repos={},packages={}},installed={["plan9k-fsutil"]={frontend="MPT",data={files={"/bin/tee.lua","/bin/mv.lua","/bin/ln.lua","/bin/cp.lua","/bin/df.lua","/bin/mkdir.lua","/bin/dd.lua","/bin/mount.lua","/bin/du.lua","/bin/more.lua","/bin/cat.lua","/bin/rm.lua","/bin/touch.lua","/bin/find.lua","/bin/mount.cow.lua","/bin/pwd.lua","/bin/ls.lua"},checksum="-1222b1c98bb3bede8ea8be775ea95f2d",name="plan9k-fsutil",dependencies={"plan9k-corelibs"},repo="plan9k"},deps={"plan9k-corelibs"}},["plan9k-edit"]={frontend="MPT",data={files={"/bin/edit.lua"},checksum="4a26acd9f650ce6269bac2d25c3a8946",name="plan9k-edit",dependencies={},repo="plan9k"},deps={}},["plan9k-filesystems"]={frontend="MPT",data={files={"/lib/msdosfs.lua","/bin/mount.msdos.lua","/usr/bin/mkdosfs.lua"},checksum="-50d8cc1c5928bec760f210185cccfafd",name="plan9k-filesystems",dependencies={},repo="plan9k"},deps={}},["plan9k-drivers"]={frontend="MPT",data={files={"/lib/modules/base/17_eeprom.lua","/lib/modules/base/17_drive.lua","/lib/modules/base/16_partition.lua","/lib/modules/base/17_nfc.lua","/lib/modules/base/17_tape.lua","/lib/modules/base/17_data.lua","/lib/modules/base/17_gpt.lua","/lib/modules/base/17_chatbox.lua","/lib/modules/base/12_mount.lua"},checksum="-fb640833fcfba3442555ebbbe66762b",name="plan9k-drivers",dependencies={},repo="plan9k"},deps={}},["plan9k-installer"]={frontend="MPT",data={files={"/bin/install.lua"},checksum="-31228542b8cc1190aa9d8be7ea4ff56c",name="plan9k-installer",dependencies={"plan9k","mpt","plan9k-extra"},repo="plan9k"},deps={"plan9k","mpt","plan9k-extra"}},["plan9k-corelibs"]={frontend="MPT",data={files={"/lib/colors.lua","/lib/term.lua","/lib/serialization.lua","/lib/text.lua","/lib/sides.lua","/lib/shell.lua","/lib/event.lua"},checksum="46d6727c48cbe2a715cda4cb4178798a",name="plan9k-corelibs",dependencies={},repo="plan9k"},deps={}},mpt={frontend="MPT",data={files={"/usr/bin/mpt.lua"},checksum="-27e3ccf071b608bd996e1e60c5fe2e9e",name="mpt",dependencies={},repo="mpt"},deps={}},["openloader-init"]={frontend="MPT",data={files={"/init.lua"},checksum="-3680f69628a75a107d486e87e2695092",name="openloader-init",dependencies={},repo="disks"},deps={}},["plan9k-extra"]={frontend="MPT",data={files={"/usr/bin/go.lua","/usr/lib/robot.lua","/etc/rc.d/autoupdate.lua"},checksum="7c8335894a6a92290be2bbc0d11f809f",name="plan9k-extra",dependencies={"plan9k-ssh","plan9k-containers","plan9k-filesystems","plan9k","mpt"},repo="plan9k"},deps={"plan9k-ssh","plan9k-containers","plan9k-filesystems","plan9k","mpt"}},["plan9k-ssh"]={frontend="MPT",data={files={"/bin/sshd.lua","/usr/sbin/sshsession.lua","/usr/bin/ssh.lua","/etc/rc.d/sshd.lua"},checksum="-17c7a134b3e83e5c3c1b4c20adcdb2c6",name="plan9k-ssh",dependencies={},repo="plan9k"},deps={}},["plan9k-network"]={frontend="MPT",data={files={"/bin/wget.lua","/bin/pastebin.lua","/lib/internet.lua"},checksum="-5faf540f0923943a61e72e8c167d6c24",name="plan9k-network",dependencies={},repo="plan9k"},deps={}},["plan9k-data"]={frontend="MPT",data={files={"/usr/lib/data.lua","/usr/bin/inflate.lua","/usr/bin/sha256sum.lua","/usr/bin/deflate.lua","/usr/bin/base64.lua","/usr/bin/gpg.lua","/usr/bin/md5sum.lua"},checksum="480a898a741b2bf424f9e0e86e5072ba",name="plan9k-data",dependencies={},repo="plan9k"},deps={}},pipes={frontend="MPT",data={files={"/lib/modules/base/18_pty.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/17_ipc.lua","/lib/modules/base/05_vfs.lua","/lib/modules/base/02_cmd.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/15_userspace.lua","/lib/modules/base/01_util.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/10_sysfs.lua","/lib/modules/base/25_init.lua","/lib/modules/base/17_keyboard.lua","/usr/man/pipes","/lib/modules/base/16_require.lua","/lib/modules/base/09_rootfs.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/11_block.lua","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/boot/kernel/pipes","/lib/modules/base/19_manageg.lua","/lib/modules/base/01_gc.lua","/lib/modules/base/16_component.lua","/lib/modules/base/19_cgroups.lua"},checksum="-3f5d6398d3e0d79bc51b1e1d3752d44e",name="pipes",dependencies={"openloader-init"},repo="plan9k"},deps={"openloader-init"}},plan9k={frontend="MPT",data={files={"/.prop"},checksum="83a507dd74c6b0cf2bfb3d599c578cd",name="plan9k",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"},repo="plan9k"},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"}},["plan9k-shell"]={frontend="MPT",data={files={"/bin/sh.lua"},checksum="456005c33de866e6cc26fc310027b6e9",name="plan9k-shell",dependencies={},repo="plan9k"},deps={}},["plan9k-containers"]={frontend="MPT",data={files={"/bin/sandbox.lua"},checksum="-5ff36b0bde938782f3821bca52931c06",name="plan9k-containers",dependencies={},repo="plan9k"},deps={}},["plan9k-coreutil"]={frontend="MPT",data={files={"/bin/resolution.lua","/bin/reboot.lua","/bin/lua.lua","/bin/kill.lua","/bin/clear.lua","/bin/shutdown.lua","/bin/components.lua","/bin/dmesg.lua","/bin/passwd.lua","/bin/sleep.lua","/bin/wc.lua","/bin/uptime.lua","/bin/ps.lua","/bin/hostname.lua","/bin/echo.lua","/bin/label.lua","/bin/watch.lua"},checksum="73511370e5cc25ed49c1763f418d3514",name="plan9k-coreutil",dependencies={"plan9k-corelibs","plan9k-fsutil"},repo="plan9k"},deps={"plan9k-corelibs","plan9k-fsutil"}},["plan9k-core"]={frontend="MPT",data={files={"/bin/init.lua","/bin/getty.lua","/lib/rc.lua","/bin/rc.lua","/bin/readkey.lua"},checksum="4923988758bf8aa5f6b232469d6821c3",name="plan9k-core",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},repo="plan9k"},deps={"pipes","plan9k-coreutil","plan9k-shell"}}}}
\ No newline at end of file
diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes
index b51583a590..94966bf5f9 100644
--- a/src/main/resources/assets/opencomputers/recipes/default.recipes
+++ b/src/main/resources/assets/opencomputers/recipes/default.recipes
@@ -295,6 +295,11 @@ inventoryControllerUpgrade {
[dropper, "oc:circuitChip2", craftingPiston]
[ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]]
}
+mfu {
+ input: [["oc:chamelium", gemLapis, "oc:chamelium"]
+ ["oc:linkedCard", "oc:adapter", "oc:linkedCard"]
+ ["oc:chamelium", gemLapis, "oc:chamelium"]]
+}
leashUpgrade {
input: [[ingotIron, lead, ingotIron]
[lead, "oc:materialCU", lead]
diff --git a/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png b/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png
new file mode 100644
index 0000000000..d9ab765480
Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png differ
diff --git a/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png.mcmeta b/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png.mcmeta
new file mode 100644
index 0000000000..f6730e30a9
--- /dev/null
+++ b/src/main/resources/assets/opencomputers/textures/items/UpgradeMF.png.mcmeta
@@ -0,0 +1,5 @@
+{
+ "animation": {
+ "frametime": 4
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/li/cil/oc/Constants.scala b/src/main/scala/li/cil/oc/Constants.scala
index fcb543b6a4..3eee3ce77c 100644
--- a/src/main/scala/li/cil/oc/Constants.scala
+++ b/src/main/scala/li/cil/oc/Constants.scala
@@ -118,6 +118,7 @@ object Constants {
final val LinkedCard = "linkedCard"
final val LootDisk = "lootDisk"
final val LuaBios = "luaBios"
+ final val MFU = "mfu"
final val Manual = "manual"
final val MicrocontrollerCaseCreative = "microcontrollerCaseCreative"
final val MicrocontrollerCaseTier1 = "microcontrollerCase1"
@@ -174,6 +175,7 @@ object Constants {
object DeviceInfo {
final val DefaultVendor = "MightyPirates GmbH & Co. KG"
+ final val Scummtech = "Scummtech, Inc."
}
diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala
index 433e66965d..9d853a65c8 100644
--- a/src/main/scala/li/cil/oc/Localization.scala
+++ b/src/main/scala/li/cil/oc/Localization.scala
@@ -170,6 +170,8 @@ object Localization {
def PrintLightValue(level: Int) = localizeImmediately("tooltip.Print.LightValue", level.toString)
def PrintRedstoneLevel(level: Int) = localizeImmediately("tooltip.Print.RedstoneLevel", level.toString)
+
+ def MFULinked(isLinked: Boolean) = localizeImmediately(if (isLinked) "tooltip.UpgradeMF.Linked" else "tooltip.UpgradeMF.Unlinked")
}
}
diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala
index bd8897fdb4..a7a8efee4a 100644
--- a/src/main/scala/li/cil/oc/Settings.scala
+++ b/src/main/scala/li/cil/oc/Settings.scala
@@ -222,6 +222,7 @@ class Settings(val config: Config) {
val transposerCost = config.getDouble("power.cost.transposer") max 0
val nanomachineCost = config.getDouble("power.cost.nanomachineInput") max 0
val nanomachineReconfigureCost = config.getDouble("power.cost.nanomachinesReconfigure") max 0
+ val mfuCost = config.getDouble("power.cost.mfuRelay") max 0
// power.rate
val accessPointRate = config.getDouble("power.rate.accessPoint") max 0
@@ -357,6 +358,7 @@ class Settings(val config: Config) {
val serverRackSwitchTier = (config.getInt("misc.serverRackSwitchTier") - 1) max Tier.None min Tier.Three
val redstoneDelay = config.getDouble("misc.redstoneDelay") max 0
val tradingRange = config.getDouble("misc.tradingRange") max 0
+ val mfuRange = config.getInt("misc.mfuRange") max 0 min 128
// ----------------------------------------------------------------------- //
// nanomachines
@@ -658,7 +660,7 @@ object Settings {
}
def checkAccess(ctxOpt: Option[DebugCard.AccessContext]): Option[String] = ctxOpt match {
- case Some(ctx) => values.get(ctx.player) match {
+ case Some(ctx) => values.get(ctx.player.toLowerCase) match {
case Some(x) =>
if (x == ctx.nonce) None
else Some("debug card is invalidated, please re-bind it to yourself")
diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala
index 61bb8efc53..6cc0ac44a9 100644
--- a/src/main/scala/li/cil/oc/client/PacketHandler.scala
+++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala
@@ -46,6 +46,7 @@ object PacketHandler extends CommonPacketHandler {
case PacketType.Analyze => onAnalyze(p)
case PacketType.ChargerState => onChargerState(p)
case PacketType.ClientLog => onClientLog(p)
+ case PacketType.Clipboard => onClipboard(p)
case PacketType.ColorChange => onColorChange(p)
case PacketType.ComputerState => onComputerState(p)
case PacketType.ComputerUserList => onComputerUserList(p)
@@ -63,6 +64,7 @@ object PacketHandler extends CommonPacketHandler {
case PacketType.HologramTranslation => onHologramPositionOffsetY(p)
case PacketType.HologramValues => onHologramValues(p)
case PacketType.LootDisk => onLootDisk(p)
+ case PacketType.CyclingDisk => onCyclingDisk(p)
case PacketType.NanomachinesConfiguration => onNanomachinesConfiguration(p)
case PacketType.NanomachinesInputs => onNanomachinesInputs(p)
case PacketType.NanomachinesPower => onNanomachinesPower(p)
@@ -132,6 +134,10 @@ object PacketHandler extends CommonPacketHandler {
OpenComputers.log.info(p.readUTF())
}
+ def onClipboard(p: PacketParser) {
+ GuiScreen.setClipboardString(p.readUTF())
+ }
+
def onColorChange(p: PacketParser) =
p.readTileEntity[Colored]() match {
case Some(t) =>
@@ -312,6 +318,13 @@ object PacketHandler extends CommonPacketHandler {
}
}
+ def onCyclingDisk(p: PacketParser) = {
+ val stack = p.readItemStack()
+ if (stack != null) {
+ Loot.disksForCyclingClient += stack
+ }
+ }
+
def onNanomachinesConfiguration(p: PacketParser) = {
p.readEntity[EntityPlayer]() match {
case Some(player) =>
diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala
index 3b42b9b907..5e6de4b7fc 100644
--- a/src/main/scala/li/cil/oc/client/Proxy.scala
+++ b/src/main/scala/li/cil/oc/client/Proxy.scala
@@ -12,6 +12,7 @@ import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.client
import li.cil.oc.client.renderer.HighlightRenderer
+import li.cil.oc.client.renderer.MFUTargetRenderer
import li.cil.oc.client.renderer.PetRenderer
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer
@@ -87,6 +88,7 @@ private[oc] class Proxy extends CommonProxy {
MinecraftForge.EVENT_BUS.register(RackMountableRenderHandler)
MinecraftForge.EVENT_BUS.register(Sound)
MinecraftForge.EVENT_BUS.register(TextBuffer)
+ MinecraftForge.EVENT_BUS.register(MFUTargetRenderer)
MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer)
NetworkRegistry.INSTANCE.registerGuiHandler(OpenComputers, GuiHandler)
diff --git a/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala
new file mode 100644
index 0000000000..470a7b35ff
--- /dev/null
+++ b/src/main/scala/li/cil/oc/client/renderer/MFUTargetRenderer.scala
@@ -0,0 +1,155 @@
+package li.cil.oc.client.renderer
+
+import cpw.mods.fml.common.eventhandler.SubscribeEvent
+import li.cil.oc.Constants
+import li.cil.oc.Settings
+import li.cil.oc.api
+import li.cil.oc.util.BlockPosition
+import li.cil.oc.util.RenderState
+import net.minecraft.client.Minecraft
+import net.minecraft.item.ItemStack
+import net.minecraftforge.client.event.RenderWorldLastEvent
+import net.minecraftforge.common.util.Constants.NBT
+import org.lwjgl.opengl.GL11
+
+object MFUTargetRenderer {
+ private val color = 0x00FF00
+ private lazy val mfu = api.Items.get(Constants.ItemName.MFU)
+
+ @SubscribeEvent
+ def onRenderWorldLastEvent(e: RenderWorldLastEvent) {
+ val mc = Minecraft.getMinecraft
+ val player = mc.thePlayer
+ if (player == null) return
+ player.getHeldItem match {
+ case stack: ItemStack if api.Items.get(stack) == mfu && stack.hasTagCompound =>
+ val data = stack.getTagCompound
+ if (data.hasKey(Settings.namespace + "coord", NBT.TAG_INT_ARRAY)) {
+ val Array(x, y, z, dimension, side) = data.getIntArray(Settings.namespace + "coord")
+ if (player.getEntityWorld.provider.dimensionId != dimension) return
+ if (player.getDistance(x, y, z) > 64) return
+
+ val bounds = BlockPosition(x, y, z).bounds.expand(0.1, 0.1, 0.1)
+
+ val px = player.lastTickPosX + (player.posX - player.lastTickPosX) * e.partialTicks
+ val py = player.lastTickPosY + (player.posY - player.lastTickPosY) * e.partialTicks
+ val pz = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * e.partialTicks
+
+ RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: entering (aka: wasntme)")
+
+ GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
+ GL11.glPushMatrix()
+ GL11.glTranslated(-px, -py, -pz)
+ RenderState.makeItBlend()
+ GL11.glDisable(GL11.GL_LIGHTING)
+ GL11.glDisable(GL11.GL_TEXTURE_2D)
+ GL11.glDisable(GL11.GL_DEPTH_TEST)
+ GL11.glDisable(GL11.GL_CULL_FACE)
+
+ GL11.glColor4f(
+ ((color >> 16) & 0xFF) / 255f,
+ ((color >> 8) & 0xFF) / 255f,
+ ((color >> 0) & 0xFF) / 255f,
+ 0.25f)
+ GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE)
+ drawBox(bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ)
+ GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL)
+ drawFace(bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ, side)
+
+ GL11.glPopMatrix()
+ GL11.glPopAttrib()
+
+ RenderState.checkError(getClass.getName + ".onRenderWorldLastEvent: leaving")
+ }
+ case _ => // Nothing
+ }
+ }
+
+ private def drawBox(minX: Double, minY: Double, minZ: Double, maxX: Double, maxY: Double, maxZ: Double) {
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glEnd()
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glEnd()
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glEnd()
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glEnd()
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glEnd()
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glEnd()
+ }
+
+ private def drawFace(minX: Double, minY: Double, minZ: Double, maxX: Double, maxY: Double, maxZ: Double, side: Int): Unit = {
+ side match {
+ case 0 => // Down
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glEnd()
+ case 1 => // Up
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glEnd()
+ case 2 => // North
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glEnd()
+ case 3 => // South
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glEnd()
+ case 4 => // East
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(minX, minY, minZ)
+ GL11.glVertex3d(minX, maxY, minZ)
+ GL11.glVertex3d(minX, maxY, maxZ)
+ GL11.glVertex3d(minX, minY, maxZ)
+ GL11.glEnd()
+ case 5 => // West
+ GL11.glBegin(GL11.GL_QUADS)
+ GL11.glVertex3d(maxX, minY, minZ)
+ GL11.glVertex3d(maxX, minY, maxZ)
+ GL11.glVertex3d(maxX, maxY, maxZ)
+ GL11.glVertex3d(maxX, maxY, minZ)
+ GL11.glEnd()
+ case _ => // WTF?
+ }
+ }
+
+}
diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala
index 3d4922f5e2..73e1877932 100644
--- a/src/main/scala/li/cil/oc/common/EventHandler.scala
+++ b/src/main/scala/li/cil/oc/common/EventHandler.scala
@@ -219,6 +219,7 @@ object EventHandler {
PetRenderer.isInitialized = false
PetRenderer.hidden.clear()
Loot.disksForClient.clear()
+ Loot.disksForCyclingClient.clear()
client.Sound.startLoop(null, "computer_running", 0f, 0)
scheduleServer(() => client.Sound.stopLoop(null))
diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala
index 0f2d23cdee..0342d8c585 100644
--- a/src/main/scala/li/cil/oc/common/Loot.scala
+++ b/src/main/scala/li/cil/oc/common/Loot.scala
@@ -46,13 +46,19 @@ object Loot {
val worldDisks = mutable.ArrayBuffer.empty[(ItemStack, Int)]
+ def disksForCycling = if(disksForCyclingClient.nonEmpty) disksForCyclingClient else disksForCyclingServer
+
+ val disksForCyclingServer = mutable.ArrayBuffer.empty[ItemStack]
+
+ val disksForCyclingClient = mutable.ArrayBuffer.empty[ItemStack]
+
val disksForSampling = mutable.ArrayBuffer.empty[ItemStack]
val disksForClient = mutable.ArrayBuffer.empty[ItemStack]
def isLootDisk(stack: ItemStack): Boolean = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy) && stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "lootFactory", NBT.TAG_STRING)
- def registerLootDisk(name: String, color: Int, factory: Callable[FileSystem]): ItemStack = {
+ def registerLootDisk(name: String, color: Int, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = {
val mod = Loader.instance.activeModContainer.getModId
OpenComputers.log.info(s"Registering loot disk '$name' from mod $mod.")
@@ -74,6 +80,10 @@ object Loot {
Loot.factories += modSpecificName -> factory
+ if(doRecipeCycling) {
+ Loot.disksForCyclingServer += stack
+ }
+
stack.copy()
}
@@ -142,7 +152,7 @@ object Loot {
} else new Callable[FileSystem] {
override def call(): FileSystem = api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, "loot/" + path)
}
- val stack = registerLootDisk(path, color.getOrElse(8), callable)
+ val stack = registerLootDisk(path, color.getOrElse(8), callable, doRecipeCycling = true)
stack.setStackDisplayName(name)
if (!external) {
Items.registerStack(stack, path)
diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala
index aa0d4c1332..b9feef5864 100644
--- a/src/main/scala/li/cil/oc/common/PacketType.scala
+++ b/src/main/scala/li/cil/oc/common/PacketType.scala
@@ -25,6 +25,7 @@ object PacketType extends Enumeration {
HologramTranslation,
HologramValues,
LootDisk,
+ CyclingDisk,
NanomachinesConfiguration,
NanomachinesInputs,
NanomachinesPower,
diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala
index a1873a0208..5641a56d8f 100644
--- a/src/main/scala/li/cil/oc/common/entity/Drone.scala
+++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala
@@ -464,7 +464,8 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
super.hitByEntity(entity)
}
- override def interactFirst(player: EntityPlayer) = {
+ override def interactFirst(player: EntityPlayer): Boolean = {
+ if (isDead) return false
if (player.isSneaking) {
if (Wrench.isWrench(player.getHeldItem)) {
if(!world.isRemote) {
@@ -536,6 +537,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
}
override def kill(): Unit = {
+ if (isDead) return
super.kill()
if (!world.isRemote) {
val stack = api.Items.get(Constants.ItemName.Drone).createItemStack(1)
@@ -583,6 +585,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
}
override def writeEntityToNBT(nbt: NBTTagCompound) {
+ if (worldObj.isRemote) return
components.saveComponents()
info.storedEnergy = control.node.localBuffer.toInt
nbt.setNewCompoundTag("info", info.save)
diff --git a/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala b/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala
new file mode 100644
index 0000000000..3830de1e77
--- /dev/null
+++ b/src/main/scala/li/cil/oc/common/event/BlockChangeHandler.scala
@@ -0,0 +1,71 @@
+package li.cil.oc.common.event
+
+import cpw.mods.fml.common.eventhandler.SubscribeEvent
+import li.cil.oc.common.EventHandler
+import li.cil.oc.util.BlockPosition
+import net.minecraft.entity.Entity
+import net.minecraft.entity.player.EntityPlayer
+import net.minecraft.world.{IWorldAccess, World}
+import net.minecraftforge.event.world.WorldEvent
+
+import scala.collection.mutable
+
+/**
+ * @author Vexatos
+ */
+object BlockChangeHandler {
+
+ def addListener(listener: ChangeListener, coord: BlockPosition) = {
+ EventHandler.scheduleServer(() => changeListeners.put(listener, coord))
+ }
+
+ def removeListener(listener: ChangeListener) = {
+ EventHandler.scheduleServer(() => changeListeners.remove(listener))
+ }
+
+ private val changeListeners = mutable.WeakHashMap.empty[ChangeListener, BlockPosition]
+
+ @SubscribeEvent
+ def onWorldLoad(e: WorldEvent.Load) {
+ e.world.addWorldAccess(new Listener(e.world))
+ }
+
+ trait ChangeListener {
+ def onBlockChanged()
+ }
+
+ private class Listener(world: World) extends IWorldAccess {
+
+ override def markBlockForUpdate(x: Int, y: Int, z: Int): Unit = {
+ val current = BlockPosition(x, y, z, world)
+ for ((listener, coord) <- changeListeners) if (coord.equals(current)) {
+ listener.onBlockChanged()
+ }
+ }
+
+ override def playRecord(recordName: String, x: Int, y: Int, z: Int) {}
+
+ override def playAuxSFX(player: EntityPlayer, sfxType: Int, x: Int, y: Int, z: Int, data: Int) {}
+
+ override def onEntityDestroy(entity: Entity) {}
+
+ override def destroyBlockPartially(breakerId: Int, x: Int, y: Int, z: Int, progress: Int) {}
+
+ override def markBlockForRenderUpdate(x: Int, y: Int, z: Int) {}
+
+ override def spawnParticle(particleType: String, x: Double, y: Double, z: Double, velX: Double, velY: Double, velZ: Double) {}
+
+ override def playSound(soundName: String, x: Double, y: Double, z: Double, volume: Float, pitch: Float) {}
+
+ override def broadcastSound(soundID: Int, x: Int, y: Int, z: Int, data: Int) {}
+
+ override def playSoundToNearExcept(player: EntityPlayer, soundName: String, x: Double, y: Double, z: Double, volume: Float, pitch: Float) {}
+
+ override def markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) {}
+
+ override def onEntityCreate(entity: Entity) {}
+
+ override def onStaticEntitiesChanged() {}
+ }
+
+}
diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala
index 52c038f1b4..6864a77da8 100644
--- a/src/main/scala/li/cil/oc/common/init/Items.scala
+++ b/src/main/scala/li/cil/oc/common/init/Items.scala
@@ -141,8 +141,12 @@ object Items extends ItemAPI {
val registeredItems = mutable.ArrayBuffer.empty[ItemStack]
- override def registerFloppy(name: String, color: Int, factory: Callable[FileSystem]): ItemStack = {
- val stack = Loot.registerLootDisk(name, color, factory)
+ @Deprecated
+ override def registerFloppy(name: String, color: Int, factory: Callable[FileSystem]): ItemStack =
+ registerFloppy(name, color, factory, doRecipeCycling = false)
+
+ override def registerFloppy(name: String, color: Int, factory: Callable[FileSystem], doRecipeCycling: Boolean): ItemStack = {
+ val stack = Loot.registerLootDisk(name, color, factory, doRecipeCycling)
registeredItems += stack
@@ -534,6 +538,7 @@ object Items extends ItemAPI {
Recipes.addSubItem(new item.DiskDriveMountable(multi), Constants.ItemName.DiskDriveMountable, "oc:diskDriveMountable")
Recipes.addSubItem(new item.UpgradeTrading(multi), Constants.ItemName.TradingUpgrade, "oc:tradingUpgrade")
registerItem(new item.DiamondChip(multi), Constants.ItemName.DiamondChip)
+ Recipes.addSubItem(new item.UpgradeMF(multi), Constants.ItemName.MFU, "oc:mfu")
// Register aliases.
for ((k, v) <- aliases) {
diff --git a/src/main/scala/li/cil/oc/common/item/DebugCard.scala b/src/main/scala/li/cil/oc/common/item/DebugCard.scala
index 7aaf1c6697..7c5a568535 100644
--- a/src/main/scala/li/cil/oc/common/item/DebugCard.scala
+++ b/src/main/scala/li/cil/oc/common/item/DebugCard.scala
@@ -19,7 +19,7 @@ class DebugCard(val parent: Delegator) extends traits.Delegate {
}
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
- if (player.isSneaking) {
+ if (!world.isRemote && player.isSneaking) {
val data = new DebugCardData(stack)
val name = player.getCommandSenderName
diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala
index 3f24d39a74..250a6315f1 100644
--- a/src/main/scala/li/cil/oc/common/item/Tablet.scala
+++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala
@@ -343,7 +343,7 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp
case _ => false
})
- override def isUseableByPlayer(player: EntityPlayer) = machine.canInteract(player.getCommandSenderName)
+ override def isUseableByPlayer(player: EntityPlayer) = machine != null && machine.canInteract(player.getCommandSenderName)
override def markDirty(): Unit = {
data.save(stack)
diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala
new file mode 100644
index 0000000000..4f4b126877
--- /dev/null
+++ b/src/main/scala/li/cil/oc/common/item/UpgradeMF.scala
@@ -0,0 +1,31 @@
+package li.cil.oc.common.item
+
+import java.util
+
+import li.cil.oc.util.BlockPosition
+import li.cil.oc.{Localization, Settings}
+import net.minecraft.entity.player.EntityPlayer
+import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NBTTagCompound
+
+class UpgradeMF(val parent: Delegator) extends traits.Delegate with traits.ItemTier {
+
+ override def onItemUseFirst(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = {
+ if (!player.worldObj.isRemote && player.isSneaking) {
+ if (!stack.hasTagCompound) {
+ stack.setTagCompound(new NBTTagCompound())
+ }
+ val data = stack.getTagCompound
+ data.setIntArray(Settings.namespace + "coord", Array(position.x, position.y, position.z, player.worldObj.provider.dimensionId, side))
+ return true
+ }
+ super.onItemUseFirst(stack, player, position, side, hitX, hitY, hitZ)
+ }
+
+ override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) {
+ tooltip.add(Localization.Tooltip.MFULinked(stack.getTagCompound match {
+ case data: NBTTagCompound => data.hasKey(Settings.namespace +"coord")
+ case _ => false
+ }))
+ }
+}
diff --git a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala
index 3c783e47c9..fd2ab609b6 100644
--- a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala
+++ b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala
@@ -15,7 +15,7 @@ class LootDiskCyclingRecipe extends IRecipe {
}
override def getCraftingResult(crafting: InventoryCrafting): ItemStack = {
- val lootDiskStacks = Loot.worldDisks.map(_._1)
+ val lootDiskStacks = Loot.disksForCycling
collectStacks(crafting).find(Loot.isLootDisk) match {
case Some(lootDisk) if lootDiskStacks.nonEmpty =>
val lootFactoryName = getLootFactoryName(lootDisk)
diff --git a/src/main/scala/li/cil/oc/integration/buildcraft/recipes/LootDiskProgrammableRecipe.scala b/src/main/scala/li/cil/oc/integration/buildcraft/recipes/LootDiskProgrammableRecipe.scala
index 137802cc39..db7a230818 100644
--- a/src/main/scala/li/cil/oc/integration/buildcraft/recipes/LootDiskProgrammableRecipe.scala
+++ b/src/main/scala/li/cil/oc/integration/buildcraft/recipes/LootDiskProgrammableRecipe.scala
@@ -24,7 +24,7 @@ object LootDiskProgrammableRecipe extends IProgrammingRecipe {
val options = mutable.ArrayBuffer.empty[ItemStack]
options.sizeHint(width * height)
- for ((stack, _) <- Loot.worldDisks) {
+ for (stack <- Loot.disksForCycling) {
options += stack.copy()
}
diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala
new file mode 100644
index 0000000000..4cfe6cde36
--- /dev/null
+++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeMF.scala
@@ -0,0 +1,51 @@
+package li.cil.oc.integration.opencomputers
+
+import li.cil.oc.api.driver.EnvironmentProvider
+import li.cil.oc.api.driver.item.HostAware
+import li.cil.oc.api.network.{EnvironmentHost, ManagedEnvironment}
+import li.cil.oc.common.{Slot, Tier}
+import li.cil.oc.server.component
+import li.cil.oc.util.BlockPosition
+import li.cil.oc.{Constants, Settings, api}
+import net.minecraft.item.ItemStack
+import net.minecraftforge.common.DimensionManager
+import net.minecraftforge.common.util.ForgeDirection
+
+/**
+ * @author Vexatos
+ */
+object DriverUpgradeMF extends Item with HostAware {
+ override def worksWith(stack: ItemStack): Boolean = isOneOf(stack,
+ api.Items.get(Constants.ItemName.MFU))
+
+ override def worksWith(stack: ItemStack, host: Class[_ <: EnvironmentHost]): Boolean =
+ worksWith(stack) && isAdapter(host)
+
+ override def slot(stack: ItemStack): String = Slot.Upgrade
+
+ override def tier(stack: ItemStack) = Tier.Three
+
+ override def createEnvironment(stack: ItemStack, host: EnvironmentHost): ManagedEnvironment = {
+ if (host.world != null && !host.world.isRemote) {
+ if (stack.hasTagCompound) {
+ stack.getTagCompound.getIntArray(Settings.namespace + "coord") match {
+ case Array(x, y, z, dim, side) =>
+ Option(DimensionManager.getWorld(dim)) match {
+ case Some(world) => return new component.UpgradeMF(host, BlockPosition(x, y, z, world), ForgeDirection.getOrientation(side))
+ case _ => // Invalid dimension ID
+ }
+ case _ => // Invalid tag
+ }
+ }
+ }
+ null
+ }
+
+ object Provider extends EnvironmentProvider {
+ override def getEnvironment(stack: ItemStack): Class[_] =
+ if (worksWith(stack))
+ classOf[component.UpgradeMF]
+ else null
+ }
+
+}
diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala
index 164d977bce..4d0fbf052c 100644
--- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala
+++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala
@@ -98,6 +98,7 @@ object ModOpenComputers extends ModProxy {
MinecraftForge.EVENT_BUS.register(Analyzer)
MinecraftForge.EVENT_BUS.register(AngelUpgradeHandler)
+ MinecraftForge.EVENT_BUS.register(BlockChangeHandler)
MinecraftForge.EVENT_BUS.register(ChunkloaderUpgradeHandler)
MinecraftForge.EVENT_BUS.register(EventHandler)
MinecraftForge.EVENT_BUS.register(ExperienceUpgradeHandler)
@@ -166,6 +167,7 @@ object ModOpenComputers extends ModProxy {
api.Driver.add(DriverUpgradeTankController)
api.Driver.add(DriverUpgradeTractorBeam)
api.Driver.add(DriverUpgradeTrading)
+ api.Driver.add(DriverUpgradeMF)
api.Driver.add(DriverAPU.Provider)
api.Driver.add(DriverDataCard.Provider)
@@ -193,6 +195,7 @@ object ModOpenComputers extends ModProxy {
api.Driver.add(DriverUpgradeSign.Provider)
api.Driver.add(DriverUpgradeTankController.Provider)
api.Driver.add(DriverUpgradeTractorBeam.Provider)
+ api.Driver.add(DriverUpgradeMF.Provider)
api.Driver.add(EnvironmentProviderBlocks)
diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala
index 1aa01ca3bc..9ccc93ac9f 100644
--- a/src/main/scala/li/cil/oc/server/PacketSender.scala
+++ b/src/main/scala/li/cil/oc/server/PacketSender.scala
@@ -67,6 +67,14 @@ object PacketSender {
pb.sendToPlayer(player)
}
+ def sendClipboard(player: EntityPlayerMP, text: String) {
+ val pb = new SimplePacketBuilder(PacketType.Clipboard)
+
+ pb.writeUTF(text)
+
+ pb.sendToPlayer(player)
+ }
+
def sendColorChange(t: Colored) {
val pb = new SimplePacketBuilder(PacketType.ColorChange)
@@ -302,6 +310,13 @@ object PacketSender {
pb.writeItemStack(stack)
+ pb.sendToPlayer(p)
+ }
+ for (stack <- Loot.disksForCyclingServer) {
+ val pb = new SimplePacketBuilder(PacketType.CyclingDisk)
+
+ pb.writeItemStack(stack)
+
pb.sendToPlayer(p)
}
}
diff --git a/src/main/scala/li/cil/oc/server/command/CommandHandler.scala b/src/main/scala/li/cil/oc/server/command/CommandHandler.scala
index 509e7d3a8f..d728eaeaa1 100644
--- a/src/main/scala/li/cil/oc/server/command/CommandHandler.scala
+++ b/src/main/scala/li/cil/oc/server/command/CommandHandler.scala
@@ -11,5 +11,6 @@ object CommandHandler {
e.registerServerCommand(WirelessRenderingCommand)
e.registerServerCommand(SpawnComputerCommand)
e.registerServerCommand(DebugWhitelistCommand)
+ e.registerServerCommand(SendDebugMessageCommand)
}
}
diff --git a/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala b/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala
new file mode 100644
index 0000000000..1340985b23
--- /dev/null
+++ b/src/main/scala/li/cil/oc/server/command/SendDebugMessageCommand.scala
@@ -0,0 +1,26 @@
+package li.cil.oc.server.command
+
+import li.cil.oc.api.Network
+import li.cil.oc.common.command.SimpleCommand
+import li.cil.oc.server.network.DebugNetwork
+import net.minecraft.command.ICommandSender
+import net.minecraft.command.WrongUsageException
+
+object SendDebugMessageCommand extends SimpleCommand("oc_sendDebugMessage") {
+ aliases += "oc_sdbg"
+
+ override def getCommandUsage(sender: ICommandSender): String = name + " [message...]"
+
+ override def processCommand(sender: ICommandSender, args: Array[String]): Unit = {
+ if (args == null || args.length == 0) {
+ throw new WrongUsageException("no destination address specified.")
+ }
+ val destination = args(0)
+ DebugNetwork.getEndpoint(destination).foreach { endpoint =>
+ val packet = Network.newPacket(sender.getCommandSenderName, destination, 0, args.drop(1).toList.toArray[AnyRef])
+ endpoint.receivePacket(packet)
+ }
+ }
+
+ override def getRequiredPermissionLevel = 2
+}
diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala
index 99ade4b534..a1be1d5945 100644
--- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala
+++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala
@@ -12,10 +12,14 @@ import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.network.Environment
import li.cil.oc.api.network.Node
+import li.cil.oc.api.network.Packet
import li.cil.oc.api.network.SidedEnvironment
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.api.prefab.AbstractValue
+import li.cil.oc.server.PacketSender
+import li.cil.oc.server.network.DebugNetwork
+import li.cil.oc.server.network.DebugNetwork.DebugNode
import li.cil.oc.server.component.DebugCard.{AccessContext, CommandSender}
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
@@ -44,7 +48,7 @@ import net.minecraftforge.fluids.IFluidHandler
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
-class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
+class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment with DebugNode {
override val node = Network.newNode(this, Visibility.Neighbors).
withComponent("debug").
withConnector().
@@ -190,21 +194,58 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
// ----------------------------------------------------------------------- //
+ @Callback(doc = """function(player:string, text:string) -- Sends text to the specified player's clipboard if possible.""")
+ def sendToClipboard(context: Context, args: Arguments): Array[AnyRef] = {
+ checkAccess()
+ Option(MinecraftServer.getServer.getConfigurationManager.func_152612_a(args.checkString(0))) match {
+ case Some(player) =>
+ PacketSender.sendClipboard(player, args.checkString(1))
+ result(true)
+ case _ =>
+ result(false, "no such player")
+ }
+ }
+
+ @Callback(doc = """function(address:string, data...) -- Sends data to the debug card with the specified address.""")
+ def sendToDebugCard(context: Context, args: Arguments): Array[AnyRef] = {
+ checkAccess()
+ val destination = args.checkString(0)
+ DebugNetwork.getEndpoint(destination).filter(_ != this).foreach{endpoint =>
+ // Cast to iterable to use Scala's toArray instead of the Arguments' one (which converts byte arrays to Strings).
+ val packet = Network.newPacket(node.address, destination, 0, args.drop(1).asInstanceOf[java.lang.Iterable[AnyRef]].toArray)
+ endpoint.receivePacket(packet)
+ }
+ result()
+ }
+
+ override def receivePacket(packet: Packet) {
+ val distance = 0
+ node.sendToReachable("computer.signal", Seq("debug_message", packet.source, Int.box(packet.port), Double.box(distance)) ++ packet.data: _*)
+ }
+
+ override def address: String = if(node != null) node.address() else "debug"
+
+ // ----------------------------------------------------------------------- //
+
override def onConnect(node: Node): Unit = {
super.onConnect(node)
- if (node == this.node) remoteNodePosition.foreach {
- case (x, y, z) =>
- remoteNode = findNode(x, y, z)
- remoteNode match {
- case Some(other) => node.connect(other)
- case _ => remoteNodePosition = None
- }
+ if (node == this.node) {
+ DebugNetwork.add(this)
+ remoteNodePosition.foreach {
+ case (x, y, z) =>
+ remoteNode = findNode(x, y, z)
+ remoteNode match {
+ case Some(other) => node.connect(other)
+ case _ => remoteNodePosition = None
+ }
+ }
}
}
override def onDisconnect(node: Node): Unit = {
super.onDisconnect(node)
if (node == this.node) {
+ DebugNetwork.remove(this)
remoteNode.foreach(other => other.disconnect(node))
}
else if (remoteNode.contains(node)) {
diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala
index 0fe212ccfc..90824d085c 100644
--- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala
+++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala
@@ -3,13 +3,13 @@ package li.cil.oc.server.component
import java.util
import li.cil.oc.Constants
-import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
-import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.Localization
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.Network
import li.cil.oc.api.driver.DeviceInfo
+import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
+import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
@@ -72,32 +72,15 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI
)
def capacityInfo = (maxResolution._1 * maxResolution._2).toString
+
def widthInfo = Array("1", "4", "8").apply(maxDepth.ordinal())
+
def clockInfo = ((2000 / setBackgroundCosts(tier)).toInt / 100).toString + "/" + ((2000 / setForegroundCosts(tier)).toInt / 100).toString + "/" + ((2000 / setPaletteColorCosts(tier)).toInt / 100).toString + "/" + ((2000 / setCosts(tier)).toInt / 100).toString + "/" + ((2000 / copyCosts(tier)).toInt / 100).toString + "/" + ((2000 / fillCosts(tier)).toInt / 100).toString
override def getDeviceInfo: util.Map[String, String] = deviceInfo
// ----------------------------------------------------------------------- //
- override val canUpdate = true
-
- override def update() {
- super.update()
- if (node.network != null && screenInstance.isEmpty && screenAddress.isDefined) {
- Option(node.network.node(screenAddress.get)) match {
- case Some(node: Node) if node.host.isInstanceOf[api.internal.TextBuffer] =>
- screenInstance = Some(node.host.asInstanceOf[api.internal.TextBuffer])
- case _ =>
- // This could theoretically happen after loading an old address, but
- // if the screen either disappeared between saving and now or changed
- // type. The first scenario is more likely, and could happen if the
- // chunk the screen is in isn't loaded when the chunk the GPU is in
- // gets loaded.
- screenAddress = None
- }
- }
- }
-
@Callback(doc = """function(address:string[, reset:boolean=true]):boolean -- Binds the GPU to the screen with the specified address and resets screen settings if `reset` is true.""")
def bind(context: Context, args: Arguments): Array[AnyRef] = {
val address = args.checkString(0)
@@ -395,6 +378,17 @@ class GraphicsCard(val tier: Int) extends prefab.ManagedEnvironment with DeviceI
}
}
+ override def onConnect(node: Node): Unit = {
+ super.onConnect(node)
+ if (screenInstance.isEmpty && screenAddress.fold(false)(_ == node.address)) {
+ node.host match {
+ case buffer: api.internal.TextBuffer =>
+ screenInstance = Some(buffer)
+ case _ => // Not the screen node we're looking for.
+ }
+ }
+ }
+
override def onDisconnect(node: Node) {
super.onDisconnect(node)
if (node == this.node || screenAddress.contains(node.address)) {
diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala
new file mode 100644
index 0000000000..36713d89d1
--- /dev/null
+++ b/src/main/scala/li/cil/oc/server/component/UpgradeMF.scala
@@ -0,0 +1,218 @@
+package li.cil.oc.server.component
+
+import java.util
+
+import li.cil.oc.Constants
+import li.cil.oc.api.driver.DeviceInfo
+import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
+import li.cil.oc.api.driver.DeviceInfo.DeviceClass
+import li.cil.oc.api.network._
+import li.cil.oc.api.prefab
+import li.cil.oc.common.event.BlockChangeHandler
+import li.cil.oc.common.event.BlockChangeHandler.ChangeListener
+import li.cil.oc.server.network
+import li.cil.oc.util.BlockPosition
+import li.cil.oc.util.ExtendedWorld._
+import li.cil.oc.Settings
+import li.cil.oc.api
+import net.minecraft.nbt.NBTTagCompound
+import net.minecraft.tileentity.TileEntity
+import net.minecraft.util.Vec3
+import net.minecraftforge.common.util.ForgeDirection
+
+import scala.collection.convert.WrapAsJava._
+
+/**
+ * Mostly stolen from {@link li.cil.oc.common.tileentity.Adapter}
+ *
+ * @author Sangar, Vexatos
+ */
+class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: ForgeDirection) extends prefab.ManagedEnvironment with ChangeListener with DeviceInfo {
+ override val node = api.Network.newNode(this, Visibility.None).
+ withConnector().
+ create()
+
+ private var otherEnv: Option[api.network.Environment] = None
+ private var otherDrv: Option[(ManagedEnvironment, api.driver.SidedBlock)] = None
+ private var blockData: Option[BlockData] = None
+
+ override val canUpdate = true
+
+ private final lazy val deviceInfo = Map(
+ DeviceAttribute.Class -> DeviceClass.Bus,
+ DeviceAttribute.Description -> "Remote Adapter",
+ DeviceAttribute.Vendor -> Constants.DeviceInfo.Scummtech,
+ DeviceAttribute.Product -> "ERR NAME NOT FOUND"
+ )
+
+ override def getDeviceInfo: util.Map[String, String] = deviceInfo
+
+ private def otherNode(tile: TileEntity, f: (Node) => Unit) {
+ network.Network.getNetworkNode(tile, dir) match {
+ case Some(otherNode) => f(otherNode)
+ case _ => // Nothing to do here
+ }
+ }
+
+ private def updateBoundState() {
+ if (node != null && node.network != null && coord.world.exists(_.provider.dimensionId == host.world.provider.dimensionId)
+ && coord.toVec3.distanceTo(Vec3.createVectorHelper(host.xPosition, host.yPosition, host.zPosition)) <= Settings.get.mfuRange) {
+ host.world.getTileEntity(coord) match {
+ case env: TileEntity with api.network.Environment =>
+ otherEnv match {
+ case Some(environment: TileEntity) =>
+ otherNode(environment, node.disconnect)
+ otherEnv = None
+ case _ => // Nothing to do here.
+ }
+ otherEnv = Some(env)
+ // Remove any driver that might be there.
+ otherDrv match {
+ case Some((environment, driver)) =>
+ node.disconnect(environment.node)
+ environment.save(blockData.get.data)
+ Option(environment.node).foreach(_.remove())
+ otherDrv = None
+ case _ => // Nothing to do here.
+ }
+ otherNode(env, node.connect)
+ case _ =>
+ // Remove any environment that might have been there.
+ otherEnv match {
+ case Some(environment: TileEntity) =>
+ otherNode(environment, node.disconnect)
+ otherEnv = None
+ case _ => // Nothing to do here.
+ }
+ val (world, x, y, z) = (coord.world.get, coord.x, coord.y, coord.z)
+ Option(api.Driver.driverFor(world, coord.x, coord.y, coord.z, dir)) match {
+ case Some(newDriver) =>
+ otherDrv match {
+ case Some((oldEnvironment, driver)) =>
+ if (newDriver != driver) {
+ // This is... odd. Maybe moved by some other mod? First, clean up.
+ otherDrv = None
+ blockData = None
+ node.disconnect(oldEnvironment.node)
+
+ // Then rebuild - if we have something.
+ val environment = newDriver.createEnvironment(world, x, y, z, dir)
+ if (environment != null) {
+ otherDrv = Some((environment, newDriver))
+ blockData = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
+ node.connect(environment.node)
+ }
+ } // else: the more things change, the more they stay the same.
+ case _ =>
+ // A challenger appears. Maybe.
+ val environment = newDriver.createEnvironment(world, x, y, z, dir)
+ if (environment != null) {
+ otherDrv = Some((environment, newDriver))
+ blockData match {
+ case Some(data) if data.name == environment.getClass.getName =>
+ environment.load(data.data)
+ case _ =>
+ }
+ blockData = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
+ node.connect(environment.node)
+ }
+ }
+ case _ => otherDrv match {
+ case Some((environment, driver)) =>
+ // We had something there, but it's gone now...
+ node.disconnect(environment.node)
+ environment.save(blockData.get.data)
+ Option(environment.node).foreach(_.remove())
+ otherDrv = None
+ case _ => // Nothing before, nothing now.
+ }
+ }
+ }
+ }
+ }
+
+ private def disconnect() {
+ otherEnv match {
+ case Some(environment: TileEntity) =>
+ otherNode(environment, node.disconnect)
+ otherEnv = None
+ case _ => // Nothing to do here.
+ }
+ otherDrv match {
+ case Some((environment, driver)) =>
+ node.disconnect(environment.node)
+ environment.save(blockData.get.data)
+ Option(environment.node).foreach(_.remove())
+ otherDrv = None
+ case _ => // Nothing to do here.
+ }
+ }
+
+ override def onBlockChanged() = updateBoundState()
+
+ override def update() {
+ super.update()
+ otherDrv match {
+ case Some((env, drv)) if env.canUpdate => env.update()
+ case _ => // No driver
+ }
+ if (host.world.getTotalWorldTime % Settings.get.tickFrequency == 0) {
+ if (!node.tryChangeBuffer(-Settings.get.mfuCost * Settings.get.tickFrequency
+ * coord.toVec3.distanceTo(Vec3.createVectorHelper(host.xPosition, host.yPosition, host.zPosition)))) {
+ disconnect()
+ }
+ }
+ }
+
+ override def onConnect(node: Node) {
+ super.onConnect(node)
+ if (node == this.node) {
+ // Not checking for range yet because host may be a moving adapter, who knows?
+ BlockChangeHandler.addListener(this, coord)
+
+ updateBoundState()
+ }
+ }
+
+ override def onDisconnect(node: Node) {
+ super.onDisconnect(node)
+ otherEnv match {
+ case Some(env: TileEntity) => otherNode(env, (otherNode) => if (node == otherNode) otherEnv = None)
+ case _ => // No environment
+ }
+ otherDrv match {
+ case Some((env, drv)) if node == env.node => otherDrv = None
+ case _ => // No driver
+ }
+ if (node == this.node) {
+ BlockChangeHandler.removeListener(this)
+ }
+ }
+
+ override def load(nbt: NBTTagCompound) {
+ super.load(nbt)
+ Option(nbt.getCompoundTag(Settings.namespace + "adapter.block")) match {
+ case Some(blockNbt: NBTTagCompound) =>
+ if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) {
+ blockData = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data")))
+ }
+ case _ => // Invalid tag
+ }
+ }
+
+ override def save(nbt: NBTTagCompound) {
+ super.save(nbt)
+ val blockNbt = new NBTTagCompound()
+ blockData.foreach({ data =>
+ otherDrv.foreach(_._1.save(data.data))
+ blockNbt.setString("name", data.name)
+ blockNbt.setTag("data", data.data)
+ })
+ nbt.setTag(Settings.namespace + "adapter.block", blockNbt)
+ }
+
+ // ----------------------------------------------------------------------- //
+
+ private class BlockData(val name: String, val data: NBTTagCompound)
+
+}
diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
index 7d563a06d9..a771dc054c 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
@@ -1,5 +1,6 @@
package li.cil.oc.server.component.traits
+import cpw.mods.fml.common.eventhandler.Event.Result
import li.cil.oc.Settings
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
@@ -10,7 +11,9 @@ import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.ResultWrapper.result
import net.minecraft.entity.item.EntityItem
import net.minecraft.item.ItemBlock
+import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.util.ForgeDirection
+import net.minecraftforge.event.entity.item.ItemTossEvent
trait InventoryWorldControl extends InventoryAware with WorldAware with SideRestricted {
@Callback(doc = "function(side:number[, fuzzy:boolean=false]):boolean -- Compare the block on the specified side with the one in the selected slot. Returns true if equal.")
@@ -54,8 +57,15 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
case _ =>
// No inventory to drop into, drop into the world.
val dropped = inventory.decrStackSize(selectedSlot, count)
+ val validator = (item: EntityItem) => {
+ val event = new ItemTossEvent(item, fakePlayer)
+ val canceled = MinecraftForge.EVENT_BUS.post(event)
+ val denied = event.hasResult && event.getResult == Result.DENY
+ !canceled && !denied
+ }
if (dropped != null && dropped.stackSize > 0) {
- InventoryUtils.spawnStackInWorld(position, dropped, Some(facing))
+ if (InventoryUtils.spawnStackInWorld(position, dropped, Some(facing), Some(validator)) == null)
+ fakePlayer.inventory.addItemStackToInventory(dropped)
}
}
diff --git a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala
index 53a26f27d2..5c62595262 100644
--- a/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala
+++ b/src/main/scala/li/cil/oc/server/machine/ArgumentsImpl.scala
@@ -196,7 +196,7 @@ class ArgumentsImpl(val args: Seq[AnyRef]) extends Arguments {
case value => value
}.toArray
- private def isDefined(index: Int) = index >= 0 && index < args.length
+ private def isDefined(index: Int) = index >= 0 && index < args.length && args(index) != null
private def checkIndex(index: Int, name: String) =
if (index < 0) throw new IndexOutOfBoundsException()
diff --git a/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala b/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala
new file mode 100644
index 0000000000..4702e7fcae
--- /dev/null
+++ b/src/main/scala/li/cil/oc/server/network/DebugNetwork.scala
@@ -0,0 +1,25 @@
+package li.cil.oc.server.network
+
+import li.cil.oc.api.network.Packet
+
+import scala.collection.mutable
+
+object DebugNetwork {
+ val cards = mutable.WeakHashMap.empty[DebugNode, Unit]
+
+ def add(card: DebugNode) {
+ cards.put(card, Unit)
+ }
+
+ def remove(card: DebugNode) {
+ cards.remove(card)
+ }
+
+ def getEndpoint(tunnel: String) = cards.keys.find(_.address == tunnel)
+
+ trait DebugNode {
+ def address: String
+
+ def receivePacket(packet: Packet): Unit
+ }
+}
diff --git a/src/main/scala/li/cil/oc/server/network/Network.scala b/src/main/scala/li/cil/oc/server/network/Network.scala
index ed3e1762d6..95a11b9809 100644
--- a/src/main/scala/li/cil/oc/server/network/Network.scala
+++ b/src/main/scala/li/cil/oc/server/network/Network.scala
@@ -165,12 +165,20 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
def nodes: Iterable[ImmutableNode] = data.values.map(_.data)
- def nodes(reference: ImmutableNode): Iterable[ImmutableNode] = {
+ def reachableNodes(reference: ImmutableNode): Iterable[ImmutableNode] = {
val referenceNeighbors = neighbors(reference).toSet
nodes.filter(node => node != reference && (node.reachability == Visibility.Network ||
(node.reachability == Visibility.Neighbors && referenceNeighbors.contains(node))))
}
+ def reachingNodes(reference: ImmutableNode): Iterable[ImmutableNode] = {
+ if (reference.reachability == Visibility.Network) nodes.filter(node => node != reference)
+ else if (reference.reachability == Visibility.Neighbors) {
+ val referenceNeighbors = neighbors(reference).toSet
+ nodes.filter(node => node != reference && referenceNeighbors.contains(node))
+ } else Iterable.empty
+ }
+
def neighbors(node: ImmutableNode): Iterable[ImmutableNode] = {
data.get(node.address) match {
case Some(n) if n.data == node => n.edges.map(_.other(n).data)
@@ -199,13 +207,13 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
def sendToReachable(source: ImmutableNode, name: String, args: AnyRef*) = {
if (source.network != wrapper)
throw new IllegalArgumentException("Source node must be in this network.")
- send(source, nodes(source), name, args: _*)
+ send(source, reachableNodes(source), name, args: _*)
}
def sendToVisible(source: ImmutableNode, name: String, args: AnyRef*) = {
if (source.network != wrapper)
throw new IllegalArgumentException("Source node must be in this network.")
- send(source, nodes(source) collect {
+ send(source, reachableNodes(source) collect {
case component: api.network.Component if component.canBeSeenFrom(source) => component
}, name, args: _*)
}
@@ -241,11 +249,11 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
connects += ((addedNode, Iterable(addedNode)))
case Visibility.Neighbors =>
connects += ((addedNode, Iterable(addedNode) ++ neighbors(addedNode)))
- nodes(addedNode).foreach(node => connects += ((node, Iterable(addedNode))))
+ reachingNodes(addedNode).foreach(node => connects += ((node, Iterable(addedNode))))
case Visibility.Network =>
// Explicitly send to the added node itself first.
connects += ((addedNode, Iterable(addedNode) ++ nodes.filter(_ != addedNode)))
- nodes(addedNode).foreach(node => connects += ((node, Iterable(addedNode))))
+ reachingNodes(addedNode).foreach(node => connects += ((node, Iterable(addedNode))))
}
}
else {
@@ -767,7 +775,7 @@ object Network extends api.detail.NetworkAPI {
def nodes = network.nodes.asJava
- def nodes(reference: ImmutableNode) = network.nodes(reference).asJava
+ def nodes(reference: ImmutableNode) = network.reachableNodes(reference).asJava
def neighbors(node: ImmutableNode) = network.neighbors(node).asJava
diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala
index ef4c5a2b50..7319260277 100644
--- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala
+++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala
@@ -227,14 +227,14 @@ object InventoryUtils {
}
/**
- * Extracts an item stack from an inventory.
- *
- * This will try to remove items of the same type as the specified item stack
- * up to the number of the stack's size for all slots in the specified inventory.
- *
- * This uses the extractFromInventorySlot method, and therefore
- * handles special cases such as sided inventories and stack size limits.
- */
+ * Extracts an item stack from an inventory.
+ *
+ * This will try to remove items of the same type as the specified item stack
+ * up to the number of the stack's size for all slots in the specified inventory.
+ *
+ * This uses the extractFromInventorySlot method, and therefore
+ * handles special cases such as sided inventories and stack size limits.
+ */
def extractFromInventory(stack: ItemStack, inventory: IInventory, side: ForgeDirection, simulate: Boolean = false) = {
val range = inventory match {
case sided: ISidedInventory => sided.getAccessibleSlotsFromSide(side.ordinal).toIterable
@@ -362,7 +362,7 @@ object InventoryUtils {
/**
* Utility method for spawning an item stack in the world.
*/
- def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: Option[ForgeDirection] = None): EntityItem = position.world match {
+ def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: Option[ForgeDirection] = None, validator: Option[EntityItem => Boolean] = None): EntityItem = position.world match {
case Some(world) if stack != null && stack.stackSize > 0 =>
val rng = world.rand
val (ox, oy, oz) = direction.fold((0, 0, 0))(d => (d.offsetX, d.offsetY, d.offsetZ))
@@ -376,8 +376,11 @@ object InventoryUtils {
entity.motionY = 0.0125 * (rng.nextDouble - 0.5) + oy * 0.08 + (ox + oz) * 0.03
entity.motionZ = 0.0125 * (rng.nextDouble - 0.5) + oz * 0.03
entity.delayBeforeCanPickup = 15
- world.spawnEntityInWorld(entity)
- entity
+ if (validator.fold(true)(_(entity))) {
+ world.spawnEntityInWorld(entity)
+ entity
+ }
+ else null
case _ => null
}
}