

# Opens a file READ/WRITE even on a READONLY filesystem
# fstype = FAT, FATRO, MFS, MFSRO
# optype = READ, WRITE

def ds3_file_access(self, fstype, optype):

    testdir = self.mkworkdir('d')
    name = 'fileaccs'

    self.mkfile("testit.bat", f"""\
d:
c:\\{name} {optype}
rem end
""", newline="\r\n")

    # compile sources
    self.mkcom_with_ia16(name, r"""

#include <dos.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define FNAME "FOO.DAT"
#define FDATA "0123456789abcdef\n"
char ibuf[32];

void __far __interrupt (*oldint24)(void);

volatile uint16_t int24_ax;

void __far __interrupt __attribute__ ((near_section)) myint24(void); /* Prototype */
__asm (
  "myint24:\n"
  "  pushw %bp\n"
  "  movw  %sp, %bp\n"

  "  pushw %ds\n"
  "  pushw 22(%bp)\n"   /* int21 caller's DS on stack */
  "  popw  %ds\n"
  "  movw %ax, int24_ax\n"
  "  popw  %ds\n"

  "  popw  %bp\n"

  "  movb $0x03, %al\n" /* 0=Ignore, 1=Retry, 2=Terminate, 3=Fail */
  "  iretw\n"
);


int main(int argc, char *argv[])
{

  int handle;
  unsigned int ret, num;

  if (argc < 2) {
    printf("Error: missing optype (READ|WRITE) argument\n");
    return -1;
  }

  // Setup int24 handler
  int24_ax = 0;
  oldint24 = _dos_getvect(0x24);
  _dos_setvect(0x24, myint24);

  ret = _dos_open(FNAME, O_RDWR, &handle);
  if (ret != 0) {
    printf("Error: File Open failed(ret = 0x%02x)\n", ret);
    _dos_setvect(0x24, oldint24);
    return -1;
  }
  printf("Info: File Open success\n");

  if (strcmp(argv[1], "WRITE") == 0) {
    // It's unlikely this write will fail on a write to readonly disk
    // due to DOS buffering, so use commit to force the error.
    printf("Info: About to write\n");
    ret = _dos_write(handle, FDATA, strlen(FDATA), &num);
    if (ret != 0) {
      printf("Info: File Write failed(ret = 0x%02x)\n", ret);
      // The file is in error condition, so _dos_close() causes another int24
      _dos_close(handle);
      _dos_setvect(0x24, oldint24);
      return -1;
    }
    printf("Info: About to commit\n");
    ret = _dos_commit(handle);
    if (ret != 0) {
      printf("Info: File Commit failed(ret=0x%02x, int24_ax=0x%04x)\n", ret, int24_ax);
      // The file is in error condition, so _dos_close() causes another int24
      _dos_close(handle);
      _dos_setvect(0x24, oldint24);
      return -1;
    }
    printf("Info: File Write/Commit success(num=%u)\n", num);

  } else {
    ret = _dos_read(handle, ibuf, strlen(FDATA), &num);
    if (ret != 0) {
      printf("Info: File Read failed(ret = 0x%02x)\n", ret);
      _dos_close(handle);
      _dos_setvect(0x24, oldint24);
      return -1;
    }
    printf("Info: File Read success(num=%u)\n", num);
  }

  ret = _dos_close(handle);
  if (ret != 0) {
    printf("Error: File Close returned(ret = %02x)\n", ret);
    _dos_setvect(0x24, oldint24);
    return -1;
  }
  printf("Info: File Close success\n");

  _dos_setvect(0x24, oldint24);
  return 0;
}
""")

    self.mkfile("FOO.DAT", "xxxxxxxx", dname=testdir)

    if fstype.startswith("MFS"):
        name = "dXXXXs/d"
    else:
        name = self.mkimage("12", cwd=testdir)

    if fstype.endswith("RO"):
        name += ":ro"

    results = self.runDosemu("testit.bat", config="""\
$_hdimage = "dXXXXs/c:hdtype1 %s +1"
$_floppy_a = ""
""" % name, timeout=10)

    self.assertIn("File Open success", results)
    if fstype.endswith("RO"):
        if optype == "WRITE":
            self.assertNotIn("File Write/Commit success", results)
        else:
            self.assertIn("File Read success", results)
            self.assertIn("File Close success", results)
    else:
        if optype == "WRITE":
            self.assertIn("File Write/Commit success", results)
        else:
            self.assertIn("File Read success", results)
        self.assertIn("File Close success", results)
