Commit | Line | Data |
---|---|---|
8eabb4ff MW |
1 | /* -*-java-*- |
2 | * | |
3 | * Declarations of C functions | |
4 | * | |
5 | * (c) 2018 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the Trivial IP Encryption (TrIPE) Android app. | |
11 | * | |
12 | * TrIPE is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. | |
24 | */ | |
25 | ||
26 | package uk.org.distorted.tripe; package object jni { | |
27 | ||
28 | /*----- Imports -----------------------------------------------------------*/ | |
29 | ||
30 | import java.io.{Closeable, File}; | |
31 | import java.util.Date; | |
32 | import Magic._; | |
33 | ||
34 | /*----- Main code ---------------------------------------------------------*/ | |
35 | ||
36 | /* Import the native code library. */ | |
37 | System.loadLibrary("toy"); | |
38 | ||
39 | /* Exception indicating that a wrapped native object has been clobbered. */ | |
40 | class NativeObjectTypeException(msg: String) extends RuntimeException(msg); | |
41 | type Wrapper = Array[Byte]; | |
42 | ||
43 | case class ErrorEntry(val tag: String, val err: Int); | |
44 | @native def errtab: Array[ErrorEntry]; | |
45 | @native def strerror(err: Int): CString; | |
46 | ||
47 | @native def hashsz(hash: String): Int; | |
48 | /* Return the output hash size for the named HASH function, or -1. */ | |
49 | ||
50 | /* Flags for `close'. */ | |
51 | val CF_CLOSERD = 1; | |
52 | val CF_CLOSEWR = 2; | |
53 | val CF_CLOSEMASK = CF_CLOSERD | CF_CLOSEWR; | |
54 | ||
55 | /* Flags for `lock'. */ | |
56 | val LKF_EXCL = 1; | |
57 | val LKF_WAIT = 2; | |
58 | ||
59 | /* Flags for `stat'. */ | |
60 | val S_IFMT = 0xf000; | |
61 | val S_IFIFO = 0x1000; | |
62 | val S_IFCHR = 0x2000; | |
63 | val S_IFDIR = 0x4000; | |
64 | val S_IFBLK = 0x6000; | |
65 | val S_IFREG = 0x8000; | |
66 | val S_IFLNK = 0xa000; | |
67 | val S_IFSOCK = 0xc000; | |
68 | ||
69 | object FileType extends Enumeration { | |
70 | val FIFO, CHR, DIR, BLK, REG, LNK, SOCK, UNK = Value; | |
71 | } | |
72 | import FileType.{Value => _, _}; | |
73 | ||
74 | class FileInfo private[this](val devMajor: Int, val devMinor: Int, | |
75 | val ino: Long, val mode: Int, val nlink: Int, | |
76 | val uid: Int, val gid: Int, | |
77 | _rdevMinor: Int, _rdevMajor: Int, | |
78 | val size: Long, | |
79 | val blksize: Int, val blocks: Long, | |
80 | val atime: Date, val mtime: Date, | |
81 | val ctime: Date) { | |
82 | def this(devMajor: Int, devMinor: Int, ino: Long, | |
83 | mode: Int, nlink: Int, uid: Int, gid: Int, | |
84 | rdevMinor: Int, rdevMajor: Int, | |
85 | size: Long, blksize: Int, blocks: Long, | |
86 | atime: Long, mtime: Long, ctime: Long) { | |
87 | this(devMajor, devMinor, ino, mode, nlink, uid, gid, | |
88 | rdevMajor, rdevMinor, size, blksize, blocks, | |
89 | new Date(atime), new Date(mtime), new Date(ctime)); | |
90 | } | |
91 | def perms: Int = mode&0xfff; | |
92 | def ftype: FileType.Value = (mode&S_IFMT) match { | |
93 | case S_IFIFO => FIFO | |
94 | case S_IFCHR => CHR | |
95 | case S_IFDIR => DIR | |
96 | case S_IFBLK => BLK | |
97 | case S_IFREG => REG | |
98 | case S_IFLNK => LNK | |
99 | case S_IFSOCK => SOCK | |
100 | case _ => UNK | |
101 | } | |
102 | def isfifo: Boolean = ftype == FIFO | |
103 | def ischr: Boolean = ftype == CHR | |
104 | def isdir: Boolean = ftype == DIR | |
105 | def isblk: Boolean = ftype == BLK | |
106 | def isreg: Boolean = ftype == REG | |
107 | def islnk: Boolean = ftype == LNK | |
108 | def issock: Boolean = ftype == SOCK | |
109 | def isdev: Boolean = ischr || isblk; | |
110 | private[this] def mustBeDevice() { | |
111 | if (!isdev) throw new IllegalArgumentException("Object is not a device"); | |
112 | } | |
113 | def rdevMajor: Int = { mustBeDevice(); _rdevMajor } | |
114 | def rdevMinor: Int = { mustBeDevice(); _rdevMinor } | |
115 | } | |
116 | @native protected def unlink(path: CString); | |
117 | def unlink(path: String) { unlink(path.toCString); } | |
118 | def unlink(file: File) { unlink(file.getPath); } | |
119 | @native protected def rmdir(path: CString); | |
120 | def rmdir(path: String) { rmdir(path.toCString); } | |
121 | def rmdir(file: File) { rmdir(file.getPath); } | |
122 | @native protected def mkdir(path: CString, mode: Int); | |
123 | def mkdir(path: String, mode: Int) { mkdir(path.toCString, mode); } | |
124 | def mkdir(path: String) { mkdir(path, 0x1ff); } | |
125 | def mkdir(file: File, mode: Int) { mkdir(file.getPath, mode); } | |
126 | def mkdir(file: File) { mkdir(file.getPath); } | |
127 | @native protected def mkfile(path: CString, mode: Int); | |
128 | def mkfile(path: String, mode: Int) { mkfile(path.toCString, mode); } | |
129 | def mkfile(path: String) { mkfile(path, 0x1b6); } | |
130 | def mkfile(file: File, mode: Int) { mkfile(file.getPath, mode); } | |
131 | def mkfile(file: File) { mkfile(file.getPath); } | |
132 | @native protected def rename(from: CString, to: CString); | |
133 | def rename(from: String, to: String) | |
134 | { rename(from.toCString, to.toCString); } | |
135 | def rename(from: File, to: File) | |
136 | { rename(from.getPath, to.getPath); } | |
137 | @native protected def stat(path: CString): FileInfo; | |
138 | def stat(path: String): FileInfo = stat(path.toCString); | |
139 | def stat(file: File): FileInfo = stat(file.getPath); | |
140 | @native protected def lstat(path: CString): FileInfo; | |
141 | def lstat(path: String): FileInfo = lstat(path.toCString); | |
142 | def lstat(file: File): FileInfo = lstat(file.getPath); | |
143 | ||
144 | @native protected def opendir(path: CString): Wrapper; | |
145 | @native protected def readdir(path: CString, dir: Wrapper): CString; | |
146 | @native protected def closedir(path: CString, dir: Wrapper); | |
147 | ||
148 | abstract class BaseDirIterator[T](cpath: CString) | |
149 | extends LookaheadIterator[T] with Closeable { | |
150 | def this(path: String) { this(path.toCString); } | |
151 | def this(dir: File) { this(dir.getPath); } | |
152 | override def close() { closedir(cpath, dir); } | |
153 | override protected def finalize() { super.finalize(); close(); } | |
154 | private[this] val dir = opendir(cpath); | |
155 | protected def mangle(file: String): T; | |
156 | override protected def fetch(): Option[T] = readdir(cpath, dir) match { | |
157 | case null => None | |
158 | case f => f.toJString match { | |
159 | case "." | ".." => fetch() | |
160 | case jf => Some(mangle(jf)) | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | class DirIterator(val path: String) extends BaseDirIterator[String](path) { | |
166 | def this(dir: File) { this(dir.getPath); } | |
167 | override protected def mangle(file: String): String = file; | |
168 | } | |
169 | def listDir(path: String): List[String] = { | |
170 | val iter = new DirIterator(path); | |
171 | try { iter.toList } | |
172 | finally { iter.close(); } | |
173 | } | |
174 | def listDir(dir: File): List[String] = listDir(dir.getPath); | |
175 | ||
176 | class DirFilesIterator private[this](val dir: File, cpath: CString) | |
177 | extends BaseDirIterator[File](cpath) { | |
178 | def this(dir: File) { this(dir, dir.getPath.toCString); } | |
179 | def this(path: String) { this(new File(path), path.toCString); } | |
180 | override protected def mangle(file: String): File = new File(dir, file); | |
181 | } | |
182 | def listDirFiles(path: String): List[File] = { | |
183 | val iter = new DirFilesIterator(path); | |
184 | try { iter.toList } | |
185 | finally { iter.close(); } | |
186 | } | |
187 | def listDirFiles(dir: File): List[File] = listDirFiles(dir.getPath); | |
188 | ||
189 | @native protected def lock(path: CString, flags: Int): Wrapper; | |
190 | @native protected def unlock(lock: Wrapper); | |
191 | class FileLock(path: String, flags: Int) extends Closeable { | |
192 | def this(file: File, flags: Int) { this(file.getPath, flags); } | |
193 | def this(path: String) { this(path, LKF_EXCL); } | |
194 | def this(file: File) { this(file.getPath, LKF_EXCL); } | |
195 | private[this] val lk = lock(path.toCString, flags); | |
196 | override def close() { unlock(lk); } | |
197 | override protected def finalize() { super.finalize(); close(); } | |
198 | } | |
199 | def withLock[T](path: String, flags: Int)(body: => T): T = { | |
200 | val lk = new FileLock(path, flags); | |
201 | try { body; } finally { lk.close(); } | |
202 | } | |
203 | def withLock[T](file: File, flags: Int)(body: => T): T = | |
204 | withLock(file.getPath, flags) { body } | |
205 | def withLock[T](path: String)(body: => T): T = | |
206 | withLock(path, LKF_EXCL) { body } | |
207 | def withLock[T](file: File)(body: => T): T = | |
208 | withLock(file.getPath, LKF_EXCL) { body } | |
209 | ||
210 | @native protected def connect(path: CString): Wrapper; | |
211 | @native def send(conn: Wrapper, buf: CString, | |
212 | start: Int, len: Int); | |
213 | @native def recv(conn: Wrapper, buf: CString, | |
214 | start: Int, len: Int): Int; | |
215 | @native def close(conn: Wrapper, how: Int); | |
216 | class Connection(path: String) extends Closeable { | |
217 | def this(file: File) { this(file.getPath); } | |
218 | private[this] val conn = connect(path.toCString); | |
219 | override def close() { jni.close(conn, CF_CLOSEMASK); } | |
220 | override protected def finalize() { super.finalize(); close(); } | |
221 | class InputStream private[Connection] extends java.io.InputStream { | |
222 | override def read(): Int = { | |
223 | val buf = new Array[Byte](1); | |
224 | val n = read(buf, 0, 1); | |
225 | if (n < 0) -1 else buf(0)&0xff; | |
226 | } | |
227 | override def read(buf: Array[Byte]): Int = | |
228 | read(buf, 0, buf.length); | |
229 | override def read(buf: Array[Byte], start: Int, len: Int) = | |
230 | recv(conn, buf, start, len); | |
231 | override def close() { jni.close(conn, CF_CLOSERD); } | |
232 | } | |
233 | lazy val input = new InputStream; | |
234 | class OutputStream private[Connection] extends java.io.OutputStream { | |
235 | override def write(b: Int) { write(Array[Byte](b.toByte), 0, 1); } | |
236 | override def write(buf: Array[Byte]) { write(buf, 0, buf.length); } | |
237 | override def write(buf: Array[Byte], start: Int, len: Int) | |
238 | { send(conn, buf, start, len); } | |
239 | override def close() { jni.close(conn, CF_CLOSEWR); } | |
240 | } | |
241 | lazy val output = new OutputStream; | |
242 | } | |
243 | ||
244 | /*----- That's all, folks -------------------------------------------------*/ | |
245 | ||
246 | } |