Example #1
0
File: test.c Project: 1tgr/mobius
void testCharDeviceIo(const wchar_t *name)
{
	handle_t file;
	fileop_t op;
	uint32_t key;
	
	file = FsOpen(name, FILE_READ);
	if (file == NULL)
		wprintf(L"Failed to open %s\n", name);
	else
	{
		op.event = EvtCreate(false);
		op.bytes = 0;
		wprintf(L"op = %p op.event = %u sig = %u\n", 
			&op,
			op.event, 
			EvtIsSignalled(op.event));
		if (op.event != NULL)
		{
			while (true)
			{
				status_t ret;

				ret = FsReadAsync(file, &key, 0, sizeof(key), &op);
				if (ret > 0)
					break;
				else if (ret == SIOPENDING)
				{
					DbgWrite(L"Wait start\n", 11);
					/*while (EvtIsSignalled(op.event))*/
						ThrWaitHandle(op.event);
					DbgWrite(L"Wait end\n", 9);
					ret = op.result;
				}

				/*wprintf(L"op.result = %d op.bytes = %u\n",
					op.result, op.bytes);*/
				if (ret == SIOPENDING)
					wprintf(L"op.result is still SIOPENDING\n");
				else
				{
					if (ret > 0 ||
						op.bytes == 0)
						break;
					wprintf(L"%c", (wchar_t) key);
					if (key == 27)
						break;
				}
				op.bytes = 0;
			}

			DbgWrite(L"Finished\n", 9);
			wprintf(L"Finished: op.result = %d, op.bytes = %u\n",
				op.result, op.bytes);
			HndClose(op.event);
		}
	}

	HndClose(file);
}
Example #2
0
File: test.c Project: 1tgr/mobius
void testBlockDeviceIo(const wchar_t *name)
{
	handle_t file;
	unsigned i, j;
	fileop_t op;
	wchar_t *ch;

	file = FsOpen(name, FILE_READ);
	if (file == NULL)
		wprintf(L"Failed to open %s\n", name);
	else
	{
		op.event = EvtCreate(false);
		for (j = 0; j < 1; j++)
		{
			status_t ret;

			wprintf(/*L"\x1b[2J"*/ L"\x1b[%um", (j % 8) + 30);

			ret = FsReadAsync(file, key, 9 * 512, sizeof(key), &op);
			if (ret > 0)
				break;
			else if (ret == SIOPENDING)
			{
				ThrWaitHandle(op.event);
				ret = op.result;
			}
			
			if (ret == SIOPENDING)
				wprintf(L"op.result is still SIOPENDING\n");
			else if (ret == 0 && op.bytes > 0)
			{
				wprintf(L"Read %u bytes\n", op.bytes);
				ch = str;
				for (i = 0; i < op.bytes; i++)
				{
					swprintf(ch, L"%02X", key[i]);
					ch += 2;
				}
				_cputws(str, op.bytes * 2);
				/*for (i = 0; i < op.bytes; i++)
					wprintf(L"%02X", key[i]);*/
			}
			else
				break;
		}
		DbgWrite(L"Finished\n", 9);
		wprintf(L"Finished: op.result = %d, op.bytes = %u\n",
			op.result, op.bytes);
		HndClose(op.event);
	}
	HndClose(file);
}
Example #3
0
File: test.c Project: 1tgr/mobius
void testFileIo(const wchar_t *name)
{
	handle_t file;
	unsigned i;
	size_t len;
	fileop_t op;
	uint64_t offset;
	
	file = FsOpen(name, FILE_READ);
	if (file == NULL)
	{
		wprintf(L"Failed to open %s\n", name);
		return;
	}

	op.event = EvtCreate(false);
	for (i = 0; i < 16; i++)
	{
		offset = 0;
		wprintf(L"\x1b[%um", (i % 8) + 30);
		while (true)
		{
			status_t ret;

			ret = FsReadAsync(file, key, offset, sizeof(key), &op);
			if (ret > 0)
				break;
			else if (ret == SIOPENDING)
			{
				ThrWaitHandle(op.event);
				ret = op.result;
			}
			len = op.bytes;
			if (len == 0)
				break;
			
			if (len < sizeof(key))
				key[len] = '\0';
			len = mbstowcs(str, key, _countof(key) - 1);
			if (len == -1)
				wprintf(L"invalid multibyte sequence\n");
			else
				_cputws(str, len);

			offset += op.bytes;
		}

		ThrSleep(5000);
	}

	HndClose(op.event);
	HndClose(file);
}
Example #4
0
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;
}
Example #5
0
File: eth.c Project: 1tgr/mobius
static void EthHandleBinding(void)
{
    net_binding_t *bind;
    request_eth_t req_eth;

    union
    {
        uint8_t raw[ETH_FRAME_LEN];
        net_ether header;
    } packet;

    page_array_t *pages;
    io_callback_t cb;
    event_t *event;
    net_hwaddr_t src, dst;

    bind = ThrGetCurrent()->info->param;

    pages = MemCreatePageArray(packet.raw, sizeof(packet.raw));
    event = EvtCreate(false);
    src.type = dst.type = NET_HW_ETHERNET;
    src.data_size = dst.data_size = 6;

    while (true)
    {
        req_eth.header.code = ETH_RECEIVE;
        req_eth.header.param = (void*) event;
        memcpy(req_eth.params.eth_receive.from, bcast, sizeof(bcast));
        req_eth.params.eth_receive.type = (uint16_t) (addr_t) bind->hw_cookie;
        req_eth.params.eth_receive.pages = pages;
        req_eth.params.eth_receive.length = sizeof(packet.raw);
        cb.type = IO_CALLBACK_FUNCTION;
        cb.u.function = EthHandleBindingCompletion;

        if (!IoRequest(&cb, bind->dev, &req_eth.header))
        {
            wprintf(L"EthHandleBinding: IoRequest error\n");
            break;
        }

        ThrWaitHandle(ThrGetCurrent(), &event->hdr);
        KeYield();

        memcpy(src.u.ethernet, packet.header.src, sizeof(src.u.ethernet));
        memcpy(dst.u.ethernet, packet.header.dst, sizeof(dst.u.ethernet));
        if (bind->proto->vtbl->receive_packet != NULL)
            bind->proto->vtbl->receive_packet(bind->proto, 
                bind, &src, &dst, 
                packet.raw + sizeof(packet.header),
                req_eth.params.eth_receive.length - sizeof(packet.header));
    }

    HndClose(&event->hdr);
    ThrExitThread(0);
    KeYield();
}
Example #6
0
bool GmgrInit(void)
{
    params_vid_t params;
    handle_t device;

    LmuxInit(&gmgr_draw);
    LmuxInit(&gmgr_mux_gfxs);
    atexit(GmgrCleanup);

    device = FsOpen(SYS_DEVICES L"/Classes/video0", FILE_READ | FILE_WRITE);
    if (device == NULL)
    {
        _wdprintf(SYS_DEVICES L"/Classes/video0" L": %s\n", _wcserror(errno));
        goto error0;
    }

    memset(&params.vid_setmode, 0, sizeof(params.vid_setmode));
    if (!FsRequestSync(device, VID_SETMODE, &params, sizeof(params), NULL))
    {
        _wdprintf(L"VID_SETMODE: %s\n", _wcserror(errno));
        goto error1;
    }

    gmgr_screen = GmgrCreateDeviceSurface(device, &params.vid_setmode);
    if (gmgr_screen == NULL)
    {
        _wdprintf(L"GmgrCreateDeviceSurface: %s\n", _wcserror(errno));
        goto error1;
    }

    gmgr_font = FontLoad(L"/Mobius/veramono.ttf", 12 * 64, 
        gmgr_screen->mode.bitsPerPixel < 8 ? FB_FONT_MONO : FB_FONT_SMOOTH);
    if (gmgr_font == NULL)
    {
        _wdprintf(L"/Mobius/veramono.ttf: %s\n", _wcserror(errno));
        goto error2;
    }

    if (!GmgrInitCursor())
        goto error3;

    return true;

error3:
    FontDelete(gmgr_font);
    gmgr_font = NULL;
error2:
    GmgrCloseSurface(gmgr_screen);
    gmgr_screen = NULL;
error1:
    HndClose(device);
error0:
    return false;
}
Example #7
0
gmgr_surface_t *GmgrCreateDeviceSurface(handle_t device, 
										const videomode_t *mode)
{
	gmgr_surface_t *surf;
	handle_t memory;

	surf = malloc(sizeof(*surf));
	if (surf == NULL)
		return NULL;

	surf->refs = 1;
	surf->device = device;
	surf->flags = GMGR_SURFACE_SHARED;
	surf->mode = *mode;

	if (mode->framebuffer[0] == '\0')
	{
		surf->base = NULL;

		if (!AccelCreateSurface(mode, device, &surf->surf))
		{
			_wdprintf(L"GmgrCreateDeviceSurface: AccelCreateSurface failed\n");
			free(surf);
			return NULL;
		}
	}
	else
	{
		memory = HndOpen(mode->framebuffer);
		surf->base = VmmMapSharedArea(memory,
			NULL,
			VM_MEM_USER | VM_MEM_READ | VM_MEM_WRITE);
		HndClose(memory);

		if (!FramebufCreateSurface(mode, surf->base, &surf->surf))
		{
			_wdprintf(L"GmgrCreateDeviceSurface: FramebufCreateSurface failed\n");
			VmmFree(surf->base);
			free(surf);
			return NULL;
		}
	}

	return surf;
}
Example #8
0
File: device.c Project: 1tgr/mobius
static void DevCallLoader(load_request_t *load)
{
    event_t *evt_done;

    evt_done = EvtCreate(false);
    load->evt_done = evt_done;

    SemDown(mux_load);
    LIST_ADD(load, load);
    SemUp(mux_load);

    wprintf(L"DevCallLoader: submitting request\n");
    SemUp(sem_load);
    ThrWaitHandle(current(), &evt_done->hdr);
    KeYield();

    HndClose(&evt_done->hdr);
}
Example #9
0
File: device.c Project: 1tgr/mobius
static void DevLoaderThread(void)
{
    load_request_t *load;
    event_t *evt_done;

    while (true)
    {
        wprintf(L"DevLoaderThread: waiting for request\n");
        SemDown(sem_load);
        wprintf(L"DevLoaderThread: got request\n");

        SemDown(mux_load);
        load = load_first;
        LIST_REMOVE(load, load);
        SemUp(mux_load);

        evt_done = (event_t*) HndCopy(&load->evt_done->hdr);

        switch (load->action)
        {
        case LOAD_ACTION_LOAD_DRIVER:
            load->status = DevDoLoadDriver(load->u.load_driver.name, 
                &load->u.load_driver.driver);
            break;

        case LOAD_ACTION_ADD_DEVICE:
            load->u.add_device.driver->add_device(load->u.add_device.driver,
                load->u.add_device.name,
                load->u.add_device.config);
            load->status = 0;
            break;

        default:
            wprintf(L"DevLoaderThread: invalid action: %u\n", load->action);
            assert(load->action == LOAD_ACTION_LOAD_DRIVER ||
                load->action == LOAD_ACTION_ADD_DEVICE);
            break;
        }

        EvtSignal(evt_done, true);
        HndClose(&evt_done->hdr);
    }
}
Example #10
0
File: thread.c Project: 1tgr/mobius
void ThrEvaluateWait(thread_t *thr, handle_hdr_t *reason)
{
    unsigned i, handles_set;
    int last_unset;

    handles_set = 0;
    last_unset = -1;
    for (i = 0; i < thr->num_wait_handles; i++)
    {
        if (thr->wait_handles[i] == reason)
        {
            HndClose(reason);
            thr->wait_handles[i] = NULL;
        }

        if (thr->wait_handles[i] == NULL)
            last_unset = i;
        else
            handles_set++;
    }

    //if (thr->num_wait_handles > 1)
        //wprintf(L"ThrEvaluateWait: handles_set = %u, num_wait_handles = %u: ",
            //handles_set, thr->num_wait_handles);

    if ((thr->wait_all  && handles_set == 0) ||
        (!thr->wait_all && handles_set < thr->num_wait_handles))
    {
        //if (thr->num_wait_handles > 1)
            //wprintf(L"running, last_unset = %d\n", last_unset);

        if (thr->wait_result != NULL)
            *thr->wait_result = last_unset;

        thr->wait_result = NULL;
        thr->num_wait_handles = 0;
        ThrRun(thr);
        ScNeedSchedule(true);
    }
    //else if (thr->num_wait_handles > 1)
        //wprintf(L"not running\n");
}
Example #11
0
void GmgrCloseSurface(gmgr_surface_t *surf)
{
	if (SysDecrementAndTest(&surf->refs))
	{
		surf->surf.vtbl->SurfDeleteCookie(&surf->surf);

		if (surf->base != NULL)
		{
			if (surf->flags & GMGR_SURFACE_SHARED)
				VmmFree(surf->base);
			else
				free(surf->base);
		}

		if (surf->device != 0)
			HndClose(surf->device);

		free(surf);
	}
}
Example #12
0
	bool OnCreate()
	{
		static const wchar_t mouse_prefix[] = L"mouse";
		handle_t dir;
		dirent_t di;
		wchar_t name[30];
		OS::LayoutColumn *column;

		if (!OS::DialogBox::OnCreate())
			return false;

		column = new OS::LayoutColumn(L"20");
		column->AddLayout(new OS::LayoutControl(&m_label));

		OS::Rect rect = GetClientArea();
		rect.Inflate(-5, -5);
		rect.bottom = rect.top + rect.Height() / _countof(m_buttons);

		m_num_buttons = 0;
		dir = FsOpenDir(SYS_DEVICES L"/Classes");
		while (FsReadDir(dir, &di, sizeof(di)) &&
			m_num_buttons < _countof(m_buttons))
		{
			if (wcslen(di.name) >= (_countof(mouse_prefix) - 1) &&
				_wcsnicmp(di.name, mouse_prefix, _countof(mouse_prefix) - 1) == 0)
			{
				swprintf(name, L"Press %c: %s", m_num_buttons + 'A', di.name);
				wcscpy(m_names[m_num_buttons], di.name);
				//m_buttons[m_num_buttons].Create(&m_container, name, rect);

				m_buttons[m_num_buttons].Create(&m_container, name, OS::Rect(0, 0, 0, 0));
				column->AddLayout(new OS::LayoutControl(m_buttons + m_num_buttons));

				rect.Offset(0, rect.Height());
				m_num_buttons++;
			}
		}

		HndClose(dir);

		if (m_num_buttons == 0)
		{
			m_label.Create(&m_container,
				L"No mice found\n"
				L"Check " SYS_BOOT L"/system.pro\n"
				L"\n"
				L"Press Esc to continue",
				OS::Label::AlignLeft | OS::Label::AlignTop);
		}
		else
		{
			m_label.Create(&m_container,
				L"Choose the input device to\n"
				L"use with The Möbius:",
				OS::Label::AlignLeft | OS::Label::AlignTop);
		}

		m_container.AddLayout(column);
		m_container.UpdateLayout();

		if (m_num_buttons == 1)
			OnKeyDown('A');

		return true;
	}
Example #13
0
File: v86.c Project: 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);*/
}
Example #14
0
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);
}
Example #15
0
void LmuxDelete(lmutex_t *mux)
{
    HndClose(mux->mutex);
}
Example #16
0
int mainCRTStartup(void)
{
    handle_t server, client;
    unsigned i;
    params_vid_t params;
    font_t *font;

    vid = FsOpen(SYS_DEVICES L"/Classes/video0", FILE_READ | FILE_WRITE);
    if (vid == NULL)
    {
        _wdprintf(L"console: " SYS_DEVICES L"/Classes/video0: %s\n", _wcserror(errno));
        return errno;
    }

    memset(&mode, 0, sizeof(mode));
    params.vid_setmode = mode;
    if (!FsRequestSync(vid, VID_SETMODE, &params, sizeof(params), NULL))
    {
        _wdprintf(L"console: failed to set video mode: %s\n", _wcserror(errno));
        HndClose(vid);
        return errno;
    }

    mode = params.vid_setmode;

    if (mode.flags & VIDEO_MODE_TEXT)
    {
        _wdprintf(L"console: text mode not supported\n");
        return 0;
    }

    if (mode.bitsPerPixel == 4)
    {
        vidmem = NULL;
        if (!AccelCreateSurface(&mode, vid, &surf))
        {
            _wdprintf(L"console: AccelCreateSurface failed\n");
            return errno;
        }
    }
    else
    {
        handle_t handle_vidmem;

        handle_vidmem = HndOpen(mode.framebuffer);
        if (handle_vidmem == 0)
        {
            _wdprintf(L"console: unable to open framebuffer %s\n", mode.framebuffer);
            return errno;
        }

        vidmem = VmmMapSharedArea(handle_vidmem, 0, 
            VM_MEM_USER | VM_MEM_READ | VM_MEM_WRITE);
        HndClose(handle_vidmem);

        if (!FramebufCreateSurface(&mode, vidmem, &surf))
        {
            _wdprintf(L"console: video mode not supported: %u bits per pixel\n", mode.bitsPerPixel);
            return errno;
        }
    }

    FontInit();
    font = FontLoad(font_name, 10 * 64, 
        mode.bitsPerPixel < 8 ? FB_FONT_MONO : FB_FONT_SMOOTH);
    if (font == NULL)
    {
        _wdprintf(L"console: failed to load font %s\n", font_name);
        return errno;
    }

    cookies[0] = (void*) 0x12345678;

    num_buffers = 1;

    LmuxInit(&lmux_consoles);
    for (i = 0; i < _countof(consoles); i++)
    {
        LmuxInit(&consoles[i].lmux);
        consoles[i].width = consoles[i].height = 0;
        consoles[i].fg_colour = consoles[i].default_fg_colour = 0xc0c0c0;
        consoles[i].bg_colour = consoles[i].default_bg_colour = 0x000000;
        consoles[i].cookie = cookies[0];
        consoles[i].buf_chars = NULL;
        consoles[i].font = font;
        FontGetMaxSize(consoles[i].font, 
            &consoles[i].char_width, &consoles[i].char_height);
    }

    num_consoles = _countof(consoles);
    ConTileBuffer(0);
    num_consoles = 0;

    current = consoles;

    for (i = 0; i < _countof(consoles); i++)
        ConClear(consoles + i);

    server = FsCreate(server_name, 0);

    ThrCreateThread(ConKeyboardThread, NULL, 16, L"ConKeyboardThread");
    ThrCreateThread(ConCursorThread, NULL, 16, L"ConCursorThread");

    ConDisplaySignonMessage();

    while ((client = PortAccept(server, FILE_READ | FILE_WRITE)))
    {
        if (num_consoles < _countof(consoles))
        {
            consoles[num_consoles].client = client;
            ThrCreateThread(ConClientThread, consoles + num_consoles, 15, L"ConClientThread");
            num_consoles++;
        }
        else
            HndClose(client);
    }

    for (i = 0; i < _countof(consoles); i++)
    {
        LmuxDelete(&consoles[i].lmux);
        free(consoles[i].buf_chars);
    }

    LmuxDelete(&lmux_consoles);
    FontDelete(font);
    HndClose(server);
    if (vidmem != NULL)
        VmmFree(vidmem);
    HndClose(vid);
    return EXIT_SUCCESS;
}
Example #17
0
File: dir.c Project: 1tgr/mobius
static void ShListDirectory(wchar_t *path, const wchar_t *spec, unsigned indent, 
                            bool do_recurse, bool is_full)
{
    handle_t search;
    file_info_t *entries;
    wchar_t *end;
    unsigned num_entries, alloc_entries, i, n, columns;
    size_t max_len, len;

    end = path + wcslen(path);
    if (end[-1] != '/')
    {
        wcscpy(end, L"/");
        end++;
    }
    //end++;

    //KeLeakBegin();
    search = FsOpenDir(path);

    if (search != NULL)
    {
        entries = malloc(sizeof(file_info_t));
        num_entries = alloc_entries = 0;
        max_len = 0;
        while (FsReadDir(search, &entries[num_entries].dirent, sizeof(dirent_t)))
        {
            len = wcslen(entries[num_entries].dirent.name) + 2;
            if (len > max_len)
                max_len = len;

            wcscpy(end, entries[num_entries].dirent.name);
            if (!FsQueryFile(path, 
                FILE_QUERY_STANDARD, 
                &entries[num_entries].standard, 
                sizeof(dirent_standard_t)))
            {
                _pwerror(path);
                continue;
            }

            num_entries++;
            if (num_entries >= alloc_entries)
            {
                alloc_entries = num_entries + 16;
                //wprintf(L"ShListDirectory: alloc_entries = %u\n", alloc_entries);
                entries = realloc(entries, sizeof(file_info_t) * (alloc_entries + 1));
            }
        }

        HndClose(search);

        qsort(entries, num_entries, sizeof(file_info_t), ShCompareDirent);
        if (max_len == 0)
            columns = 1;
        else
            columns = (sh_width - 2) / max_len - 1;
        for (i = n = 0; i < num_entries; i++)
        {
            if (do_recurse)
            {
                if (entries[i].standard.attributes & FILE_ATTR_DIRECTORY)
                {
                    printf("%*s+ %S\n", indent * 2, "", entries[i].dirent.name);                
                    ShListDirectory(path, spec, indent + 1, true, is_full);
                }
                else if (_wcsmatch(spec, entries[i].dirent.name) == 0)
                    printf("%*s  %S\n", indent * 2, "", entries[i].dirent.name);
            }
            else if (_wcsmatch(spec, entries[i].dirent.name) == 0)
            {
                if (is_full)
                {
                    if (entries[i].standard.attributes & FILE_ATTR_DIRECTORY)
                        printf("[Directory]\t");
                    else if (entries[i].standard.attributes & FILE_ATTR_DEVICE)
                        printf("   [Device]\t");
                    else
                        printf("%10lu\t", (uint32_t) entries[i].standard.length);

                    printf("\t%S", entries[i].dirent.name);

                    printf("%*s%S\n", 
                        20 - wcslen(entries[i].dirent.name) + 1, "", 
                        entries[i].standard.mimetype);
                }
                else
                {
                    if (entries[i].standard.attributes & FILE_ATTR_DIRECTORY)
                        printf("\x1b[1m");

                    printf("%S%*s", entries[i].dirent.name, 
                        max_len - wcslen(entries[i].dirent.name), "");

                    if (entries[i].standard.attributes & FILE_ATTR_DIRECTORY)
                        printf("\x1b[m");

                    if (n == columns)
                    {
                        printf("\n");
                        n = 0;
                    }
                    else
                        n++;
                }
            }
        }

        if (!do_recurse && !is_full && (n - 1) != columns)
            printf("\n");
        free(entries);
    }
    else
        _pwerror(path);

    //KeLeakEnd();
}