Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/org/perlonjava/core/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "dfa87a6c4";
public static final String gitCommitId = "32360b133";

/**
* Git commit date of the build (ISO format: YYYY-MM-DD).
Expand All @@ -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 28 2026 16:14:51";
public static final String buildTimestamp = "Apr 28 2026 16:35:16";

// Prevent instantiation
private Configuration() {
Expand Down
58 changes: 43 additions & 15 deletions src/main/java/org/perlonjava/runtime/operators/IOOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -558,28 +558,56 @@ public static RuntimeScalar open(int ctx, RuntimeBase... args) {
RuntimeScalar fileHandle = (RuntimeScalar) args[0];
if (args.length < 2) {
// 1-argument open: open FILEHANDLE
// Uses $_ as the filename (with embedded mode prefix parsed from it)
String fileName = getGlobalVariable("main::_").toString();
// Per Perl semantics, the global scalar variable of the same name as the
// filehandle holds the filename (which may include a leading mode prefix).
// For example: `open MYFH` reads filename from $main::MYFH, `open 0` reads
// from $main::0 (which is the script name $0).
String filehandleName = null;
RuntimeGlob existingGlob = null;
if ((fileHandle.type == RuntimeScalarType.GLOB || fileHandle.type == RuntimeScalarType.GLOBREFERENCE) && fileHandle.value instanceof RuntimeGlob glob) {
existingGlob = glob;
filehandleName = glob.globName;
} else {
// Otherwise, derive the name from the scalar value. This covers both
// bareword-as-string ("MYFH") and constants like `open 0` where the
// filehandle is named "0".
String name = fileHandle.toString();
if (name != null && !name.isEmpty()) {
filehandleName = name.contains("::") ? name : ("main::" + name);
}
}

// Resolve the filename from the global scalar of the same name.
String fileName = filehandleName != null
? getGlobalVariable(filehandleName).toString()
: "";
RuntimeIO oneFh = RuntimeIO.open(fileName);
if (oneFh == null) {
return scalarUndef;
}
// Assign the IO handle to the filehandle glob (reuse the existing assignment logic below)
RuntimeGlob targetGlob = null;
if ((fileHandle.type == RuntimeScalarType.GLOB || fileHandle.type == RuntimeScalarType.GLOBREFERENCE) && fileHandle.value instanceof RuntimeGlob glob) {
targetGlob = glob;
} else if ((fileHandle.type == RuntimeScalarType.STRING || fileHandle.type == RuntimeScalarType.BYTE_STRING) && fileHandle.value instanceof String name) {
if (!name.isEmpty() && name.matches("^[A-Za-z_][A-Za-z0-9_]*(::[A-Za-z_][A-Za-z0-9_]*)*$")) {
String fullName = name.contains("::") ? name : ("main::" + name);
targetGlob = GlobalVariable.getGlobalIO(fullName);
RuntimeScalar newGlob = new RuntimeScalar();
newGlob.type = RuntimeScalarType.GLOBREFERENCE;
newGlob.value = targetGlob;
fileHandle.set(newGlob);
}

RuntimeGlob targetGlob = existingGlob;
if (targetGlob == null && filehandleName != null) {
targetGlob = GlobalVariable.getGlobalIO(filehandleName);
}
if (targetGlob != null) {
targetGlob.setIO(oneFh);
// If args[0] is a writable scalar (not readonly), update it to point
// at the glob. We must NOT call set() on a readonly scalar (e.g. when
// args[0] is a numeric literal like in `open 0`).
if (!(fileHandle instanceof RuntimeScalarReadOnly)
&& fileHandle.type != RuntimeScalarType.GLOB
&& fileHandle.type != RuntimeScalarType.GLOBREFERENCE) {
try {
RuntimeScalar newGlob = new RuntimeScalar();
newGlob.type = RuntimeScalarType.GLOBREFERENCE;
newGlob.value = targetGlob;
fileHandle.set(newGlob);
} catch (RuntimeException ignored) {
// Read-only / unsettable scalar - the IO has already been
// registered on the global glob, so callers can find it by name.
}
}
} else {
RuntimeScalar newGlob = new RuntimeScalar();
newGlob.type = RuntimeScalarType.GLOBREFERENCE;
Expand Down
62 changes: 62 additions & 0 deletions src/main/perl/lib/POSIX.pm
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,21 @@ our @EXPORT_OK = qw(

# Constants - access (for access() function)
F_OK R_OK W_OK X_OK

# Constants - termios (termios_h)
BRKINT
CS5 CS6 CS7 CS8 CSIZE CSTOPB CREAD PARENB PARODD HUPCL CLOCAL
ECHO ECHOE ECHOK ECHONL
ICANON IEXTEN ISIG
ICRNL INPCK ISTRIP IXON IXOFF IGNBRK IGNCR IGNPAR INLCR IXANY PARMRK
OPOST
TCSADRAIN TCSAFLUSH TCSANOW
VEOF VEOL VERASE VINTR VKILL VMIN VQUIT VSTART VSTOP VSUSP VTIME

# Constants - sysconf (subset, used by POE etc.)
_SC_ARG_MAX _SC_CHILD_MAX _SC_CLK_TCK _SC_NGROUPS_MAX _SC_OPEN_MAX
_SC_JOB_CONTROL _SC_SAVED_IDS _SC_VERSION _SC_PAGESIZE _SC_PAGE_SIZE
_SC_NPROCESSORS_CONF _SC_NPROCESSORS_ONLN
);

our %EXPORT_TAGS = (
Expand Down Expand Up @@ -575,6 +590,53 @@ for my $const (qw(
*{$const} = eval "sub () { POSIX::_const_$const() }";
}

# sysconf() variable name constants and stub implementation.
# Real POSIX sysconf() returns system-dependent runtime limits. PerlOnJava
# does not implement true sysconf(), but many CPAN modules (POE, Proc::Daemon,
# etc.) call sysconf(_SC_OPEN_MAX) etc. for sensible defaults. Provide the
# common _SC_* names as constants and have sysconf() return reasonable values.
BEGIN {
my %sc = (
_SC_ARG_MAX => 0,
_SC_CHILD_MAX => 1,
_SC_CLK_TCK => 2,
_SC_NGROUPS_MAX => 3,
_SC_OPEN_MAX => 4,
_SC_JOB_CONTROL => 5,
_SC_SAVED_IDS => 6,
_SC_VERSION => 7,
_SC_PAGESIZE => 8,
_SC_PAGE_SIZE => 8, # alias of _SC_PAGESIZE
_SC_NPROCESSORS_CONF => 9,
_SC_NPROCESSORS_ONLN => 10,
);
no strict 'refs';
for my $name (keys %sc) {
my $value = $sc{$name};
*{"POSIX::$name"} = sub () { $value };
}
}

sub sysconf {
my $name = shift;
return undef unless defined $name;
if ($name == 0) { return 4096 * 1024; } # _SC_ARG_MAX
elsif ($name == 1) { return 1024; } # _SC_CHILD_MAX
elsif ($name == 2) { return 100; } # _SC_CLK_TCK
elsif ($name == 3) { return 16; } # _SC_NGROUPS_MAX
elsif ($name == 4) { return 1024; } # _SC_OPEN_MAX
elsif ($name == 5) { return 1; } # _SC_JOB_CONTROL
elsif ($name == 6) { return 1; } # _SC_SAVED_IDS
elsif ($name == 7) { return 200809; } # _SC_VERSION
elsif ($name == 8) { return 4096; } # _SC_PAGESIZE
elsif ($name == 9 || $name == 10) { # _SC_NPROCESSORS_*
my $n = eval { 0 + (`getconf _NPROCESSORS_ONLN 2>/dev/null` || 1) };
$n = 1 if !$n || $n < 1;
return $n;
}
return undef;
}

# Locale category constants - defined directly since XS _const_ may not exist
BEGIN {
my %lc = (
Expand Down
Loading