示例#1
0
//==============================================================
void *_MmFileLoadB(const char *file, int area)
//--------------------------------------------------------------
// malloc して読み込む
// ※ブロックタイプ
//--------------------------------------------------------------
// in:	file = ファイル名
//		area = 読み込みエリア
//--------------------------------------------------------------
// out:	読み込みアドレス
//==============================================================
{
	sFILE *fp;
	size_t len;
	void *p;

	fp = FsOpen(file);
	ASSERT(fp);
	len = FsGetSizeOrig(fp);
	p = MemMalloc(area, len, GetFileBaseName(file));
	ASSERT(p);
	FsRead(fp, p, len);
	FsClose(fp);

	mn_read_size = len;							// FIXME:実ファイルサイズを返すべきか?

//	PRINTF("File '%s' Loaded.(%d bytes)\n", file, len);

	return p;
}
示例#2
0
//==============================================================
void *MmTextFileLoad(const char *file)
//--------------------------------------------------------------
// テキストファイルを読み込む
// ※データ終端に '\0' を付加します
//--------------------------------------------------------------
// in:	file = ファイル名
//--------------------------------------------------------------
// out:	読み込みアドレス
//==============================================================
{
	sFILE *fp;
	size_t len;
	char *p;

	fp = FsOpen(file);
	ASSERT(fp);
	len = FsGetSizeOrig(fp);
	p = (char *)fsMalloc(len + 1, "text data");
	ASSERT(p);
	FsRead(fp, p, len);
	FsClose(fp);

	*(p + len) = '\0';

	return p;
}
示例#3
0
文件: v86.c 项目: 1tgr/mobius
static void ShInt21(context_v86_t *ctx)
{
    void *ptr;
    char *ch;
    uint32_t key;
    
    switch ((uint16_t) ctx->regs.eax >> 8)
    {
    case 0:
	ThrExitThread(0);
	break;

    case 1:
        while (!FsRead(ProcGetProcessInfo()->std_in, &key, 0, sizeof(key), NULL))
            ;

	wprintf(L"%c", (uint8_t) key);
	ctx->regs.eax = (ctx->regs.eax & 0xffffff00) | (uint8_t) key;
	break;

    case 2:
	wprintf(L"%c", ctx->regs.edx & 0xff);
	break;

    case 9:
	ptr = FP_TO_LINEAR(ctx->v86_ds, ctx->regs.edx);
	ch = strchr(ptr, '$');
	fwrite(ptr, 1, ch - (char*) ptr, stdout);
	break;

    case 0x4c:
	ThrExitThread(ctx->regs.eax & 0xff);
	break;
    }
}
示例#4
0
文件: console.c 项目: 1tgr/mobius
int ConClientThread(void *param)
{
    char buf[80];
    console_t *console;
    size_t bytes, length;
    wchar_t wbuf[80];
    con_mbstate_t state;

    console = param;
    LmuxAcquire(&lmux_consoles);
    if (current == NULL)
        current = console;
    LmuxRelease(&lmux_consoles);

    ConInitMbState(&state);
    while (FsRead(console->client, buf, 0, sizeof(buf), &bytes))
    {
        length = ConMultiByteToWideChar(wbuf, buf, bytes, &state);
        if (length > 0)
            ConPutStr(console, wbuf, length);
    }

    HndClose(console->client);
    LmuxAcquire(&lmux_consoles);
    if (current == console)
        current = NULL;
    LmuxRelease(&lmux_consoles);

    return 0;
}
示例#5
0
//==============================================================
void MakeBackupFile(const char *fname)
//--------------------------------------------------------------
// PATH_BACKUP にバックアップをとる
//--------------------------------------------------------------
// in:	file = バックアップを取るファイル名
//--------------------------------------------------------------
// out:	なし
//==============================================================
{
	char d_name[FNAME_MAXLEN];
	void *tmp_buf;
	sFILE *fd_src, *fd_dst;
	size_t len, size;

	// オリジナルのファイルを開く
	//----------------------------
	fd_src = FsOpen(fname);
	if(!fd_src)
	{
		return;
	}

	// バックアップファイルの作成
	//----------------------------
	sprintf(d_name, PATH_BACKUP"/%s.%s", GetFileBaseName(fname), StrMakeUniqueName());
	fd_dst = FsCreate(d_name);
	if (!fd_dst)
	{
		FsClose(fd_src);
		return;
	}

	// 作業バッファを確保
	//--------------------
	tmp_buf = devMalloc(TMPBUF_LEN, "MakeBackupFile");

	len = FsGetSizeOrig(fd_src);
	while(len)
	{
		size = (len >= TMPBUF_LEN) ? TMPBUF_LEN : len;
		FsRead(fd_src, tmp_buf, size);
		FsWrite(fd_dst, tmp_buf, size);

		len -= size;
	}

	FsClose(fd_src);
	FsClose(fd_dst);

	Free(tmp_buf);

//	SYSINFODSP("Make Backup file");
//	SYSINFODSP("%s", MSG_FILE(d_name));
//	SYSINFO("Make Backup file");
//	SYSINFO("%s", MSG_FILE(d_name));
//	SYSINFOCNS("Make Backup file");
//	SYSINFOCNS("%s", MSG_FILE(d_name));
}
示例#6
0
ssize_t	read(int fd, void *buf, size_t nbyte)
{
	size_t bytes;

	if (!FsRead(fd, buf, nbyte, &bytes))
		return -1;

	return bytes;
}
示例#7
0
void InputRecordRead(sFILE *fp)
{
	gInput[INP_CH0].record = FALSE;
	gInput[INP_CH0].playback = FALSE;

	u_int header;
	FsRead(fp, &header, sizeof(u_int));
	record_num = ntohl(header);
	record_ofs = record_num;

	FreeWork(record);
	record = (sInputRecord *)fsMalloc(sizeof(sInputRecord) * record_num, "record");
	ASSERT(record);

	FileBuffer buffer;
	size_t size = FsGetSize(fp) - 4;
	void *zptr = fsMalloc(size, "zlib");
	ASSERT(zptr);
	FsRead(fp, zptr, size);
	buffer.ptr = (u_char *)ZlibDecode(zptr, ZlibEncodeSize(zptr));
	buffer.ofs = 0;
	Free(zptr);

	sInputRecord *ptr = record;
	for(int i = 0; i < record_ofs; i += 1)
	{
		ptr->x = (int)getIntValue(&buffer);
		ptr->y = (int)getIntValue(&buffer);
		ptr->btn_p = getIntValue(&buffer);
		ptr->btn_td = getIntValue(&buffer);
		ptr->btn_tu = getIntValue(&buffer);

		RecKey *recKey = ptr->recKey;
		for(int h = 0; h < KEY_INPUT_BUFFER * 2; h += 1)
		{
			recKey->key = getCharValue(&buffer);
			recKey->push = (BOOL)getCharValue(&buffer);
			recKey += 1;
		}
		ptr->recNum = getIntValue(&buffer);
		ptr += 1;
	}
	Free(buffer.ptr);
}
示例#8
0
//----------------------------------------------------------------------
//
//	ProcessGetCodeSizes
//
//	Get the code sizes (stack & data) for a file.  A file descriptor
//	for the named file is returned.  This descriptor MUST be closed
//	(presumably by the caller) at some point.
//
//----------------------------------------------------------------------
int
ProcessGetCodeInfo (const char *file, uint32 *startAddr,
		    uint32 *codeStart, uint32 *codeSize,
		     uint32 *dataStart, uint32 *dataSize)
{
  int		fd;
  int		totalsize;
  char		buf[100];
  char		*pos;

  // Open the file for reading.  If it returns a negative number, the open
  // didn't work.
  if ((fd = FsOpen (file, FS_MODE_READ)) < 0) {
    dbprintf ('f', "ProcessGetCodeInfo: open of %s failed (%d).\n",
	      file, fd);
    return (-1);
  }
  dbprintf ('f', "File descriptor is now %d.\n", fd);
  if ((totalsize = FsRead (fd, buf, sizeof (buf))) != sizeof (buf)) {
    dbprintf ('f', "ProcessGetCodeInfo: read got %d (not %d) bytes from %s\n",
	      totalsize, (int)sizeof (buf), file);
    FsClose (fd);
    return (-1);
  }
  if (dstrstr (buf, "start:") == NULL) {
    dbprintf ('f', "ProcessGetCodeInfo: %s missing start line (not a DLX executable?)\n", file);
    return (-1);
  }
  pos = (char *)dindex (buf, ':') + 1;
  // Get the start address and overall size
  *startAddr = dstrtol (pos, &pos, 16);
  totalsize = dstrtol (pos, &pos, 16);
  // Get code & data section start & sizes
  *codeStart = dstrtol (pos, &pos, 16);
  *codeSize = dstrtol (pos, &pos, 16);
  *dataStart = dstrtol (pos, &pos, 16);
  *dataSize = dstrtol (pos, &pos, 16);
  // Seek to start of first real line
  FsSeek (fd, 1 + dindex (buf, '\n') - buf, 0);
  return (fd);
}
示例#9
0
文件: disk.c 项目: zhan1182/OS-Design
int DiskReadBlock (uint32 blocknum, disk_block *b) {
  int fsfd = -1;
  uint32 intrvals = 0;
  char *filename = NULL;

  if (blocknum >= DISK_NUMBLOCKS) {
    printf("DiskReadBlock: cannot read from block larger than filesystem size\n");
    return DISK_FAIL;
  }

  if (filename[11] == 'X') {
    printf("DiskReadBlock: you didn't change the filesystem filename in include/os/disk.h.  Cowardly refusing to do anything.\n");
    GracefulExit();
  }

  intrvals = DisableIntrs();

  // Open the hard disk file
  if ((fsfd = FsOpen(DISK_FILENAME, FS_MODE_READ)) < 0) {
    printf ("DiskReadBlock: File system %s cannot be opened!\n", DISK_FILENAME);
    return DISK_FAIL;
  }

  /* printf("fsfd = %d\n", fsfd); */

  // Read data from virtual disk
  FsSeek(fsfd, blocknum * DISK_BLOCKSIZE, FS_SEEK_SET);
  if (FsRead(fsfd, b->data, DISK_BLOCKSIZE) != DISK_BLOCKSIZE) {
    printf ("DiskReadBlock: Block %d could not be read!\n", blocknum);
    FsClose (fsfd);
    return DISK_FAIL;
  }

  // Close the hard disk file
  FsClose (fsfd);

  RestoreIntrs(intrvals);
  return DISK_BLOCKSIZE;
}
示例#10
0
static bool IpcReadFromPipe(handle_t file, void *buf, size_t length)
{
    size_t bytes, total;

    total = 0;
	errno = 0;
    while (total < length)
    {
        //printf("FsRead(file=%u, length=%u-%u=%u)\n",
            //file, length, total, length - total);
        if (!FsRead(file, (char*) buf + total, 0, length - total, &bytes) ||
			(length > 0 && bytes == 0))
		{
			_wdprintf(L"%s: IpcReadFromPipe(%u): bytes = %u, errno = %d\n", 
				ProcGetProcessInfo()->module_first->name, file, bytes, errno);
            return false;
		}

        total += bytes;
    }

    return true;
}
示例#11
0
//----------------------------------------------------------------------
//
//	ProcessGetFromFile
//
//	Inputs:
//	addr -	points to an integer that contains the address of
//		the byte past that previously returned.  If this is the
//		first call to this routine, *addr should be set to 0.
//	fd -	File descriptor from which to read data.  The file format
//		is the same as that used by the DLX simulator.
//	buf -	points to a buffer that will receive data from the input
//		file.  Note that the data is NOT 0-terminated, and may
//		include any number of 0 bytes.
//	max -	maximum length of data to return.  The routine collects data
//		until either the address changes or it has read max bytes.
//
//	Returns the number of bytes actually stored into buf.  In addition,
//	*addr is updated to point to the byte following the last byte in
//	the buffer.
//
//	Load a file into memory.  The file format consists of a
//	leading address, followed by a colon, followed by the data
//	to go at that address.  If the address is omitted, the data
//	follows that from the previous line of the file.
//
//----------------------------------------------------------------------
int
ProcessGetFromFile (int fd, unsigned char *buf, uint32 *addr, int max)
{
  char	localbuf[204];
  int	nbytes;
  int	seekpos;
  unsigned char *pos = buf;
  char	*lpos = localbuf;

  // Remember our position at the start of the routine so we can adjust
  // it later.
  seekpos = FsSeek (fd, 0, FS_SEEK_CUR);
  // The maximum number of characters we could read is limited to the
  // maximum buffer space available to the caller * 2 because each 2
  // characters in the input file result in a single byte of program
  // info being read in.
  max = max * 2;
  // If the callers maximum is greater than the available buffer space,
  // limit the buffer space further.
  if (max > (sizeof(localbuf)-4)) {
    max = sizeof(localbuf)-4;
  }
  if ((nbytes = FsRead (fd, localbuf, max)) <= 0) {
    return (0);
  }
  // 'Z' is unused in load file, so use it to mark the end of the buffer
  // Back up until just after the last newline in the data we read.
  dbprintf ('f', "Got %d bytes at offset %d ...", nbytes, seekpos);
  while (localbuf[--nbytes] != '\n') {
  }
  localbuf[nbytes+1] = 'Z';
  localbuf[nbytes+2] = '\0';
  dbprintf ('f', " terminated at %d.\n", nbytes);
  dbprintf ('f', "Buffer is '%s'\n", localbuf);
  nbytes = 0;
  while (dindex (lpos, 'Z') != NULL) {
    if (dindex (lpos, ':') == NULL) {
      break;
    }
    if (*lpos != ':') {
      // If we're going to go to a new address, we break out of the
      // loop and return what we've got already.
      if (nbytes > 0) {
	break;
      }
      *addr = dstrtol (lpos, &lpos, 16);
      dbprintf ('f', "New address is 0x%x.\n", (int)(*addr));
    }
    if (*lpos != ':') {
      break;
    }
    lpos++;	// skip past colon
    while (1) {
      while (((*lpos) == ' ') || (*lpos == '\t')) {
	lpos++;
      }
      if (*lpos == '\n') {
	lpos++;
	break;
      } else if (!(isxdigit (*lpos) && isxdigit (*(lpos+1)))) {
     // Exit loop if at least one digit isn't a hex digit.
	break;
      }
      pos[nbytes++] = (getxvalue(*lpos) * 16) + getxvalue(*(lpos+1));
      lpos += 2;
      (*addr)++;
    }
  }
  // Seek to just past the last line we read.
  FsSeek (fd, seekpos + lpos - localbuf, FS_SEEK_SET);
  dbprintf ('f', "Seeking to %d and returning %d bytes!\n",
	    (int)(seekpos + lpos - localbuf), nbytes);
  return (nbytes);
}
示例#12
0
文件: cdfs.c 项目: 1tgr/mobius
status_t CdfsReadDir(fsd_t *fsd, void *dir_cookie, dirent_t *buf)
{
	cdfs_t *cdfs;
	cdsearch_t *search;
	entry_t entry;
	uint32_t sector;
	size_t bytes_read;
	unsigned i;
	char entry_name[MAX_NAME_LENGTH];
	wchar_t entry_name_wide[MAX_NAME_LENGTH + 1];
	bool found;

	cdfs = (cdfs_t*) fsd;
	search = dir_cookie;

	sector = search->offset / SECTOR_SIZE;
	found = false;
	while (!found)
	{
		if (!FsRead(search->node->directory, 
			&entry, 
			search->offset, 
			offsetof(entry_t, id), 
			&bytes_read))
			return errno;

		if (bytes_read < offsetof(entry_t, id))
			return EHARDWARE;

		if (entry.struct_size == 0)
			return EEOF;

		assert(entry.id_length < _countof(entry_name));

		if (search->index >= 2 && entry.id_length > 0)
		{
			if (!FsRead(search->node->directory, 
				entry_name,
				search->offset + offsetof(entry_t, id), 
				min(_countof(entry_name), entry.id_length),
				&bytes_read))
				return errno;

			if (bytes_read < min(_countof(entry_name) - 1, entry.id_length))
				return EHARDWARE;

			for (i = 0; entry_name[i] != ';' && i < entry.id_length; i++)
				entry_name_wide[i] = (wchar_t) (unsigned char) tolower(entry_name[i]);

			entry_name_wide[i] = '\0';

			buf->vnode = CdfsGenerateNodeId(cdfs, search->node->id, search->index);
			wcsncpy(buf->name, entry_name_wide, _countof(buf->name));
			found = true;
		}

		search->offset += entry.struct_size;
		if (search->offset / SECTOR_SIZE != sector)
		{
			assert(search->offset / SECTOR_SIZE < sector + 1);
			sector++;
			search->offset = sector * SECTOR_SIZE;
		}

		search->index++;
	}

	return 0;
}
示例#13
0
文件: cdfs.c 项目: 1tgr/mobius
status_t CdfsParseElement(fsd_t *fsd, const wchar_t *name, 
						  wchar_t **new_path, vnode_t *node)
{
	cdfs_t *cdfs;
	cdnode_t *cdnode;
	entry_t entry;
	uint32_t offset, sector;
	uint8_t entry_name[MAX_NAME_LENGTH];
	wchar_t entry_name_wide[MAX_NAME_LENGTH + 1];
	unsigned i, index;
	size_t bytes_read;

	cdfs = (cdfs_t*) fsd;
	cdnode = CdfsGetNode(cdfs, node->id);
	if (cdnode == NULL)
	{
		wprintf(L"CdfsParseElement(%x/%s): invalid parent node\n",
			node->id, name);
		return ENOTFOUND;
	}

	if (cdnode->directory == NULL)
	{
        wprintf(L"CdfsParseElement(%x/%s): parent is not a directory\n", 
			node->id, name);
        return ENOTADIR;
    }

	offset = 0;
	sector = 0;
	index = 0;
	while (true)
	{
		if (!FsRead(cdnode->directory, 
			&entry, 
			offset, 
			offsetof(entry_t, id), 
			&bytes_read))
			return errno;

		if (bytes_read < offsetof(entry_t, id) ||
			entry.struct_size == 0)
			break;

		assert(entry.id_length < _countof(entry_name));

		if (index >= 2)
		{
			if (!FsRead(cdnode->directory, 
				entry_name,
				offset + offsetof(entry_t, id), 
				min(_countof(entry_name), entry.id_length),
				&bytes_read))
				return errno;

			if (bytes_read < min(_countof(entry_name) - 1, entry.id_length))
				break;

			for (i = 0; entry_name[i] != ';' && i < entry.id_length; i++)
				entry_name_wide[i] = (wchar_t) (unsigned char) tolower(entry_name[i]);

			entry_name_wide[i] = '\0';

			if (_wcsicmp(entry_name_wide, name) == 0)
			{
				//cdnode_t *temp;
				//wprintf(L"CdfsParseElement(%x/%s): ", node->id, name);
				node->id = CdfsAllocNode(cdfs, node->id, index, &entry, entry_name_wide);
				CdfsReleaseNode(cdfs, cdnode);
				//temp = CdfsGetNode(cdfs, node->id);
				//wprintf(L"%p %x\n", temp->name, node->id);
				//CdfsReleaseNode(cdfs, temp);
				return 0;
			}
		}

		offset += entry.struct_size;
		if (offset / SECTOR_SIZE != sector)
		{
			assert(offset / SECTOR_SIZE < sector + 1);
			sector++;
			offset = sector * SECTOR_SIZE;
		}

		index++;
	}

	CdfsReleaseNode(cdfs, cdnode);
	return ENOTFOUND;
}
示例#14
0
文件: typedump.c 项目: 1tgr/mobius
static void ShDumpFile(const wchar_t *name, void (*fn)(const void*, addr_t, size_t))
{
    static char buf[16 + 1];

    handle_t file;
    size_t len;
    addr_t origin;
    dirent_standard_t di;
    char *ptr, *dyn;

    di.length = 0;
    if (!FsQueryFile(name, FILE_QUERY_STANDARD, &di, sizeof(di)) ||
        di.length == 0)
    {
        di.length = sizeof(buf) - 1;
        ptr = buf;
        dyn = NULL;
    }
    else
    {
        dyn = malloc(di.length);
        ptr = dyn;
    }

    printf("ShDumpFile(%S): reading in chunks of %lu\n", name, (uint32_t) di.length);
    file = FsOpen(name, FILE_READ);
    if (file == NULL)
    {
        _pwerror(name);
        return;
    }

    origin = 0;
    do
    {
        //KeLeakBegin();

        //printf("[b]");
		if (!FsRead(file, ptr, origin, di.length, &len))
		{
			_pwerror(name);
			break;
		}

		//KeLeakEnd();

        if (len == 0)
        {
            printf("FSD read zero bytes but didn't report an error\n");
            break;
        }

        if (len < di.length)
            ptr[len] = '\0';
        /*printf("%u", len);*/
        fn(ptr, origin, len);

        origin += len;
        if (len < di.length)
        {
            printf("FSD hit the end of the file: successful but only %u bytes read\n", len);
            break;
        }

        //printf("[e]");
        fflush(stdout);
    } while (true);

    free(dyn);
    HndClose(file);
}
示例#15
0
文件: v86.c 项目: 1tgr/mobius
void ShCmdV86(const wchar_t *cmd, wchar_t *params)
{
    uint8_t *code;
    psp_t *psp;
    FARPTR fp_code, fp_stackend;
    handle_t thr, file;
    dirent_standard_t di;
    bool doWait;
    wchar_t **names, **values;

    ShParseParams(params, &names, &values, &params);
    params = ShPrompt(L" .COM file? ", params);
    if (*params == '\0')
        return;

    doWait = true;
    if (ShHasParam(names, L"nowait"))
        doWait = false;
    
    free(names);
    free(values);

    /*if (!FsQueryFile(params, FILE_QUERY_STANDARD, &di, sizeof(di)))
    {
        _pwerror(params);
        return;
    }*/
    di.length = 0x10000;

    file = FsOpen(params, FILE_READ);
    if (file == NULL)
    {
        wprintf(L"FsOpen: ");
        _pwerror(params);
        return;
    }

    code = aligned_alloc(di.length + 0x100);
    psp = (psp_t*) code;
    memset(psp, 0, sizeof(*psp));
    psp->int20 = 0x20cd;

    if (!FsRead(file, code + 0x100, 0, di.length, NULL))
    {
        wprintf(L"FsRead: ");
        _pwerror(params);
        HndClose(file);
        return;
    }

    HndClose(file);

    fp_code = i386LinearToFp(code);
    fp_code = MK_FP(FP_SEG(fp_code), FP_OFF(fp_code) + 0x100);
        
    if (sh_v86stack == NULL)
        sh_v86stack = aligned_alloc(65536);

    fp_stackend = i386LinearToFp(sh_v86stack);
    memset(sh_v86stack, 0, 65536);
    
    thr = ThrCreateV86Thread(fp_code, fp_stackend, 15, ShV86Handler, params);
    if (doWait)
        ThrWaitHandle(thr);

    /* xxx - need to clean up HndClose() implementation before we can use this */
    /*HndClose(thr);*/
}
示例#16
0
文件: console.c 项目: 1tgr/mobius
int ConKeyboardThread(void *param)
{
    uint32_t ch, code;
    handle_t keyboard;
    //void *old_buffer;
    //unsigned i;

    keyboard = FsOpen(SYS_DEVICES L"/keyboard", FILE_READ);
    while (FsRead(keyboard, &ch, 0, sizeof(ch), NULL))
    {
        code = ch & ~KBD_BUCKY_ANY;
        if ((ch & KBD_BUCKY_ALT) != 0 &&
            code >= KEY_F1 && 
            code <= KEY_F12)
        {
            /*LmuxAcquire(&lmux_consoles);
            LmuxAcquire(&current->lmux);
            ConDrawCursor(current, false);

            old_buffer = current->cookie;
            current = consoles + code - KEY_F1;

            if (old_buffer != current->buffer)
                for (i = 0; i < num_consoles; i++)
                    if (consoles[i].buffer == current->buffer)
                        ConRedraw(consoles + i);

            ConDrawCursor(current, true);
            LmuxRelease(&current->lmux);
            LmuxRelease(&lmux_consoles);*/
        }
        else if (ch == (KBD_BUCKY_CTRL | KBD_BUCKY_ALT | KEY_DEL))
            SysShutdown(SHUTDOWN_REBOOT);
        else if (ch == (KBD_BUCKY_ALT | '\t') || 
            ch == (KBD_BUCKY_ALT | KBD_BUCKY_SHIFT | '\t'))
        {
            LmuxAcquire(&lmux_consoles);

            LmuxAcquire(&current->lmux);
            ConDrawCursor(current, false);
            LmuxRelease(&current->lmux);

            if (ch & KBD_BUCKY_SHIFT)
            {
                if (current - consoles - 1 < 0)
                    current = consoles + num_consoles - 1;
                else
                    current--;
            }
            else
            {
                if (current - consoles + 1 >= num_consoles)
                    current = consoles;
                else
                    current++;
            }

            LmuxAcquire(&current->lmux);
            ConDrawCursor(current, true);
            LmuxRelease(&current->lmux);

            LmuxRelease(&lmux_consoles);
        }
        else
        {
            LmuxAcquire(&lmux_consoles);
            if (current != NULL && current->client != NULL)
                FsWrite(current->client, &ch, 0, sizeof(ch), NULL);
            LmuxRelease(&lmux_consoles);
        }
    }

    _wdprintf(L"console(keyboard): FsRead failed, %s\n", _wcserror(errno));
    return errno;
}