| /* | |
| * File I/O, based on DOS int 21h calls | |
| * This file is part of the DOSMid project | |
| * http://dosmid.sourceforge.net | |
| * Modified by Rivoreo | |
| * https://sourceforge.net/p/rivoreo/dosmid-code/ | |
| * Copyright (C) 2018 Mateusz Viste | |
| * Copyright 2015-2024 Rivoreo | |
| */ | |
| #ifdef MSDOS | |
| #include <dos.h> /* REGS */ | |
| #else | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #include <errno.h> | |
| #define _fmemcpy memcpy | |
| #endif | |
| #include <string.h> /* _fmemcpy() */ | |
| #include "fio.h" /* include self for control */ | |
| #ifndef MSDOS | |
| static int sync_read(int fd, void *buffer, size_t count) { | |
| char *p = buffer; | |
| do { | |
| int s = read(fd, p, count); | |
| if(s < 0) { | |
| if(errno == EINTR) continue; | |
| if(p == (char *)buffer) return -1; | |
| break; | |
| } | |
| if(!s) break; | |
| count -= s; | |
| p += s; | |
| } while(count > 0); | |
| return p - (char *)buffer; | |
| } | |
| #endif | |
| static void fio_seek_sync(struct fiofile *f) { | |
| #ifdef MSDOS | |
| /* DOS 2+ - LSEEK - SET CURRENT FILE POSITION | |
| AH = 42h | |
| AL = origin of move | |
| 00h start of file | |
| 01h current file position | |
| 02h end of file | |
| BX = file handle | |
| CX:DX = (signed) offset from origin of new file position | |
| Return on success: | |
| CF clear | |
| DX:AX = new file position in bytes from start of file | |
| Return on error: | |
| CF set | |
| AX = error code */ | |
| union REGS regs; | |
| unsigned short *off; | |
| long offset; | |
| #endif | |
| if ((f->flags & FIO_FLAG_SEEKSYNC) == 0) return; | |
| #ifdef MSDOS | |
| offset = f->curpos; | |
| off = (unsigned short *)(&offset); | |
| regs.x.ax = 0x4200u; | |
| regs.x.bx = f->fh; | |
| regs.x.cx = off[1]; | |
| regs.x.dx = off[0]; | |
| int86(0x21, ®s, ®s); | |
| f->flags &= ~FIO_FLAG_SEEKSYNC; | |
| /* if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
| return(((long)regs.x.dx << 16) | regs.x.ax); */ | |
| #else | |
| lseek(f->fh, f->curpos, SEEK_SET); | |
| #endif | |
| } | |
| /* seek to offset position of file pointed at by fhandle. returns current file position on success, a negative error otherwise */ | |
| signed long int fio_seek(struct fiofile *f, unsigned short int origin, signed long int offset) { | |
| switch (origin) { | |
| case FIO_SEEK_START: | |
| if (offset < 1) { | |
| f->curpos = 0; | |
| } else { | |
| f->curpos = offset; | |
| } | |
| break; | |
| case FIO_SEEK_END: | |
| f->curpos = f->flen; | |
| case FIO_SEEK_CUR: | |
| f->curpos += offset; | |
| break; | |
| } | |
| f->flags |= FIO_FLAG_SEEKSYNC; | |
| return(f->curpos); | |
| } | |
| /* reads a line from file pointed at by fhandle, fill buff up to buflen bytes. returns the line length (possibly longer than buflen), or -1 on EOF */ | |
| int fio_getline(struct fiofile *f, void far *buff, short int buflen) { | |
| unsigned char bytebuf; | |
| short linelen = 0; | |
| buflen--; /* leave space for the zero terminator */ | |
| for (;;) { | |
| if (fio_read(f, &bytebuf, 1) == 0) { /* EOF */ | |
| if (linelen == 0) linelen = -1; | |
| break; | |
| } | |
| if (bytebuf == '\n') break; | |
| if (bytebuf == '\r') continue; | |
| linelen++; | |
| if (buflen > 0) { | |
| buflen--; | |
| *((unsigned char far *)buff) = bytebuf; | |
| buff = ((unsigned char far *)buff) + 1; | |
| } | |
| } | |
| *((unsigned char far *)buff) = 0; | |
| return(linelen); | |
| } | |
| static void loadcache(struct fiofile *f) { | |
| #ifdef MSDOS | |
| union REGS regs; | |
| struct SREGS sregs; | |
| #endif | |
| fio_seek_sync(f); | |
| f->flags |= FIO_FLAG_SEEKSYNC; | |
| f->bufoffs = f->curpos; | |
| #ifdef MSDOS | |
| regs.h.ah = 0x3f; | |
| regs.x.bx = f->fh; | |
| regs.x.cx = FIO_CACHE; | |
| sregs.ds = FP_SEG(f->buff); | |
| regs.x.dx = FP_OFF(f->buff); | |
| int86x(0x21, ®s, ®s, &sregs); | |
| #else | |
| sync_read(f->fh, f->buff, FIO_CACHE); | |
| #endif | |
| } | |
| /* open file fname and set fhandle with the associated file handle. returns 0 on success, non-zero otherwise */ | |
| int fio_open(const char far *fname, int mode, struct fiofile *f) { | |
| #ifdef MSDOS | |
| /* DOS 2+ - OPEN - OPEN EXISTING FILE | |
| AH = 3Dh | |
| AL = access and sharing modes | |
| DS:DX -> ASCIZ filename | |
| CL = attribute mask of files to look for (server call only) | |
| Return: | |
| CF clear if successful | |
| AX = file handle or error code | |
| CF set on error */ | |
| union REGS regs; | |
| struct SREGS sregs; | |
| /* */ | |
| regs.h.ah = 0x3d; | |
| regs.h.al = mode; | |
| sregs.ds = FP_SEG(fname); | |
| regs.x.dx = FP_OFF(fname); | |
| int86x(0x21, ®s, ®s, &sregs); | |
| f->fh = regs.x.ax; | |
| if (regs.x.cflag != 0) return(-1); | |
| #else | |
| int flags; | |
| switch(mode) { | |
| case FIO_OPEN_RD: | |
| flags = O_RDONLY; | |
| break; | |
| case FIO_OPEN_WR: | |
| flags = O_WRONLY; | |
| break; | |
| case FIO_OPEN_RW: | |
| flags = O_RDWR; | |
| break; | |
| default: | |
| return -1; | |
| } | |
| int fd = open(fname, flags); | |
| if(fd == -1) return -1; | |
| f->fh = fd; | |
| #endif | |
| f->curpos = 0; | |
| loadcache(f); | |
| /* fseek to end so I know the file length */ | |
| #ifdef MSDOS | |
| regs.x.ax = 0x4202u; | |
| regs.x.bx = f->fh; | |
| regs.x.cx = 0; | |
| regs.x.dx = 0; | |
| int86(0x21, ®s, ®s); | |
| f->flen = (((long)regs.x.dx << 16) | regs.x.ax); | |
| /* fseek to start */ | |
| regs.x.ax = 0x4200u; | |
| regs.x.bx = f->fh; | |
| regs.x.cx = 0; | |
| regs.x.dx = 0; | |
| int86(0x21, ®s, ®s); | |
| #else | |
| off_t len = lseek(fd, 0, SEEK_END); | |
| if(len != (off_t)-1) f->flen = len; | |
| lseek(fd, 0, SEEK_SET); | |
| #endif | |
| f->flags = FIO_FLAG_SEEKSYNC; | |
| return(0); | |
| } | |
| /* reads count bytes from file pointed at by fhandle, and writes the data into buff. returns the number of bytes actually read, or a negative number on error */ | |
| int fio_read(struct fiofile *f, void far *buff, int count) { | |
| #ifdef MSDOS | |
| /* DOS 2+ - READ - READ FROM FILE OR DEVICE | |
| * AH = 3Fh | |
| * BX = file handle | |
| * CX = number of bytes to read | |
| * DS:DX -> buffer for data | |
| * Return: | |
| * CF clear if successful | |
| * AX = number of bytes actually read (0 if at EOF before call) | |
| * CF set on error | |
| * AX = error code (05h,06h) (see #01680 at AH=59h/BX=0000h) */ | |
| union REGS regs; | |
| struct SREGS sregs; | |
| #endif | |
| if (f->curpos + count > f->flen) count = f->flen - f->curpos; | |
| if (count == 0) return(0); | |
| if (count <= FIO_CACHE) { | |
| if ((f->curpos < f->bufoffs) || (f->curpos + count > f->bufoffs + FIO_CACHE)) { | |
| loadcache(f); | |
| } | |
| _fmemcpy(buff, f->buff + (f->curpos - f->bufoffs), count); | |
| f->curpos += count; | |
| return(count); | |
| } | |
| fio_seek_sync(f); | |
| #ifdef MSDOS | |
| regs.h.ah = 0x3f; | |
| regs.x.bx = f->fh; | |
| regs.x.cx = count; | |
| sregs.ds = FP_SEG(buff); | |
| regs.x.dx = FP_OFF(buff); | |
| int86x(0x21, ®s, ®s, &sregs); | |
| if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
| f->curpos += regs.x.ax; | |
| return(regs.x.ax); | |
| #else | |
| return sync_read(f->fh, buff, count); | |
| #endif | |
| } | |
| /* close file handle. returns 0 on success, non-zero otherwise */ | |
| int fio_close(struct fiofile *f) { | |
| #ifdef MSDOS | |
| /* DOS 2+ - CLOSE - CLOSE FILE | |
| AH = 3Eh | |
| BX = file handle | |
| Return on success: | |
| CF clear | |
| AX destroyed | |
| On error: | |
| CF set | |
| AX = error code */ | |
| union REGS regs; | |
| regs.h.ah = 0x3e; | |
| regs.x.bx = f->fh; | |
| int86(0x21, ®s, ®s); | |
| if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
| return(0); | |
| #else | |
| return close(f->fh); | |
| #endif | |
| } |