Human68k CUI emulator with sound.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

v68io.c 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <limits.h>
  4. #include <mntent.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <dirent.h>
  10. #include <errno.h>
  11. #include "v68io.h"
  12. #define MAX_DRIVES ('Z' - 'A' + 1)
  13. struct drive {
  14. char vpath[PATH_MAX];
  15. char cwd[PATH_MAX];
  16. };
  17. struct drive drives[MAX_DRIVES];
  18. static uint8_t curdrive = 0;
  19. struct dosfile {
  20. int fd;
  21. uint16_t mode;
  22. char name[0x80];
  23. };
  24. struct dosfile dosfiles[96];
  25. int v68_io_init() {
  26. memset(drives, 0, sizeof(drives));
  27. // Init Z: as unix /
  28. curdrive = 'Z' - 'A';
  29. strncpy(drives[curdrive].vpath, "/", PATH_MAX);
  30. char buf[PATH_MAX];
  31. buf[0] = 0;
  32. getcwd(buf, PATH_MAX);
  33. int l = strlen(buf);
  34. if(l > 0) {
  35. for(int i = 1; i < l; i++)
  36. drives[curdrive].cwd[i-1] = buf[i] == '/' ? '\\' : buf[i];
  37. }
  38. dosfiles[0].fd = 0;
  39. dosfiles[0].mode = 0;
  40. strcpy(dosfiles[0].name, "STDIN");
  41. dosfiles[1].fd = 1;
  42. dosfiles[1].mode = 1;
  43. strcpy(dosfiles[0].name, "STDOUT");
  44. dosfiles[2].fd = 2;
  45. dosfiles[2].mode = 1;
  46. strcpy(dosfiles[0].name, "STDERR");
  47. // reserved fds, I think one is for printer or something
  48. dosfiles[3].fd = -1;
  49. dosfiles[4].fd = -1;
  50. return 0;
  51. }
  52. uint8_t v68_io_unused_drive() {
  53. // From D: to Y:
  54. for(int i = 3; i < 25; i++) {
  55. if(!drives[i].vpath[0]) return i;
  56. }
  57. return 0xff;
  58. }
  59. int v68_io_autodetect_drives() {
  60. static char *ignore_fstypes[] = {
  61. "sysfs", "proc", "devtmpfs", "devpts", "tmpfs", "securityfs", "swap", 0
  62. };
  63. char *home = getenv("HOME");
  64. if(home) {
  65. strncpy(drives['H' - 'A'].vpath, home, PATH_MAX);
  66. drives['H' - 'A'].cwd[0] = 0;
  67. }
  68. FILE *f = fopen("/etc/fstab", "r");
  69. struct mntent *m;
  70. while((m = getmntent(f))) {
  71. int valid = 1;
  72. for(char **i = ignore_fstypes; *i; i++) {
  73. if(!strcmp(*i, m->mnt_type))
  74. valid = 0;
  75. }
  76. if(!strcmp(m->mnt_dir, "/"))
  77. valid = 0;
  78. if(valid) {
  79. v68_io_add_drive(v68_io_unused_drive(), m->mnt_dir);
  80. }
  81. }
  82. fclose(f);
  83. return 0;
  84. }
  85. int v68_io_open(char *filename, int mode) {
  86. char xlated[PATH_MAX];
  87. v68_io_xlate_dos_path(filename, xlated, sizeof(xlated));
  88. char resolved[PATH_MAX];
  89. v68_io_resolve_path(xlated, resolved, sizeof(resolved));
  90. switch(mode & 0x03) {
  91. case 0x00:
  92. mode = O_RDONLY;
  93. break;
  94. case 0x01:
  95. mode = O_WRONLY | O_TRUNC;
  96. break;
  97. case 0x02:
  98. mode = O_RDWR;
  99. break;
  100. }
  101. int fd = open(resolved, mode, 0644);
  102. if(fd < 0) {
  103. // fprintf(stderr, "Could not open %s: %s (%d)\n", filename, strerror(errno), errno);
  104. return -1;
  105. }
  106. for(int i = 5; i < sizeof(dosfiles) / sizeof(dosfiles[0]); i++) {
  107. if(dosfiles[i].fd == 0) {
  108. dosfiles[i].fd = fd;
  109. strncpy(dosfiles[i].name, filename, sizeof(dosfiles[i].name));
  110. dosfiles[i].mode = mode;
  111. return i;
  112. }
  113. }
  114. close(fd);
  115. return -1;
  116. }
  117. int v68_io_create(char *filename, int attribs) {
  118. char xlated[PATH_MAX];
  119. v68_io_xlate_dos_path(filename, xlated, sizeof(xlated));
  120. char resolved[PATH_MAX];
  121. v68_io_resolve_path(xlated, resolved, sizeof(resolved));
  122. int fd = creat(resolved, 0644);
  123. if(fd < 0) {
  124. fprintf(stderr, "Could not create %s: %s (%d)\n", resolved, strerror(errno), errno);
  125. return -1;
  126. }
  127. for(int i = 5; i < sizeof(dosfiles) / sizeof(dosfiles[0]); i++) {
  128. if(dosfiles[i].fd == 0) {
  129. dosfiles[i].fd = fd;
  130. strncpy(dosfiles[i].name, filename, sizeof(dosfiles[i].name));
  131. dosfiles[i].mode = 0x02;
  132. return i;
  133. }
  134. }
  135. close(fd);
  136. return -1;
  137. }
  138. #define CHECKFD(fd) { \
  139. if(fd >= sizeof(dosfiles) / sizeof(dosfiles[0])) \
  140. return -1; \
  141. \
  142. if(!dosfiles[fd].fd) \
  143. return -1; \
  144. }
  145. int v68_io_close(int fd) {
  146. verbose2("v68_io_close fd=%d\n", fd);
  147. CHECKFD(fd);
  148. int r = close(dosfiles[fd].fd);
  149. if(r) return -1;
  150. dosfiles[fd].fd = 0;
  151. dosfiles[fd].mode = 0;
  152. dosfiles[fd].name[0] = 0;
  153. return 0;
  154. }
  155. int v68_io_read(int fd, void *buf, size_t count) {
  156. CHECKFD(fd);
  157. return read(dosfiles[fd].fd, buf, count);
  158. }
  159. int v68_io_write(int fd, void *buf, size_t count) {
  160. CHECKFD(fd);
  161. return write(dosfiles[fd].fd, buf, count);
  162. }
  163. int v68_io_seek(int fd, off_t offset, int whence) {
  164. CHECKFD(fd);
  165. switch(whence) {
  166. case 1: whence = SEEK_CUR; break;
  167. case 2: whence = SEEK_END; break;
  168. default: whence = SEEK_SET;
  169. }
  170. return lseek(dosfiles[fd].fd, offset, whence);
  171. }
  172. int v68_io_tell(int fd) {
  173. CHECKFD(fd);
  174. return lseek(dosfiles[fd].fd, 0, SEEK_CUR);
  175. }
  176. int v68_io_rename(char *oldpath, char *newpath) {
  177. return rename(oldpath, newpath);
  178. }
  179. int v68_io_chmod(char *pathname, mode_t mode) {
  180. return 0;
  181. }
  182. int v68_io_curdrv(void) {
  183. return curdrive;
  184. }
  185. int v68_io_chgdrv(uint8_t drv) {
  186. if(drv > MAX_DRIVES) return -1;
  187. if(drives[drv].vpath[0] == 0) return -1;
  188. curdrive = drv;
  189. return 0;
  190. }
  191. int v68_io_getcwd(uint8_t drv, char *buf, size_t size) {
  192. if(drv == 0) drv = curdrive;
  193. if(drv > MAX_DRIVES) return -1;
  194. if(drives[drv].vpath[0] == 0) return -1;
  195. if(size < 1) return -1;
  196. if(!buf) return -1;
  197. strncpy(buf, drives[drv].cwd, size);
  198. return 0;
  199. }
  200. int v68_io_chdir(char *buf) {
  201. return 0;
  202. }
  203. int v68_argv_to_cmdline(int argc, char **argv, char *cmdline, int len) {
  204. return 0;
  205. }
  206. int v68_io_add_drive(uint8_t drive, char *path) {
  207. if(drive >= MAX_DRIVES) return -1;
  208. strncpy(drives[drive].vpath, path, PATH_MAX);
  209. drives[drive].cwd[0] = 0;
  210. return 0;
  211. }
  212. char *v68_io_xlate_path(char *filename, char *out, int len) {
  213. int longest = -1, max_length = -1;;
  214. for(int i = 0; i < MAX_DRIVES; i++) {
  215. int l = strlen(drives[i].vpath);
  216. if(!strncasecmp(filename, drives[i].vpath, l)) {
  217. if(l > max_length) {
  218. longest = i;
  219. max_length = l;
  220. }
  221. }
  222. }
  223. if(longest >= 0) {
  224. snprintf(out, len, "%c:%s", 'A' + longest, filename + max_length);
  225. for(int i = 0; i < len; i++) {
  226. if(out[i] == '/') out[i] = '\\';
  227. }
  228. return out;
  229. }
  230. return 0;
  231. }
  232. char *v68_io_xlate_dos_path(char *filename, char *out, int len) {
  233. int l = strlen(filename);
  234. if(l >= 2 && filename[1] == ':') {
  235. // If drive is specified
  236. uint8_t drv = (filename[0] & ~0x20) - 'A';
  237. if(drv < 0 || drv >= 26) return 0;
  238. if(!drives[drv].vpath[0]) return 0;
  239. if(l > 2) {
  240. if(drives[drv].vpath[0] == '/' && drives[drv].vpath[1] == 0)
  241. snprintf(out, len, "/%s", filename + 3);
  242. else
  243. snprintf(out, len, "%s/%s", drives[drv].vpath, filename + 3);
  244. } else {
  245. snprintf(out, len, "%s", drives[drv].vpath);
  246. }
  247. } else if(l >= 1 && (filename[0] == '/' || filename[0] == '\\')) {
  248. // Start with root of drive
  249. if(drives[curdrive].vpath[0] == '/' && drives[curdrive].vpath[1] == 0)
  250. snprintf(out, len, "/%s", filename + 1);
  251. else
  252. snprintf(out, len, "%s/%s", drives[curdrive].vpath, filename + 1);
  253. } else {
  254. // Start with current dir
  255. if(drives[curdrive].vpath[0] == '/' && drives[curdrive].vpath[1] == 0)
  256. snprintf(out, len, "/%s/%s", drives[curdrive].cwd, filename);
  257. else
  258. snprintf(out, len, "%s/%s/%s", drives[curdrive].vpath, drives[curdrive].cwd, filename);
  259. }
  260. for(char *c = out; *c; c++) {
  261. if(*c == '\\') *c = '/';
  262. }
  263. return out;
  264. }
  265. // translate URL entities
  266. #define APPEND_CHAR(x) { \
  267. out[out_pos++] = (x); \
  268. out[out_pos] = 0; \
  269. if(out_pos == len - 1) \
  270. return -1; \
  271. }
  272. static int urldecode(char *in, char *out, int len) {
  273. int in_percent = 0;
  274. char ent[3] = { 0, 0, 0 };
  275. int ent_pos = 0, out_pos = 0;
  276. for(char *c = in; *c; c++) {
  277. if(in_percent) {
  278. if((*c >= '0' && *c <= '9') || (*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) {
  279. ent[ent_pos] = *c;
  280. ent_pos++;
  281. if(ent_pos >= 2) {
  282. APPEND_CHAR(strtol(ent, 0, 16));
  283. in_percent = 0;
  284. }
  285. } else {
  286. // weird char
  287. APPEND_CHAR('%');
  288. for(int i = 0; i < ent_pos; i++) {
  289. APPEND_CHAR(ent[i]);
  290. }
  291. if(*c == '%') {
  292. in_percent = 1;
  293. ent_pos = 0;
  294. } else {
  295. APPEND_CHAR(*c);
  296. in_percent = 0;
  297. }
  298. }
  299. } else {
  300. if(*c == '%') {
  301. in_percent = 1;
  302. ent_pos = 0;
  303. } else {
  304. APPEND_CHAR(*c);
  305. }
  306. }
  307. }
  308. return out_pos;
  309. }
  310. #undef APPEND_CHAR
  311. static int find_ci(char *from, char *to, int to_len) {
  312. to[0] = 0;
  313. char buf[PATH_MAX];
  314. buf[0] = '/';
  315. buf[1] = 0;
  316. char *saveptr;
  317. for(char *c = strtok_r(from, "/\\", &saveptr); c && *c; c = strtok_r(0, "/\\", &saveptr)) {
  318. DIR *d = opendir(buf);
  319. if(!d) {
  320. to[0] = 0;
  321. return -1;
  322. }
  323. struct dirent de, *result;
  324. int found = 0;
  325. for(readdir_r(d, &de, &result); result; readdir_r(d, &de, &result)) {
  326. if(!strcasecmp(c, de.d_name)) {
  327. found = 1;
  328. snprintf(buf, to_len, "%s/%s", to, de.d_name);
  329. break;
  330. }
  331. }
  332. closedir(d);
  333. if(!found) {
  334. snprintf(buf, to_len, "%s/%s", to, c);
  335. // return -1;
  336. }
  337. strncpy(to, buf, to_len);
  338. }
  339. return 0;
  340. }
  341. char *v68_io_resolve_path(char *filename, char *out, int len) {
  342. char buf[PATH_MAX];
  343. if(urldecode(filename, buf, sizeof(buf)) < 0) return 0;
  344. // only deal with absolute paths
  345. if(buf[0] != '/') return 0;
  346. struct stat st;
  347. if(stat(buf, &st) == 0) {
  348. strncpy(out, buf, len);
  349. return out;
  350. }
  351. if(find_ci(buf, out, len)) return 0;
  352. return out;
  353. }
  354. void v68_dump_drives() {
  355. for(int i = 0; i < MAX_DRIVES; i++) {
  356. if(!drives[i].vpath[0]) continue;
  357. verbose2("%c:\\%s = %s\n", 'A' + i, drives[i].cwd, drives[i].vpath);
  358. }
  359. }