diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 8fc6664d6..4fd48602a 100644 --- a/src/main/java/org/perlonjava/core/Configuration.java +++ b/src/main/java/org/perlonjava/core/Configuration.java @@ -33,7 +33,7 @@ public final class Configuration { * Automatically populated by Gradle/Maven during build. * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String gitCommitId = "36ce11560"; + public static final String gitCommitId = "bebebd07e"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). @@ -48,7 +48,7 @@ public final class Configuration { * Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at" * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String buildTimestamp = "Apr 29 2026 11:48:26"; + public static final String buildTimestamp = "Apr 29 2026 12:11:44"; // Prevent instantiation private Configuration() { diff --git a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixInterface.java b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixInterface.java index e1c49d2d5..da70fb1ad 100644 --- a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixInterface.java +++ b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixInterface.java @@ -122,6 +122,14 @@ public interface FFMPosixInterface { * @return 0 on success, -1 on error */ int link(String oldPath, String newPath); + + /** + * Create a FIFO (named pipe). + * @param path Path of the FIFO to create + * @param mode Permission mode (modified by umask) + * @return 0 on success, -1 on error (check errno) + */ + int mkfifo(String path, int mode); /** * Set file access and modification times. diff --git a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixLinux.java b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixLinux.java index f31f67e88..08c1a1db2 100644 --- a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixLinux.java +++ b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixLinux.java @@ -45,6 +45,7 @@ public class FFMPosixLinux implements FFMPosixInterface { // Method handles that need errno capture private static MethodHandle killHandle; private static MethodHandle chmodHandle; + private static MethodHandle mkfifoHandle; private static MethodHandle statHandle; private static MethodHandle lstatHandle; @@ -213,6 +214,16 @@ private static synchronized void ensureInitialized() { FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT), captureErrno ); + + // mkfifo is optional — not every libc exposes it (it always does on + // Linux/macOS; this guard just keeps initialization robust). + stdlib.find("mkfifo").ifPresent(addr -> + mkfifoHandle = linker.downcallHandle( + addr, + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT), + captureErrno + ) + ); // stat and lstat - take path pointer and stat buffer pointer statHandle = linker.downcallHandle( @@ -805,6 +816,29 @@ public int link(String oldPath, String newPath) { return -1; } } + + @Override + public int mkfifo(String path, int mode) { + ensureInitialized(); + if (mkfifoHandle == null) { + // libc has no mkfifo on this platform + setErrno(38); // ENOSYS - function not implemented + return -1; + } + try (Arena arena = Arena.ofConfined()) { + MemorySegment pathSegment = arena.allocateFrom(path); + MemorySegment capturedState = arena.allocate(Linker.Option.captureStateLayout()); + int result = (int) mkfifoHandle.invokeExact(capturedState, pathSegment, mode); + if (result == -1) { + int err = capturedState.get(ValueLayout.JAVA_INT, errnoOffset); + setErrno(err); + } + return result; + } catch (Throwable e) { + setErrno(1); // EPERM + return -1; + } + } @Override public int utimes(String path, long atime, long mtime) { diff --git a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixWindows.java b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixWindows.java index 87759bad8..25ede5402 100644 --- a/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixWindows.java +++ b/src/main/java/org/perlonjava/runtime/nativ/ffm/FFMPosixWindows.java @@ -336,6 +336,13 @@ public int link(String oldPath, String newPath) { return -1; } } + + @Override + public int mkfifo(String path, int mode) { + // Windows has no POSIX FIFOs (named pipes use a different API). + setErrno(38); // ENOSYS + return -1; + } @Override public int utimes(String path, long atime, long mtime) { diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/Encode.java b/src/main/java/org/perlonjava/runtime/perlmodule/Encode.java index e65cfada6..3485cdc8e 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/Encode.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/Encode.java @@ -90,6 +90,10 @@ public class Encode extends PerlModuleBase { CHARSET_ALIASES.put("UCS-2LE", StandardCharsets.UTF_16LE); CHARSET_ALIASES.put("ucs-2le", StandardCharsets.UTF_16LE); + // ISO-10646-1 is an Encode alias for UCS-2 (used by File::BOM, etc.) + CHARSET_ALIASES.put("iso-10646-1", StandardCharsets.UTF_16BE); + CHARSET_ALIASES.put("ISO-10646-1", StandardCharsets.UTF_16BE); + // Shift_JIS aliases try { Charset shiftJIS = Charset.forName("Shift_JIS"); diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/POSIX.java b/src/main/java/org/perlonjava/runtime/perlmodule/POSIX.java index 2b0b913a4..f82eb815e 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/POSIX.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/POSIX.java @@ -42,6 +42,7 @@ public static void initialize() { module.registerMethod("_getcwd", "getcwd", null); module.registerMethod("_strerror", "strerror", null); module.registerMethod("_access", "access", null); + module.registerMethod("_mkfifo", "posix_mkfifo", null); module.registerMethod("_dup2", "dup2", null); // Low-level FD operations @@ -951,6 +952,28 @@ public static RuntimeList access(RuntimeArray args, int ctx) { return new RuntimeScalar("0 but true").getList(); } + /** + * POSIX mkfifo() - create a named pipe (FIFO). + * Arguments: path, mode (e.g. 0700) + * Returns true on success, undef on failure (and sets $!). + */ + public static RuntimeList posix_mkfifo(RuntimeArray args, int ctx) { + if (args.size() < 2) { + GlobalVariable.getGlobalVariable("main::!").set("Bad arguments to mkfifo"); + return RuntimeScalarCache.scalarUndef.getList(); + } + String path = args.get(0).toString(); + int mode = args.get(1).getInt(); + var posix = FFMPosix.get(); + int result = posix.mkfifo(path, mode); + if (result == -1) { + GlobalVariable.getGlobalVariable("main::!").set(posix.strerror(posix.errno())); + return RuntimeScalarCache.scalarUndef.getList(); + } + // Real Perl returns "0 but true" — true in boolean, 0 numerically. + return new RuntimeScalar("0 but true").getList(); + } + // ==================== Low-level FD Operations ==================== /** diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/ErrnoVariable.java b/src/main/java/org/perlonjava/runtime/runtimetypes/ErrnoVariable.java index 97e7911af..82e5aef01 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/ErrnoVariable.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/ErrnoVariable.java @@ -340,6 +340,13 @@ public void dynamicSaveState() { errnoStack.push(new int[]{errno}); messageStack.push(message); super.dynamicSaveState(); + // After saving, reset to the default "no error" state so that + // `local $!;` actually clears the variable inside the dynamic scope, + // matching Perl's semantics. + this.errno = 0; + this.message = ""; + this.type = RuntimeScalarType.DUALVAR; + this.value = new DualVar(new RuntimeScalar(0), new RuntimeScalar("")); } @Override diff --git a/src/main/perl/lib/POSIX.pm b/src/main/perl/lib/POSIX.pm index 8e3b3da57..2afc99a62 100644 --- a/src/main/perl/lib/POSIX.pm +++ b/src/main/perl/lib/POSIX.pm @@ -361,6 +361,7 @@ sub unlink { POSIX::_unlink(@_) } sub link { POSIX::_link(@_) } sub rename { POSIX::_rename(@_) } sub mkdir { POSIX::_mkdir(@_) } +sub mkfifo { POSIX::_mkfifo(@_) } sub rmdir { POSIX::_rmdir(@_) } sub getcwd { POSIX::_getcwd() } sub chdir { POSIX::_chdir(@_) }