X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FRuntime.java;h=c2941f01b00aadb4783ace909d9ec436a7d8b2a1;hp=39a4f14d19c268685289c15f15963fbf5869364f;hb=971b6acee01ff851771ea80d1c0270937a33e0f6;hpb=ef1c27c7eff6c432f3ad7a21671947b87539acb2 diff --git a/src/org/ibex/nestedvm/Runtime.java b/src/org/ibex/nestedvm/Runtime.java index 39a4f14..c2941f0 100644 --- a/src/org/ibex/nestedvm/Runtime.java +++ b/src/org/ibex/nestedvm/Runtime.java @@ -2,20 +2,23 @@ // Based on org.xwt.imp.MIPS by Adam Megacz // Portions Copyright 2003 Adam Megacz +// FEATURE: Add a patch to gcc that enabled -Wall -Werror by default + package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; -import java.util.Arrays; - -// FEATURE: Look over the public API, make sure we're exposing a bare minimum -// (we might make this an interface in the future) public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { + public static final String VERSION = "1.0"; + + /** True to write useful diagnostic information to stderr when things go wrong */ + final static boolean STDERR_DIAG = true; + /** Number of bits to shift to get the page number (1<< 1) { stackSize = max(stackSize,pageSize); - stackSize = (stackSize + pageSize) & ~(pageSize-1); + stackSize = (stackSize + pageSize - 1) & ~(pageSize-1); stackPages = stackSize >>> pageShift; - heapStart = (heapStart + pageSize) & ~(pageSize-1); + heapStart = (heapStart + pageSize - 1) & ~(pageSize-1); if(stackPages + STACK_GUARD_PAGES + (heapStart >>> pageShift) >= totalPages) throw new IllegalArgumentException("total pages too small"); } else { @@ -146,15 +152,18 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { readPages = new int[totalPages][]; writePages = new int[totalPages][]; - if(totalPages == 1) + if(totalPages == 1) { readPages[0] = writePages[0] = new int[pageSize>>2]; - else - for(int i=stackBottom >>> pageShift;i>2]; + } else { + for(int i=(stackBottom >>> pageShift);i>2]; + } + } - addFD(new StdinFD(System.in)); - addFD(new StdoutFD(System.out)); - addFD(new StdoutFD(System.err)); + InputStream stdin = Boolean.valueOf(getSystemProperty("nestedvm.textstdin")).booleanValue() ? new TextInputStream(System.in) : System.in; + addFD(new TerminalFD(stdin)); + addFD(new TerminalFD(System.out)); + addFD(new TerminalFD(System.err)); } /** Copy everything from src to addr initializing uninitialized pages if required. @@ -290,7 +299,6 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { public final void memcpy(int dst, int src, int count) throws FaultException { int pageWords = (1<>>2; int pageWordMask = pageWords - 1; - if((dst&3) == 0 && (src&3)==0) { if((count&~3) != 0) { int c = count>>2; @@ -352,7 +360,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { if(page == null) throw new WriteFaultException(a<<2); int index = a&pageWordMask; int n = min(c,pageWords-index); - Arrays.fill(page,index,index+n,fourBytes); + /* Arrays.fill(page,index,index+n,fourBytes);*/ + for(int i=index;iindex in the _user_info table to word * The user_info table is a chunk of memory in the program's memory defined by the @@ -475,16 +483,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } /** Calls _execute() (subclass's execute()) and catches exceptions */ + // FEATURE: Have these call kill() so we get a pretty message to stdout private void __execute() { try { _execute(); } catch(FaultException e) { - e.printStackTrace(); + if(STDERR_DIAG) e.printStackTrace(); sys_exit(128+11); // SIGSEGV exitException = e; } catch(ExecutionException e) { - e.printStackTrace(); - System.err.println(e); + if(STDERR_DIAG) e.printStackTrace(); sys_exit(128+4); // SIGILL exitException = e; } @@ -501,9 +509,9 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { return state != PAUSED; } - protected static String[] concatArgv(String argv0, String[] rest) { - String[] argv = new String[rest.length+1]; - System.arraycopy(rest,0,argv,1,rest.length); + static String[] concatArgv(String argv0, String[] rest) { + String[] argv = new String[rest.length+1]; + System.arraycopy(rest,0,argv,1,rest.length); argv[0] = argv0; return argv; } @@ -518,9 +526,9 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { start(args,env); for(;;) { if(execute()) break; - System.err.println("WARNING: Pause requested while executing run()"); + if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing run()"); } - if(state == EXECED) System.err.println("WARNING: Process exec()ed while being run under run()"); + if(state == EXECED && STDERR_DIAG) System.err.println("WARNING: Process exec()ed while being run under run()"); return state == EXITED ? exitStatus() : 0; } @@ -536,14 +544,23 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { sp = top = writePages.length*(1< ARG_MAX) throw new IllegalArgumentException("args/environ too big"); + // HACK: heapStart() isn't always available when the constructor + // is run and this sometimes doesn't get initialized + if(heapEnd == 0) { + heapEnd = heapStart(); + if(heapEnd == 0) throw new Error("heapEnd == 0"); + int pageSize = writePages.length == 1 ? 4096 : (1< 7) throw new IllegalArgumentException("args.length > 7"); + CPUState state = new CPUState(); + getCPUState(state); + + int sp = state.r[SP]; + int[] ia = new int[args.length]; + for(int i=0;iaddr in the process setting A0-A3 and S0-S3 to the given arguments and returns the contents of V1 when the the pause syscall is invoked */ - public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) { + //public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) { + public final int call(int addr, int a0, int[] rest) throws CallException { + if(rest.length > 7) throw new IllegalArgumentException("rest.length > 7"); if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state"); int oldState = state; CPUState saved = new CPUState(); @@ -591,13 +640,15 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { cpustate.r[SP] = cpustate.r[SP]&~15; cpustate.r[RA] = 0xdeadbeef; cpustate.r[A0] = a0; - cpustate.r[A1] = a1; - cpustate.r[A2] = a2; - cpustate.r[A3] = a3; - cpustate.r[S0] = s0; - cpustate.r[S1] = s1; - cpustate.r[S2] = s2; - cpustate.r[S3] = s3; + switch(rest.length) { + case 7: cpustate.r[S3] = rest[6]; + case 6: cpustate.r[S2] = rest[5]; + case 5: cpustate.r[S1] = rest[4]; + case 4: cpustate.r[S0] = rest[3]; + case 3: cpustate.r[A3] = rest[2]; + case 2: cpustate.r[A2] = rest[1]; + case 1: cpustate.r[A1] = rest[0]; + } cpustate.pc = addr; state = RUNNING; @@ -607,25 +658,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { getCPUState(cpustate); setCPUState(saved); - if(state != PAUSED) - System.out.println("WARNING: Process exit()ed while servicing a call() request"); - else - state = oldState; + if(state != PAUSED) throw new CallException("Process exit()ed while servicing a call() request"); + state = oldState; return cpustate.r[V1]; } - - /** Determines if the process can access fileName. The default implementation simply logs - the request and allows it */ - protected boolean allowFileAccess(String fileName, boolean write) { - //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName); - return true; - } - + /** Allocated an entry in the FileDescriptor table for fd and returns the number. Returns -1 if the table is full. This can be used by subclasses to use custom file descriptors */ - public int addFD(FD fd) { + public final int addFD(FD fd) { if(state == EXITED || state == EXECED) throw new IllegalStateException("addFD called in inappropriate state"); int i; for(i=0;ifdn and removes it from the file descriptor table */ - public boolean closeFD(int fdn) { + public final boolean closeFD(int fdn) { if(state == EXITED || state == EXECED) throw new IllegalStateException("closeFD called in inappropriate state"); if(fdn < 0 || fdn >= OPEN_MAX) return false; if(fds[fdn] == null) return false; @@ -646,17 +688,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } /** Duplicates the file descriptor fdn and returns the new fs */ - public int dupFD(int fdn) { - int i; - if(fdn < 0 || fdn >= OPEN_MAX) return -1; - if(fds[fdn] == null) return -1; - for(i=0;i= OPEN_MAX) return -1; + if(fds[fdn] == null) return -1; + for(i=0;i= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + + final Seekable.File sf; + try { + sf = new Seekable.File(f,write,(flags & O_TRUNC) != 0); + } catch(FileNotFoundException e) { + if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES); + return null; + } catch(IOException e) { throw new ErrnoException(EIO); } + + return new SeekableFD(sf,flags) { protected FStat _fstat() { return hostFStat(f,data); } }; + } + + FStat hostFStat(File f, Object data) { return new HostFStat(f); } + + FD hostFSDirFD(File f, Object data) { return null; } + + FD _open(String path, int flags, int mode) throws ErrnoException { + return hostFSOpen(new File(path),flags,mode,null); + } + + /** The open syscall */ + private int sys_open(int addr, int flags, int mode) throws ErrnoException, FaultException { + String name = cstring(addr); + + // HACK: TeX, or GPC, or something really sucks + if(name.length() == 1024 && getClass().getName().equals("tests.TeX")) name = name.trim(); + + flags &= ~O_NOCTTY; // this is meaningless under nestedvm + FD fd = _open(name,flags,mode); + if(fd == null) return -ENOENT; + int fdn = addFD(fd); + if(fdn == -1) { fd.close(); return -ENFILE; } + return fdn; } /** The write syscall */ - private int sys_write(int fdn, int addr, int count) { + + private int sys_write(int fdn, int addr, int count) throws FaultException, ErrnoException { count = Math.min(count,MAX_CHUNK); if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + byte[] buf = byteBuf(count); + copyin(addr,buf,count); try { - byte[] buf = byteBuf(count); - copyin(addr,buf,count); return fds[fdn].write(buf,0,count); - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; - } catch(IOException e) { - // FEATURE: We should support signals and send a SIGPIPE - if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13); - System.err.println(e); - return -EIO; + } catch(ErrnoException e) { + if(e.errno == EPIPE) sys_exit(128+13); + throw e; } } /** The read syscall */ - private int sys_read(int fdn, int addr, int count) { + private int sys_read(int fdn, int addr, int count) throws FaultException, ErrnoException { count = Math.min(count,MAX_CHUNK); if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD; - try { - byte[] buf = byteBuf(count); - int n = fds[fdn].read(buf,0,count); - copyout(buf,addr,n); - return n; - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; - } catch(IOException e) { - System.err.println(e); - return -EIO; - } + if(fds[fdn] == null) return -EBADFD; + byte[] buf = byteBuf(count); + int n = fds[fdn].read(buf,0,count); + copyout(buf,addr,n); + return n; } /** The close syscall */ @@ -758,45 +801,36 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** The seek syscall */ - private int sys_lseek(int fdn, int offset, int whence) { + private int sys_lseek(int fdn, int offset, int whence) throws ErrnoException { if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null) return -EBADFD; if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL; - try { - int n = fds[fdn].seek(offset,whence); - return n < 0 ? -ESPIPE : n; - } catch(IOException e) { - return -ESPIPE; - } + int n = fds[fdn].seek(offset,whence); + return n < 0 ? -ESPIPE : n; } /** The stat/fstat syscall helper */ - int stat(FStat fs, int addr) { - try { - memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16) - memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode - memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16) - memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16) - memWrite(addr+16,fs.size()); // st_size - memWrite(addr+20,fs.atime()); // st_atime - // memWrite(addr+24,0) // st_spare1 - memWrite(addr+28,fs.mtime()); // st_mtime - // memWrite(addr+32,0) // st_spare2 - memWrite(addr+36,fs.ctime()); // st_ctime - // memWrite(addr+40,0) // st_spare3 - memWrite(addr+44,fs.blksize()); // st_bklsize; - memWrite(addr+48,fs.blocks()); // st_blocks - // memWrite(addr+52,0) // st_spare4[0] - // memWrite(addr+56,0) // st_spare4[1] - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; - } + int stat(FStat fs, int addr) throws FaultException { + memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16) + memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode + memWrite(addr+8,fs.nlink()<<16|fs.uid()&0xffff); // st_nlink (top 16) // st_uid (bottom 16) + memWrite(addr+12,fs.gid()<<16|0); // st_gid (top 16) // st_rdev (bottom 16) + memWrite(addr+16,fs.size()); // st_size + memWrite(addr+20,fs.atime()); // st_atime + // memWrite(addr+24,0) // st_spare1 + memWrite(addr+28,fs.mtime()); // st_mtime + // memWrite(addr+32,0) // st_spare2 + memWrite(addr+36,fs.ctime()); // st_ctime + // memWrite(addr+40,0) // st_spare3 + memWrite(addr+44,fs.blksize()); // st_bklsize; + memWrite(addr+48,fs.blocks()); // st_blocks + // memWrite(addr+52,0) // st_spare4[0] + // memWrite(addr+56,0) // st_spare4[1] return 0; } /** The fstat syscall */ - private int sys_fstat(int fdn, int addr) { + private int sys_fstat(int fdn, int addr) throws FaultException { if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null) return -EBADFD; return stat(fds[fdn].fstat(),addr); @@ -808,17 +842,13 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { long tv_usec; }; */ - private int sys_gettimeofday(int timevalAddr, int timezoneAddr) { + private int sys_gettimeofday(int timevalAddr, int timezoneAddr) throws FaultException { long now = System.currentTimeMillis(); int tv_sec = (int)(now / 1000); int tv_usec = (int)((now%1000)*1000); - try { - memWrite(timevalAddr+0,tv_sec); - memWrite(timevalAddr+4,tv_usec); - return 0; - } catch(FaultException e) { - return -EFAULT; - } + memWrite(timevalAddr+0,tv_sec); + memWrite(timevalAddr+4,tv_usec); + return 0; } private int sys_sleep(int sec) { @@ -862,15 +892,17 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { private int sys_sysconf(int n) { switch(n) { case _SC_CLK_TCK: return 1000; + case _SC_PAGESIZE: return writePages.length == 1 ? 4096 : (1<incr is how much to increase the break by */ - public int sbrk(int incr) { + public final int sbrk(int incr) { if(incr < 0) return -ENOMEM; if(incr==0) return heapEnd; incr = (incr+3)&~3; @@ -886,7 +918,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { try { for(int i=start;i= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null) return -EBADFD; - return fds[fdn].isatty() ? 1 : 0; - } - + /** Hook for subclasses to do something when the process exits */ + void _exited() { } - /** Hook for subclasses to do something when the process exits (MUST set state = EXITED) */ - protected void _exit() { state = EXITED; } private int sys_exit(int status) { exitStatus = status; for(int i=0;i= OPEN_MAX) return -EBADFD; @@ -954,19 +983,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { for(i=arg;isyscall should be the contents of V0 and a, b, c, and d should be the contenst of A0, A1, A2, and A3. The call MAY change the state @see Runtime#state state */ - protected int syscall(int syscall, int a, int b, int c, int d) { + protected final int syscall(int syscall, int a, int b, int c, int d, int e, int f) { + try { + int n = _syscall(syscall,a,b,c,d,e,f); + //if(n < 0) System.err.println("syscall: " + syscall + " returned " + n); + return n; + } catch(ErrnoException ex) { + //ex.printStackTrace(); + return -ex.errno; + } catch(FaultException ex) { + return -EFAULT; + } catch(RuntimeException ex) { + ex.printStackTrace(); + throw new Error("Internal Error in _syscall()"); + } + } + + int _syscall(int syscall, int a, int b, int c, int d, int e, int f) throws ErrnoException, FaultException { switch(syscall) { case SYS_null: return 0; case SYS_exit: return sys_exit(a); @@ -994,9 +1036,11 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { case SYS_sleep: return sys_sleep(a); case SYS_times: return sys_times(a); case SYS_getpagesize: return sys_getpagesize(); - case SYS_isatty: return sys_isatty(a); case SYS_fcntl: return sys_fcntl(a,b,c); case SYS_sysconf: return sys_sysconf(a); + + case SYS_memcpy: memcpy(a,b,c); return a; + case SYS_memset: memset(a,b,c); return a; case SYS_kill: case SYS_fork: @@ -1007,10 +1051,10 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { case SYS_mkdir: case SYS_getcwd: case SYS_chdir: - System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")"); + if(STDERR_DIAG) System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")"); return -ENOSYS; default: - System.err.println("Attempted to use unknown syscall: " + syscall); + if(STDERR_DIAG) System.err.println("Attempted to use unknown syscall: " + syscall); return -ENOSYS; } } @@ -1040,7 +1084,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } /** Helper function to read a cstring from main memory */ - public String cstring(int addr) throws ReadFaultException { + public final String cstring(int addr) throws ReadFaultException { StringBuffer sb = new StringBuffer(); for(;;) { int word = memRead(addr&~3); @@ -1056,28 +1100,23 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** File Descriptor class */ public static abstract class FD { private int refCount = 1; - - /** returns true if the fd is readable */ - public boolean readable() { return false; } - /** returns true if the fd is writable */ - public boolean writable() { return false; } /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */ - public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); } + public int read(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } /** Write. Should return the number of bytes written or throw an IOException on error */ - public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); } + public int write(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */ - public int seek(int n, int whence) throws IOException { return -1; } + public int seek(int n, int whence) throws ErrnoException { return -1; } + + public int getdents(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } + + public int flags() { return O_RDONLY; } /** Return a Seekable object representing this file descriptor (can be read only) This is required for exec() */ Seekable seekable() { return null; } - /** Should return true if this is a tty */ - // FEATURE: get rid of the isatty syscall and just do with newlib's dumb isatty.c - public boolean isatty() { return false; } - private FStat cachedFStat = null; public final FStat fstat() { if(cachedFStat == null) cachedFStat = _fstat(); @@ -1097,69 +1136,142 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { public abstract static class SeekableFD extends FD { private final int flags; private final Seekable data; - public boolean readable() { return (flags&3) != WR_ONLY; } - public boolean writable() { return (flags&3) != RD_ONLY; } SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; } protected abstract FStat _fstat(); + public int flags() { return flags; } Seekable seekable() { return data; } - public int seek(int n, int whence) throws IOException { - switch(whence) { - case SEEK_SET: break; - case SEEK_CUR: n += data.pos(); break; - case SEEK_END: n += data.length(); break; - default: return -1; + public int seek(int n, int whence) throws ErrnoException { + try { + switch(whence) { + case SEEK_SET: break; + case SEEK_CUR: n += data.pos(); break; + case SEEK_END: n += data.length(); break; + default: return -1; + } + data.seek(n); + return n; + } catch(IOException e) { + throw new ErrnoException(ESPIPE); } - data.seek(n); - return n; } - public int write(byte[] a, int off, int length) throws IOException { + public int write(byte[] a, int off, int length) throws ErrnoException { + if((flags&3) == RD_ONLY) throw new ErrnoException(EBADFD); // NOTE: There is race condition here but we can't fix it in pure java if((flags&O_APPEND) != 0) seek(0,SEEK_END); - return data.write(a,off,length); + try { + return data.write(a,off,length); + } catch(IOException e) { + throw new ErrnoException(EIO); + } } - public int read(byte[] a, int off, int length) throws IOException { - int n = data.read(a,off,length); - return n < 0 ? 0 : n; + public int read(byte[] a, int off, int length) throws ErrnoException { + if((flags&3) == WR_ONLY) throw new ErrnoException(EBADFD); + try { + int n = data.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } } protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } } } - public static class OutputStreamFD extends FD { - private OutputStream os; - public boolean writable() { return true; } - public OutputStreamFD(OutputStream os) { this.os = os; } - public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; } - public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } } - public FStat _fstat() { return new FStat(); } - } - - public static class InputStreamFD extends FD { - private InputStream is; - public boolean readable() { return true; } - public InputStreamFD(InputStream is) { this.is = is; } - public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; } - public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } } + public static class InputOutputStreamFD extends FD { + private final InputStream is; + private final OutputStream os; + + public InputOutputStreamFD(InputStream is) { this(is,null); } + public InputOutputStreamFD(OutputStream os) { this(null,os); } + public InputOutputStreamFD(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + if(is == null && os == null) throw new IllegalArgumentException("at least one stream must be supplied"); + } + + public int flags() { + if(is != null && os != null) return O_RDWR; + if(is != null) return O_RDONLY; + if(os != null) return O_WRONLY; + throw new Error("should never happen"); + } + + public void _close() { + if(is != null) try { is.close(); } catch(IOException e) { /*ignore*/ } + if(os != null) try { os.close(); } catch(IOException e) { /*ignore*/ } + } + + public int read(byte[] a, int off, int length) throws ErrnoException { + if(is == null) return super.read(a,off,length); + try { + int n = is.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(os == null) return super.write(a,off,length); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + public FStat _fstat() { return new FStat(); } } - protected static class StdinFD extends InputStreamFD { - public StdinFD(InputStream is) { super(is); } - public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } - public boolean isatty() { return true; } - } - protected static class StdoutFD extends OutputStreamFD { - public StdoutFD(OutputStream os) { super(os); } + static class TerminalFD extends InputOutputStreamFD { + public TerminalFD(InputStream is) { this(is,null); } + public TerminalFD(OutputStream os) { this(null,os); } + public TerminalFD(InputStream is, OutputStream os) { super(is,os); } public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } - public boolean isatty() { return true; } + public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } public int mode() { return 0600; } }; } + } + + // This is pretty inefficient but it is only used for reading from the console on win32 + static class TextInputStream extends InputStream { + private int pushedBack = -1; + private final InputStream parent; + public TextInputStream(InputStream parent) { this.parent = parent; } + public int read() throws IOException { + if(pushedBack != -1) { int c = pushedBack; pushedBack = -1; return c; } + int c = parent.read(); + if(c == '\r' && (c = parent.read()) != '\n') { pushedBack = c; return '\r'; } + return c; + } + public int read(byte[] buf, int pos, int len) throws IOException { + boolean pb = false; + if(pushedBack != -1 && len > 0) { + buf[0] = (byte) pushedBack; + pushedBack = -1; + pos++; len--; pb = true; + } + int n = parent.read(buf,pos,len); + if(n == -1) return -1; + for(int i=0;i b ? a : b; } + static byte[] getNullTerminatedBytes(String s) { + byte[] buf1 = getBytes(s); + byte[] buf2 = new byte[buf1.length+1]; + System.arraycopy(buf1,0,buf2,0,buf1.length); + return buf2; + } + + final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); } + final static int min(int a, int b) { return a < b ? a : b; } + final static int max(int a, int b) { return a > b ? a : b; } }