/***************************************************
 *						   *
 *	chkfrag - check disk for fragmentation	   *
 *						   *
 ***************************************************/

#include <stdio.h>			    /* standard library 	*/
#include <dos.h>			    /* dos access and registers */
#include <malloc.h>			    /* memory allocation	*/
#include <stdlib.h>			    /* common lib modules	*/

#define UINT unsigned int		    /* unsigned integer type	*/
#define ULONG unsigned long		    /* unsigned long type	*/
#define NOT !				    /* logical not		*/
#define BOOT b_rec			    /* boot record shorthand	*/
#define FILES (_A_SYSTEM | _A_HIDDEN | _A_SUBDIR)   /* files type	*/
#define LABEL (_A_VOLID)			    /* label type	*/
#define CLEAR(s,c) strclr(s,c,sizeof(s))    /* string clear		*/

/*
 *  Globals
 */

char	huge *fat,			/* address of FAT		*/
	cdrive[66],			/* startup drive and path	*/
	fat_16; 			/* true if 16 bit FAT entries	*/

int	sections = 0,			/* file sections		*/
	secsize = 0 ,			/* sector size of drive 	*/
	frag = 0,			/* fragmented files		*/
	unfrag = 0,			/* unfragmented files		*/
	files = 0,			/* processed files		*/
	dirs = 0,			/* processed directories	*/
	list = 0;			/* list frag'd files switch     */

UINT	nclusters;			/* number of clusters		*/

/*
 *  formal declarations
 */

void	get_fat(int),			/* read FAT into memory 	*/
	cfexit(int),			/* exit routine 		*/
	check_frag(char *, UINT, int),	/* check if file/dir is frag'd  */
	dir_search(char *),		/* search directory		*/
	strclr(char *, int, int),	/* clear a string		*/
	check_unlinked();		/* check for unlinked clusters	*/
int	chkdrv(char);			/* check local, SUBST/ASSIGN	*/
char	*fname(char *, char*),		/* fcb filename to normal fname */
	*readlabel(char),		/* read the drive label 	*/
	*translate_name(char far *);	/* translate name		*/
UINT	next_cluster(UINT, int, int *); /* find the next cluster in FAT */

struct	fcb
    {
    char    hexff;			/* extended fcb first byte	*/
    char    extra[5];			/* extended fcb work area	*/
    char    attrib;			/* extended fcb attribute	*/
    char    drive;			/* fcb - drive			*/
    char    filename[8];		/* fcb - filename		*/
    char    ext[3];			/* fcb - extension		*/
    unsigned
    int     block;			/* fcb - block number		*/
    unsigned
    long    filesize;			/* fcb - file size		*/
    int     date;			/* fcb - file date		*/
    char    system[10]; 		/* fcb - reserved area		*/
    char    record;			/* fcb - current record 	*/
    unsigned
    long    rnd_recno;			/* fcb - random record number	*/
    } ;

/************************************************************************
 *									*
 *	mainline							*
 *									*
 ************************************************************************/

main(argc, argv)
int	argc;				/* count of arguments		*/
char	*argv[];			/* argument strings		*/
{
int	pf = 0, 			/* percent fragmented		*/
	ctc = 0,			/* return code chosen		*/
	dc = 0, 			/* drive chosen 		*/
	pe = 0, 			/* parm in error		*/
	rc,				/* return code			*/
	i, j,				/* loop counter, work		*/
	option = 0;			/* program option		*/
char	*p;				/* work pointer 		*/
static
char	drive[] = " :\\",               /* drive and path to check      */
	*rc_type[] =
	    { "Percentage",
	      "Number of Files",
	      "Number of Extra Segments" },

	*suggestion[] =
	    { "No fragmentation -- No action suggested",
	      "Little fragmentation -- No action suggested",
	      "Moderate fragmentation -- Defrag should be performed soon",
	      "Fragmentation critical -- Defrag or Backup/Format/Restore" },

	*errors[] =
	    { "Invalid drive specified",
	      "Cannot CHKFRAG a network drive",
	      "Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
	      "Must run with DOS 2.0 or greater" },

	*options[] =
	     { "/%", "/N", "/E", "/L" } ;


printf("CHKFRAG 1.0 (c) Ziff Communications Co.\n%s%c%s\n",
       "PC Magazine ", 254, " Bob Flanders & Michael Holmes\n");

getcwd(cdrive, sizeof(cdrive)); 	    /* get current drive/path	*/
*drive = *cdrive;			    /* ..setup default drive	*/

for (i = 1; i < argc; i++)		    /* check each argument	*/
    {
    strupr(p = argv[i]);		    /* uppercase argument	*/

    if (strlen(p) == 2 && p[1] == ':')      /* q. drive parm specified? */
	{
	*drive = *p;			    /* a. yes .. setup drive	*/
	dc++;				    /* .. show drive selected	*/
	}

     else
	{
	for (j = 0; strcmp(p, options[j])   /* search arguments 	*/
		    && (j < 4); j++);

	switch(j)			    /* based on argument	*/
	    {
	    case 0:			    /* /% option		*/
	    case 1:			    /* /N option		*/
	    case 2:			    /* /E option		*/

		option = j;		    /* set up the option value	*/

		ctc++;			    /* increment code type count*/
		break;			    /* exit switch		*/

	    case 3:			    /* /L switch		*/
		list++; 		    /* .. show listing wanted	*/
		break;

	    case 4:			    /* error			*/
		pe = j; 		    /* argument in error	*/
		break;			    /* .. error 		*/
	    }
	}
    }

if (pe || (ctc > 1) || (list>1) || (dc > 1))/* q. any error?		*/
    {					    /* a. yes .. handle error	*/
    printf("\n\tformat\tCHKFRAG  [d:] [/%% | /N | /E] [/L]\n\n");
    printf("\twhere\td: is the drive to check for fragmentation\n");
    printf("\t\t/%% sets errorlevel as a percentage\n");
    printf("\t\t/N sets errorlevel to number of fragmented files (max 254)\n");
    printf("\t\t/E sets errorlevel number of extra sections (max 254)\n");
    printf("\t\t/L causes fragmented files to be listed\n");
    cfexit(255);
    }

if (i = chkdrv(*drive)) 		    /* check drive, version err */
    {
    printf("Error: %s", errors[--i]);       /* display any error        */
    cfexit(255);			    /* tell the batch job	*/
    }

get_fat(*drive - 'A');                      /* read FAT into memory     */
dir_search(drive);			    /* search for files 	*/
check_unlinked();			    /* check unlinked clusters	*/

if (files + dirs)			    /* q. any files and dirs?	*/
    pf = (frag * 100) / (files + dirs);     /* a. yes .. % files frag'd */

if (!pf && frag)			    /* q. something frag'd      */
    pf = 1;				    /* a. yes .. show non-zero	*/

printf("\n%d Files, %d Directories,\n",     /* report to user           */
	    files, dirs);
printf("%d Unfragmented, %d Fragmented, %d Extra Sections\n",
	    unfrag, frag, sections);
printf("%d%% of files are fragmented\n\n", pf);

switch(option)				    /* return w/errorlevel	*/
    {
    case 0:				    /* percentage return	*/
	rc = pf;
	break;

    case 1:				    /* files return		*/
	rc = frag > 254 ? 254 : frag;
	break;

    case 2:				    /* extra sections return	*/
	rc = sections > 254 ? 254 : sections;
    }

if (pf == 0)				    /* q. no fragments? 	*/
    i = 0;				    /* a. yes .. tell 'em       */

 else if (pf < 11)			    /* q. little fragmentation? */
    i = 1;				    /* a. yes .. set index	*/

 else if (pf < 76)			    /* q. moderate fragm'tion   */
    i = 2;				    /* a. yes .. setup msg	*/

 else
    i = 3;				    /* ..push the button, Jim	*/

printf("%s%s\nCHKFRAG finished, Return code %d\n\n%s%s\n",
	"Return type chosen: ", rc_type[option], rc,
	"Suggestion:\n     ", suggestion[i]);

cfexit(rc);				    /* return w/errorlevel	*/
}

/************************************************************************
 *									*
 *	get_fat -- read boot record and fat into memory 		*
 *									*
 ************************************************************************/

void	get_fat(drv)
int	drv;				/* drive number 		*/
{
union	REGS r; 			/* work registers		*/
struct	SREGS s;			/* ..and work segment regs	*/
struct	bootrec
	{
	char jmp[3],			/* jump instruction		*/
	     oem[8];			/* OEM name			*/
	UINT bytes;			/* bytes per sector		*/
	char cluster;			/* sectors per cluster		*/
	UINT res_sectors;		/* reserved sectors		*/
	char fats;			/* number of fats		*/
	UINT roots,			/* number of root dir entries	*/
	     sectors;			/* total sectors		*/
	char media;			/* media descriptor block	*/
	UINT fatsize,			/* sectors per fat		*/
	     tracksize, 		/* sectors per track		*/
	     heads,			/* number of heads		*/
	     hidden;			/* hidden sectors		*/
	} far *b_rec;			/* boot record definition	*/

char *nomem = "Not enough memory for processing\n";

r.h.ah = 0x36;				    /* ah = get freespace	*/
r.h.dl = drv + 1;			    /* get drive		*/
int86(0x21, &r, &r);			    /* r.x.cx = bytes/sector	*/

if ((BOOT = (struct bootrec far *) malloc(r.x.cx)) == NULL)
    {					    /* q. no memory?		*/
    printf(nomem);			    /* a. yes .. give		*/
    cfexit(255);			    /* ..error msg/exit 	*/
    }

r.h.al = drv;				    /* al = drive number	*/
r.x.cx = 1;				    /* cx = number of sectors	*/
r.x.dx = 0;				    /* dx = starting sector	*/
r.x.bx = FP_OFF(BOOT);			    /* bx = offset of buffer	*/
s.ds   = FP_SEG(BOOT);			    /* ds = segment of buffer	*/
int86x(0x25, &r, &r, &s);		    /* read boot sector 	*/

if (r.x.cflag)				    /* q. error reading disk?	*/
    {
    printf("Error reading boot record\n");  /* a. yes .. give error msg */
    cfexit(255);			    /* ..and return to DOS	*/
    }

if ((fat = (char huge *) halloc((long) BOOT->fatsize * (long) BOOT->bytes, 1))
			     == (char huge *) NULL)
    {					    /* q. no memory?		*/
    printf(nomem);			    /* a. yes .. give		*/
    cfexit(255);			    /* ..error msg/exit 	*/
    }

fat_16 = (BOOT->sectors / BOOT->cluster) > 4087;/* set if 16bit FAT tbl */

r.h.al = drv;				    /* al = drive number	*/
r.x.cx = BOOT->fatsize; 		    /* cx = number of sectors	*/
r.x.dx = BOOT->res_sectors;		    /* dx = starting sector	*/
r.x.bx = FP_OFF(fat);			    /* bx = offset of buffer	*/
s.ds   = FP_SEG(fat);			    /* ds = segment of buffer	*/
int86x(0x25, &r, &r, &s);		    /* read boot sector 	*/

if (r.x.cflag)				    /* q. error reading disk?	*/
    {
    printf("%02.2x %02.2x Error reading FAT\n",
		  r.h.ah, r.x.di);	    /* a. yes .. give error msg */
    cfexit(255);			    /* ..and return to DOS	*/
    }

nclusters = (BOOT->sectors - (BOOT->res_sectors
		+ (BOOT->fatsize * BOOT->fats)
		+ ((BOOT->roots * 32) / BOOT->bytes)))
		/ BOOT->cluster;

printf("Drive %c:%s %u Sectors, %u Clusters, %u Clustersize\n",
	drv + 'A', readlabel(drv + 'A'), BOOT->sectors, nclusters,
	BOOT->cluster * BOOT->bytes);

printf("\nChecking disk structure ..");

}


/************************************************************************
 *									*
 *	check_frag -- check a file/directory for fragmentation		*
 *									*
 ************************************************************************/

void	check_frag(s, n, dflag)
char	*s;				/* file/directory name		*/
UINT	n;				/* starting cluster number	*/
int	dflag;				/* directory flag		*/
{
UINT	i, j;				/* working storage		*/
int	flag = 0,			/* flag for frag'd file         */
	rc;				/* error return code		*/


for(; i = next_cluster(n, 1, &rc); n = i)   /* walk down the chain	*/
    {
    if (i == 1) 			    /* q. invalid cluster?	*/
	{
	printf("\n\t%s -- %s%s\n%s\n",        /* a. yes .. give err msg   */
		s, rc ? "Invalid cluster detected"
		      : "File cross-linked",
		", Run aborted",
		"\n\t** Please run CHKDSK **");
	cfexit(255);			    /* ..and exit w/error code	*/
	}

    if ((n + 1) != i)			    /* q. non-contiguous area?	*/
	{
	flag++; 			    /* show fragmented file	*/

	if (i > n)			    /* q. possibly bad cluster? */
	    {
	    for (j = n + 1;		    /* check for bad spots	*/
		 next_cluster(j, 0, &rc) == 0xfff7 && j < i;
		 j++);

	    if (j == i) 		    /* q. was entire area bad?	*/
		flag--; 		    /* a. yes .. don't report   */

	     else
		sections++;		    /* incr files sections count*/
	    }

	 else
	    sections++; 		    /* incr files sections count*/
	}
    }

if (flag)				    /* q. fragmented file	*/
    {
    if (NOT frag && list)		    /* q. first frag file?	*/
	printf("\nFragmented Files/Directories:\n");

    if (list)				    /* q. list frag'd files?    */
	printf("%s%s\n",                    /* a. yes .. give it to them*/
		dflag ? "DIR> " : "     ", s);

    frag++;				    /* accumulate frag'd count  */
    }

 else
    unfrag++;				    /* else total unfrag'd files*/

}


/************************************************************************
 *									*
 *	next_cluster -- return next cluster number from FAT		*
 *									*
 ************************************************************************/

UINT	next_cluster(n, x, rc)
UINT	n;				/* current cluster number	*/
int	x,				/* flag, 1 = reset FAT entry	*/
	*rc;				/* error return code		*/
{
ULONG	e;				/* entry number in FAT		*/
UINT	huge *p,			/* pointer for 16 bit FAT entry */
	mask1, mask2;			/* mask for and'ing and or'ing  */
int	flag;				/* shift/and flag		*/


*rc = 0;				    /* clear return code	*/

if (! (e = n))				    /* q. invalid cluster nbr	*/
    return(0);				    /* a. yes .. rtn EOF	*/

if (fat_16)				    /* q. 16 bit FAT entries?	*/
    {
    p = (UINT *) &fat[0];		    /* a. yes .. get FAT addr	*/
    n = p[e];				    /* retrieve next entry	*/

    if (NOT n)				    /* q. unallocated cluster?	*/
	{
	n = 1;				    /* a. yes .. error condition*/
	*rc = 1;			    /* set return code		*/
	}

    if (x)				    /* q. need to reset entry?	*/
	p[e] = 1;			    /* a. yes .. show processed */

    if (n >= 0xfff0 && n != 0xfff7)	    /* q. reserved and not bad	*/
	n = 0;				    /* a. yes .. show EOF	*/
    }

 else
    {
    e = (n << 1) + n;			    /* cluster number * 3	*/
    flag = e & 1;			    /* need to do shift later?	*/
    e >>= 1;				    /* cluster number * 1.5	*/
    n = *(UINT *) &fat[e];		    /* get next cluster 	*/

    if (flag)				    /* q. need to do shift?	*/
	{
	n >>= 4;			    /* a. yes .. shift by 4 bits*/
	mask1 = 0x000f; 		    /* mask to clear upper bits */
	mask2 = 0x0010; 		    /* ..and footprint mask	*/
	}

     else
	{
	n &= 0xfff;			    /* else .. strip upper bits */
	mask1 = 0xf000; 		    /* mask to clear lower bits */
	mask2 = 0x0001; 		    /* ..and footprint mask	*/
	}

    if (NOT n)				    /* q. unallocated cluster?	*/
	{
	n = 1;				    /* a. yes .. error condition*/
	*rc = 1;			    /* set return code		*/
	}

    if (x)				    /* q. need to reset entry?	*/
	{
	*(UINT *) &fat[e] &= mask1;	    /* a. yes .. 'and' off bits */
	*(UINT *) &fat[e] |= mask2;	    /* ..and put down footprint */
	}

    if (n >= 0xff0)			    /* q. EOF/reserved range?	*/
	if (n == 0xff7) 		    /* q. bad cluster?		*/
	    n = 0xfff7; 		    /* a. yes .. show bad one	*/
	 else
	    n = 0;			    /* else .. show EOF 	*/
    }

return(n);

}

/************************************************************************
 *									*
 *	check_unlinked -- check for unlinked clusters			*
 *									*
 ************************************************************************/


void	check_unlinked()
{
int	rc;				/* error return code		*/
UINT	i,				/* loop counter 		*/
	j;				/* work return cluster nbr	*/


for (i = 2; i < nclusters; i++) 	    /* check thru entire FAT	*/
    {
    if ((j = next_cluster(i, 0, &rc)) != 0  /* q. unallocated cluster?	*/
		&& j != 1 && j != 0xfff7)   /* ..or used/bad cluster?	*/
	{
	printf("\nLost clusters detected, %s%s",/* a. no .. give msg    */
		"Run aborted\n",
		"\t** Please run CHKDSK **\n");
	cfexit(255);				/* ..and exit w/error	*/
	}
    }
}


/************************************************************************
 *									*
 *	dir_search -- recursively search all files & subdirectories	*
 *									*
 ************************************************************************/

void	dir_search(base_dir)
char	*base_dir;			/* base subdirectory to search	*/
{
int	oldds,				/* old dta segment		*/
	oldda;				/* old dta address		*/
char	pass,				/* pass number			*/
	work_dir[65],			/* work directory		*/
	first_done;			/* find first done		*/
struct	fcb find_work;			/* fcb work area		*/

/*
 *  The following areas are STATIC .. not allocated on recursion
 */

static
struct	SREGS	s;			/* segment registers		*/

static
union	REGS	r;			/* other registers		*/

static
char	far *cftmp;			/* work pointer 		*/

static
int	rc;				/* work return code		*/

static
union
    {
    char    dtabuff[128];		/* dta area			*/

    struct				/* Disk transfer area layout	*/
	{
	char dta1[6];			/* first part of dta		*/
	char attrib;			/* attribute byte		*/
	char drive;			/* drive			*/
	char filename[8];		/* filename			*/
	char ext[3];			/* extension			*/
	char d_attrib;			/* directory attribute		*/
	char dta2[10];			/* more reserved space		*/
	unsigned
	int  d_time;			/* directory time		*/
	unsigned
	int  d_date;			/* directory date		*/
	unsigned
	int  d_cluster; 		/* first cluster		*/
	unsigned
	long d_filesize;		/* size of file 		*/
	} dta;
    } dta;

/*
 *  End of static area
 */

r.h.ah = 0x2f;				    /* ah = get dta		*/
int86x(0x21, &r, &r, &s);		    /* .. ask DOS		*/

oldds = s.es;				    /* save old DTA segment	*/
oldda = r.x.bx; 			    /* .. and offset		*/

cftmp = (char far *) &dta;		    /* get current fcb address	*/

r.h.ah = 0x1a;				    /* ah = set DTA		*/
s.ds = FP_SEG(cftmp);			    /* ds -> DTA segment	*/
r.x.dx = FP_OFF(cftmp); 		    /* ds:dx -> DTA		*/
int86x(0x21, &r, &r, &s);		    /* setup new DTA		*/

if (strcmp(base_dir, translate_name(base_dir))) /* q. JOIN'd?           */
    return;					/* a. yes .. skip it	*/

chdir(base_dir);			    /* get the base directory	*/

for(first_done=pass=0;;)		    /* look through current dir */
    {
    if (first_done == 0)		    /* q. find first done?	*/
	{				    /* a. no .. do it		*/

	if (base_dir[1] == ':')             /* q. disk specified?       */
	    find_work.drive =		    /* a. yes .. set fcb drive	*/
		    ((base_dir[0] & 0xdf) - 'A') + 1;
	 else
	    find_work.drive = 0;	    /* else use default 	*/

	find_work.hexff = 0xff; 	    /* extended fcb		*/
	CLEAR(find_work.extra, 0);	    /* set extra area		*/
	CLEAR(find_work.filename, '?');     /* .. and file name         */
	CLEAR(find_work.ext, '?');          /* .. and extension         */
	find_work.attrib = FILES;	    /* set up attribute to find */

	r.h.ah = 0x11;			    /* ah = find first		*/
	cftmp = (char far *) &find_work;    /* get pointer to work fcb	*/
	s.ds = FP_SEG(cftmp);		    /* ds -> segment of fcb	*/
	r.x.dx = FP_OFF(cftmp); 	    /* ds:dx -> offset		*/
	int86x(0x21, &r, &r, &s);	    /* .. find first		*/

	rc = r.h.al;			    /* get return code		*/

	first_done = 1; 		    /* first find done		*/
	}

     else
	{
	r.h.ah = 0x12;			    /* ah = find next		*/
	cftmp = (char far *) &find_work;    /* get pointer to work fcb	*/
	s.ds = FP_SEG(cftmp);		    /* ds -> segment of fcb	*/
	r.x.dx = FP_OFF(cftmp); 	    /* ds:dx -> offset		*/
	int86x(0x21, &r, &r, &s);	    /* .. find first		*/

	rc = r.h.al;			    /* get return code		*/
	}

    strcpy(work_dir, base_dir); 	    /* get current base 	*/

    if (work_dir[strlen(work_dir)-1] != '\\')   /* if needed ..         */
	 strcat(work_dir, "\\");                /* .. add a backslash   */

    strcat(work_dir,			    /* .. add the name		*/
	 fname(dta.dta.filename, dta.dta.ext));

    if (pass)				    /* q. second pass?		*/
	{
	if (rc) 			    /* q. more files found?	*/
	    break;			    /* a. no .. exit		*/

	if (!(dta.dta.d_attrib & _A_SUBDIR)	/* q. directory?	*/
	      || (dta.dta.filename[0] == '.'))  /* .. or a dot dir?     */
	      continue; 			/* a. get next entry	*/

	dirs++; 			    /* accumulate dir count	*/
	dir_search(work_dir);		    /* recursively call ourself */
	}

     else				    /* first pass processing	*/
	{
	if (rc) 			    /* q. anything found?	*/
	    {				    /* a. no .. 		*/
	    first_done = 0;		    /* re-execute find-first	*/
	    pass++;			    /* go to next pass		*/
	    continue;			    /* .. continue processing	*/
	    }

	if (dta.dta.filename[0] == '.')     /* q. dot directory?        */
	    continue;			    /* a. yes .. skip it	*/

	if (!(dta.dta.d_attrib & _A_SUBDIR))	/* q. a file?		*/
	    files++;				/* a. yes .. count them */

	check_frag(work_dir,		    /* check for frag'd file    */
		dta.dta.d_cluster,
		dta.dta.d_attrib & _A_SUBDIR);
	}
    }

r.h.ah = 0x1a;				    /* ah = set DTA		*/
s.ds = oldds;				    /* ds -> DTA segment	*/
r.x.dx = oldda; 			    /* ds:dx -> DTA		*/
int86x(0x21, &r, &r, &s);		    /* setup new DTA		*/

}


/************************************************************************
 *									*
 *	fname -- build a normalized filename from an FCB		*
 *									*
 ************************************************************************/

char	*fname(filename, ext)
char	*filename;			/* filename with trailing blanks*/
char	*ext;				/* extension			*/
{
int	i;				/* loop control 		*/
char	*p;				/* work pointer 		*/
static
char	fwork[13];			/* returned work area		*/


p = fwork;				    /* initialize string pointer*/

for (i = 0; (i < 8) && (*filename != ' '); i++) /* move fname w/o blanks*/
    *p++ = *filename++;

if (*ext != ' ')                            /* q. extension blank?      */
    {
    *p++ = '.';                             /* a. no .. add the dot     */

    for (i = 0; (i < 3) && (*ext != ' '); i++)  /* add ext w/o blanks   */
	*p++ = *ext++;
    }

*p = 0; 				    /* terminate string w/null	*/

return(fwork);				    /* return string to caller	*/

}


/************************************************************************
 *									*
 *	strclr -- clear an area to a value				*
 *									*
 ************************************************************************/

void	strclr(s, c, n)
char	*s;				/* area to initialize		*/
int	c,				/* value to use 		*/
	n;				/* length of area to clear	*/
{

while(n--)				    /* initialize whole area	*/
    *s++ = c;				    /* ..to value specified	*/

}


/************************************************************************
 *									*
 *	translate_name --  translate a DOS name 			*
 *									*
 ************************************************************************/

char	*translate_name(name)
char	far *name;			/* name to translate		*/
{
static
char	translate_area[65],		/* work/return area		*/
	far *sp;			/* work pointer 		*/

union	REGS r; 			/* work registers		*/
struct	SREGS s;			/* ..and work segment regs	*/


r.h.ah = 0x60;				/* ah = translate		*/

sp = (char far *) name; 		/* set up a pointer ..		*/
r.x.si = FP_OFF(sp);			/* set pointer to input name	*/
s.ds = FP_SEG(sp);			/* .. and segment		*/

sp = (char far *) translate_area;	/* set up a pointer ..		*/
r.x.di = FP_OFF(sp);			/* set pointer to output area	*/
s.es = FP_SEG(sp);			/* .. and segment		*/
int86x(0x21, &r, &r, &s);		/* translate the name		*/

if (r.x.cflag)				/* if bad name ..		*/
    return((char far *) NULL);		/* .. return error		*/

 else
    return(translate_area);		/* return xlated name		*/
}


/************************************************************************
 *									*
 *	chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd     *
 *									*
 ************************************************************************/

int	chkdrv(c)
char	c;				/* drive to check		*/
{
union	REGS r; 			/* work registers		*/
struct	SREGS s;			/* ..and work segment regs	*/

static
char	wdrv[] = " :\\";                /* work area for drive name     */


if (_osmajor < 2)			    /* q. pre-DOS 2.00? 	*/
    return(4);				    /* a. yes .. can't run it   */

if (_osmajor >= 3 && _osminor >= 1)	    /* q. DOS 3.1 or higher?	*/
    {
    r.x.ax = 0x4409;			    /* ah = ioctl, local test	*/
    r.h.bl = (c - 'A') + 1;                 /* bl = drive to test       */
    int86(0x21, &r, &r);		    /* test drive		*/

    if (r.x.cflag)			    /* q. bad drive?		*/
	return(1);			    /* a. yes .. error		*/

    if (r.x.dx & 0x1000)		    /* q. remote?		*/
	return(2);			    /* a. yes .. error		*/

    wdrv[0] = c;			    /* set up name		*/

    if (strcmp(wdrv, translate_name(wdrv))) /* q. SUBST or ASSIGNED?	*/
	return(3);			    /* a. yes .. return error	*/
    }

return(0);				    /* return ok		*/

}


/************************************************************************
 *									*
 *	Read drive label, if available					*
 *									*
 ************************************************************************/

char	*readlabel(c)
char	c;				/* drive to check		*/
{
char	*p, *q; 			/* work pointers		*/
struct	find_t f;			/* structure for directory entry*/

static
char	work_dir[13] = { " :\\*.*" } ;  /* directory to check           */


work_dir[0] = c;			    /* setup for find first	*/

if (_dos_findfirst(work_dir, LABEL, &f))    /* q. error on label get?	*/
    work_dir[0] = 0;			    /* a. yes .. then no label	*/

 else
    {
    for(p = work_dir, q = f.name; *q; q++)  /* copy label w/o middle .	*/
	if (*q != '.')                      /* q. is this char a dot?   */
	    *p++ = *q;			    /* a. no .. copy it 	*/

    *p = 0;				    /* terminate string 	*/
    }

return(work_dir);			    /* ..and return label string*/

}

/************************************************************************
 *									*
 *	cfexit() - return to DOS after resetting dir, dir		*
 *									*
 ************************************************************************/

void	cfexit(rc)
int	rc;				/* return code to exit with	*/

{
int	i;				/* work variable		*/

_dos_setdrive((*cdrive - 'A')+1, &i);       /* reset the default drive  */
chdir(cdrive);				    /* .. and directory 	*/

exit(rc);				    /* .. and return to DOS	*/
}


