Skip to content

Commit

Permalink
Implement simple termios functions (#1899)
Browse files Browse the repository at this point in the history
  • Loading branch information
BCSharp authored Feb 2, 2025
1 parent e3272b9 commit 82873fc
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/core/IronPython.Modules/fcntl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public static object ioctl(CodeContext context, int fd, int cmd, [NotNone] IBuff

#region Helper Methods

private static int GetFileDescriptor(CodeContext context, object? obj) {
internal static int GetFileDescriptor(CodeContext context, object? obj) {
if (!PythonOps.TryGetBoundAttr(context, obj, "fileno", out object? filenoMeth)) {
throw PythonOps.TypeError("argument must be an int, or have a fileno() method.");
}
Expand Down
101 changes: 93 additions & 8 deletions src/core/IronPython.Modules/termios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
using Microsoft.Scripting.Runtime;

using IronPython.Runtime;
using IronPython.Runtime.Exceptions;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;

using static IronPython.Modules.PythonIOModule;

Expand Down Expand Up @@ -323,8 +325,81 @@ public static void PerformModuleReload(PythonContext context, PythonDictionary d
public static int B230400 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 230400 : 0x1003;
// higher baud rates are not defined on macOS

#endregion


#region Public Functions

[DllImport("libc", SetLastError = true, EntryPoint = "tcsendbreak")]
private static extern int _tcsendbreak(int fd, int duration);

[LightThrowing]
public static object? tcsendbreak(CodeContext context, int fd, int duration) {
CheckFileDescriptor(fd);
if (_tcsendbreak(fd, duration) == -1) {
return LightExceptions.Throw(GetLastTermiosError(context));
}
return null;
}

[LightThrowing]
public static object? tcsendbreak(CodeContext context, object? fd, int duration)
=> tcsendbreak(context, PythonFcntl.GetFileDescriptor(context, fd), duration);


[DllImport("libc", SetLastError = true, EntryPoint = "tcdrain")]
private static extern int _tcdrain(int fd);

[LightThrowing]
public static object? tcdrain(CodeContext context, int fd) {
CheckFileDescriptor(fd);
if (_tcdrain(fd) == -1) {
return LightExceptions.Throw(GetLastTermiosError(context));
}
return null;
}

[LightThrowing]
public static object? tcdrain(CodeContext context, object? fd)
=> tcdrain(context, PythonFcntl.GetFileDescriptor(context, fd));


[DllImport("libc", SetLastError = true, EntryPoint = "tcflush")]
private static extern int _tcflush(int fd, int queue);

[LightThrowing]
public static object? tcflush(CodeContext context, int fd, int queue) {
CheckFileDescriptor(fd);
if (_tcflush(fd, queue) == -1) {
return LightExceptions.Throw(GetLastTermiosError(context));
}
return null;
}

[LightThrowing]
public static object? tcflush(CodeContext context, object? fd, int queue)
=> tcflush(context, PythonFcntl.GetFileDescriptor(context, fd), queue);


[DllImport("libc", SetLastError = true, EntryPoint = "tcflow")]
private static extern int _tcflow(int fd, int action);

[LightThrowing]
public static object? tcflow(CodeContext context, int fd, int action) {
CheckFileDescriptor(fd);
if (_tcflow(fd, action) == -1) {
return LightExceptions.Throw(GetLastTermiosError(context));
}
return null;
}

[LightThrowing]
public static object? tcflow(CodeContext context, object? fd, int action)
=> tcflow(context, PythonFcntl.GetFileDescriptor(context, fd), action);


public static object tcgetattr(CodeContext context, int fd) {
if (fd < 0) throw PythonOps.ValueError("file descriptor cannot be a negative integer ({0})", fd);
CheckFileDescriptor(fd);
if (fd > 0) throw new NotImplementedException("termios support only for stdin");

if (context.LanguageContext.SystemStandardIn is not TextIOWrapper stdin) {
Expand Down Expand Up @@ -360,6 +435,7 @@ public static object tcgetattr(CodeContext context, object? file) {


public static void tcsetattr(CodeContext context, int fd, int when, object? attributes) {
CheckFileDescriptor(fd);
if (fd != 0) throw new NotImplementedException();

if (context.LanguageContext.SystemStandardIn is not TextIOWrapper stdin) {
Expand Down Expand Up @@ -528,11 +604,20 @@ public RawConsole(CodeContext context) : base(context) {
public override bool isatty(CodeContext context) => true;
}

private static int ToInt(this object? o)
=> o switch {
int i => i,
BigInteger bi => (int)bi,
Extensible<BigInteger> ebi => (int)ebi.Value,
_ => throw PythonOps.TypeErrorForBadInstance("an integer is required (got type {0})", o)
};

private static void CheckFileDescriptor(int fd) {
if (fd < 0) {
throw PythonOps.ValueError("file descriptor cannot be a negative integer ({0})", fd);
}
}


private static Exception GetLastTermiosError(CodeContext context) {
int errno = Marshal.GetLastWin32Error();
return PythonExceptions.CreateThrowable(termioserror(context), errno, PythonNT.strerror(errno));
}


private static PythonType termioserror(CodeContext context)
=> (PythonType)context.LanguageContext.GetModuleState("termioserror");
}

0 comments on commit 82873fc

Please sign in to comment.