shithub: drawcpu

ref: cc44473e8c095bb48bfcf68b982239bddfb97e07
dir: /kern/devlfd-win32.c/

View raw version
#include <windows.h>
#include "u.h"
#include "lib.h"
#include "dat.h"
#include "fns.h"
#include "error.h"

Chan*
lfdchan(void *fd)
{
    Chan *c;
    
    c = newchan();
    c->type = devno('L');
    c->aux = fd;
    c->path = newpath("/fd");
    c->mode = ORDWR;
    c->qid.type = QTFILE;
    c->qid.path = (uintptr)fd;  /* Use handle as qid.path (index for std handles) */
    c->qid.vers = 0;
    c->dev = 0;
    c->offset = 0;
    return c;
}

/*
 * Initialize or retrieve a handle, supporting stdin/stdout/stderr and additional handles
 */
int
lfdfd(int fd)
{
    Fgrp *fg = up->fgrp;
    HANDLE h;
    Chan *c;

    /* Get handle for standard fds or validate custom handle */
    switch(fd) {
    case 0: h = GetStdHandle(STD_INPUT_HANDLE); break;
    case 1: h = GetStdHandle(STD_OUTPUT_HANDLE); break;
    case 2: h = GetStdHandle(STD_ERROR_HANDLE); break;
    default:
        h = (HANDLE)(uintptr)fd;  /* Assume fd is a valid HANDLE */
        if(h == INVALID_HANDLE_VALUE || h == NULL)
            return -1;
        break;
    }
    if(h == INVALID_HANDLE_VALUE || h == NULL)
        return -1;

    lock(&fg->ref.lk);
    if(fd >= fg->nfd) {
        int newnfd = fd + DELTAFD;
        Chan **newfd = smalloc(newnfd * sizeof(Chan*));
        if(fg->nfd > 0)
            memmove(newfd, fg->fd, fg->nfd * sizeof(Chan*));
        free(fg->fd);
        fg->fd = newfd;
        fg->nfd = newnfd;
    }
    if(fg->fd[fd] == nil) {
        c = lfdchan((void*)h);
        fg->fd[fd] = c;
        if(fd > fg->maxfd)
            fg->maxfd = fd;
    } else {
        c = fg->fd[fd];
    }
    unlock(&fg->ref.lk);
    return newfd(c);
}

static int
fdgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
{
    Fgrp *fg = up->fgrp;
    int fd;

    if(s == DEVDOTDOT) {
        devdir(c, c->qid, "#L", 0, eve, DMDIR|0775, dp);
        return 1;
    }

    lock(&fg->ref.lk);
    if(name) {
        /* Look up by name (e.g., "0", "1", "2", etc.) */
        char *end;
        fd = strtol(name, &end, 10);
        if(*end != '\0' || fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
            unlock(&fg->ref.lk);
            return -1;  /* Invalid or closed handle */
        }
    } else {
        /* Generate by index, starting with 0, 1, 2, then others up to maxfd */
        for(fd = 0, s = s; fd <= fg->maxfd && s >= 0; fd++) {
            if(fd >= fg->nfd || fg->fd[fd] == nil)
                continue;
            if(s-- == 0)
                break;
        }
        if(fd > fg->maxfd || fd >= fg->nfd || fg->fd[fd] == nil) {
            unlock(&fg->ref.lk);
            return -1;
        }
    }

    sprint(up->genbuf, "%d", fd);
    devdir(c, fg->fd[fd]->qid, up->genbuf, 0, eve, 0666, dp);
    unlock(&fg->ref.lk);
    return 1;
}

static Chan*
lfdattach(char *spec)
{
    Chan *c;
    Fgrp *fg = up->fgrp;

    if(spec && *spec)
        error(Ebadarg);

    /* Ensure standard handles are initialized */
    lock(&fg->ref.lk);
    if(fg->nfd < 3) {
        int newnfd = 3 + DELTAFD;
        Chan **newfd = smalloc(newnfd * sizeof(Chan*));
        if(fg->nfd > 0)
            memmove(newfd, fg->fd, fg->nfd * sizeof(Chan*));
        free(fg->fd);
        fg->fd = newfd;
        fg->nfd = newnfd;
    }
    if(fg->fd[0] == nil)
        fg->fd[0] = lfdchan((void*)GetStdHandle(STD_INPUT_HANDLE));
    if(fg->fd[1] == nil)
        fg->fd[1] = lfdchan((void*)GetStdHandle(STD_OUTPUT_HANDLE));
    if(fg->fd[2] == nil)
        fg->fd[2] = lfdchan((void*)GetStdHandle(STD_ERROR_HANDLE));
    if(fg->maxfd < 2)
        fg->maxfd = 2;
    unlock(&fg->ref.lk);

    c = devattach('L', spec);
    c->qid.type = QTDIR;
    c->qid.path = 0;
    return c;
}

static Walkqid*
lfdwalk(Chan *c, Chan *nc, char **name, int nname)
{
    return devwalk(c, nc, name, nname, nil, 0, fdgen);
}

static int
lfdstat(Chan *c, uchar *db, int n)
{
    return devstat(c, db, n, nil, 0, fdgen);
}

static Chan*
lfdopen(Chan *c, int omode)
{
    Fgrp *fg = up->fgrp;
    int fd;

    if(c->qid.type & QTDIR) {
        if(omode != OREAD)
            error(Eperm);
        c->mode = openmode(omode);
        c->flag |= COPEN;
        c->offset = 0;
        return c;
    }

    lock(&fg->ref.lk);
    fd = (int)c->qid.path;  /* qid.path stores the fd index */
    if(fd < 0 || fd >= fg->nfd || fg->fd[fd] == nil) {
        unlock(&fg->ref.lk);
        error(Enonexist);
    }

    /* Enforce permissions based on fd */
    switch(fd) {
    case 0:  /* stdin */
        if(omode != OREAD)
            error(Eperm);
        break;
    case 1:  /* stdout */
    case 2:  /* stderr */
        if(omode != OWRITE)
            error(Eperm);
        break;
    default:
        if((omode & (OREAD|OWRITE)) == 0)
            error(Eperm);
        break;
    }

    c->mode = openmode(omode);
    c->flag |= COPEN;
    c->offset = 0;
    c->aux = fg->fd[fd]->aux;  /* Use the HANDLE from the stored Chan */
    unlock(&fg->ref.lk);
    return c;
}

static void
lfdclose(Chan *c)
{
    Fgrp *fg = up->fgrp;
    int fd;

    if(c->qid.type & QTDIR)
        return;

    lock(&fg->ref.lk);
    fd = (int)c->qid.path;
    if(fd >= 0 && fd < fg->nfd && fg->fd[fd] != nil) {
        CloseHandle((HANDLE)fg->fd[fd]->aux);
        fg->fd[fd] = nil;
        if(fd == fg->maxfd) {
            while(fg->maxfd > 0 && fg->fd[fg->maxfd] == nil)
                fg->maxfd--;
        }
    }
    unlock(&fg->ref.lk);
}

static long
lfdread(Chan *c, void *buf, long n, vlong off)
{
    DWORD r;

    USED(off);  /* Can't pread on pipes */
    if(c->qid.type & QTDIR)
        return devdirread(c, buf, n, nil, 0, fdgen);

    if(!ReadFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
        oserror();
    return r;
}

static long
lfdwrite(Chan *c, void *buf, long n, vlong off)
{
    DWORD r;

    USED(off);  /* Can't pwrite on pipes */
    if(!WriteFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
        oserror();
    return r;
}

Dev lfddevtab = {
    'L',
    "lfd",
    
    devreset,
    devinit,
    devshutdown,
    lfdattach,
    lfdwalk,
    lfdstat,
    lfdopen,
    devcreate,
    lfdclose,
    lfdread,
    devbread,
    lfdwrite,
    devbwrite,
    devremove,
    devwstat,
};