/* CURRDIR.C -- uses undocumented DOS to return pointer to
   current directory structure for a given drive */

#include <stdlib.h>
#include <dos.h>

#include "currdir.h"

typedef enum { UNKNOWN=-1, FALSE=0, TRUE=1 } OK;

CDS far *currdir(unsigned drive)
{
    /* statics to preserve state: only do init once */
    static BYTE far *dir = (BYTE far *) 0;
    static OK ok = UNKNOWN;
    static unsigned currdir_size;
    static BYTE lastdrv;

    if (ok == UNKNOWN)  /* only do init once */
    {
        unsigned drv_ofs, lastdrv_ofs;

        /* curr dir struct not available in DOS 1.x or 2.x */
        if ((ok = (_osmajor < 3) ? FALSE : TRUE) == FALSE)
            return (CDS far *) 0;

        /* compute offset of curr dir struct and LASTDRIVE in DOS
           list of lists, depending on DOS version */
        drv_ofs = (_osmajor == 3 && _osminor == 0) ? 0x17 : 0x16;
        lastdrv_ofs = (_osmajor == 3 && _osminor == 0)
                         ? 0x1b
                         : 0x21;

#if defined(__TURBOC) || (defined(_MSC_VER) && (_MSC_VER >= 600))       
#ifdef __TURBOC__
#define _asm    asm
#endif

        _asm    push si    /* needs to be preserved */

        /* get DOS list of lists into ES:BX */
        _asm    mov ah, 52h
        _asm    int 21h

        /* get LASTDRIVE byte */
        _asm    mov si, lastdrv_ofs
        _asm    mov ah, byte ptr es:[bx+si]
        _asm    mov lastdrv, ah

        /* get current directory structure */
        _asm    mov si, drv_ofs
        _asm    les bx, es:[bx+si]
        _asm    mov word ptr dir+2, es
        _asm    mov word ptr dir, bx

        _asm    pop si
#else           
{       // Microsoft C 5.1 -- no inline assembler available
        union REGS r;
        struct SREGS s;
        BYTE far *doslist;
        segread(&s);
        r.h.ah = 0x52;
        intdosx(&r, &r, &s);
        FP_SEG(doslist) = s.es;
        FP_OFF(doslist) = r.x.bx;
        lastdrv = doslist[lastdrv_ofs];
        dir = *((BYTE far * far *) (&doslist[drv_ofs]));
}
#endif

        /* OS/2 DOS box sets dir to FFFF:FFFF */
        if (dir == (BYTE far *) -1L) ok = FALSE;

        /* compute curr directory structure size */
        currdir_size = (_osmajor >= 4) ? 0x58 : 0x51;

    } /* end of static initializations */

    if (ok == FALSE)
        return (CDS far *) 0;

    if (drive >= lastdrv) /* is their drive < LASTDRIVE? */
        return (CDS far *) 0;

    /* return array entry corresponding to drive */
    return dir + (drive * currdir_size);
}
