/* 
DEVCON.C
revised: 14 May 1991
note: the do-while loop shown in the first and second printings of
      UNDOCUMENTED DOS will miss the last device driver in the chain!
      This has been fixed in this version, which uses a for(;;) loop.
*/

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

/* some device attribute bits */
#define CHAR_DEV    (1 << 15)
#define INT29       (1 << 4)
#define IS_CLOCK    (1 << 3)
#define IS_NUL      (1 << 2)

#pragma pack(1)

typedef unsigned char BYTE;

typedef struct DeviceDriver {
    struct DeviceDriver far *next;
    unsigned attr;
    unsigned strategy;
    unsigned intr;
    union {
        BYTE name[8];
        BYTE blk_cnt;
        } u;
    } DeviceDriver;

typedef struct {
    void far *dpb;
    void far *sft;
    DeviceDriver far *clock;
    DeviceDriver far *con;
    unsigned max_bytes;
    void far *disk_buff;
    void far *cds;
    void far *fcb;
    unsigned prot_fcb;
    unsigned char blk_dev;
    unsigned char lastdrv;
    DeviceDriver nul;   /* not a pointer */
    unsigned char join;
    // ...
    } ListOfLists;  // DOS 3.1+
        
void fail(char *s) { puts(s); exit(1); }

main(int argc, char *argv[])
{
    ListOfLists far *doslist;
    DeviceDriver far *dd;
    
    _asm {
        xor bx, bx
        mov es, bx
        mov ah, 52h
        int 21h
        mov doslist, bx
        mov doslist+2, es
        }

    if (! doslist)
        fail("INT 21h Function 52h not supported");
    if (_fmemcmp(doslist->nul.u.name, "NUL     ", 8) != 0)
        fail("NUL name wrong");
    if (! (doslist->nul.attr & IS_NUL))
        fail("NUL attr wrong");
    if (_fmemcmp(doslist->con->u.name, "CON     ", 8) != 0)
        fail("CON name wrong");
    if (! (doslist->con->attr & CHAR_DEV))
        fail("CON attr wrong");
    if (_fmemcmp(doslist->clock->u.name, "CLOCK$  ", 8) != 0)
        fail("CLOCK$ name wrong");
    if (! (doslist->clock->attr & IS_CLOCK))
        fail("CLOCK$ attr wrong");

    if (argv[1][0] == '-')
    {
        /* print out device chain */
        for (dd = &doslist->nul;;)
        {
            printf("%Fp\t", dd);
            if (dd->attr & CHAR_DEV)
                printf("%.8Fs\n", dd->u.name); 
            else
                printf("Block dev: %u unit(s)\n", dd->u.blk_cnt);
            if (FP_OFF(dd->next) == -1)
                break;
			dd = dd->next;
        } 
    }

    /* go back to first CON driver */
    dd = &doslist->nul;
    while (_fmemcmp(dd->u.name, "CON     ", 8) != 0)
        dd = dd->next;
    
    /* DOS List Of Lists holds separate ptr to latest CON driver */
    puts(dd == doslist->con ? "no new CON" : "new CON");
    
    return 0;
}
