/*  FREE command - Dec 1986 - Rev. 2 - for DeSmet C
Vincent Bly  Nanosoft  PO Box 409  Ft. Belvoir, VA  22060

***  Returns free disk space or free RAM memory  ***
*** Sets errorlevel if less than spec bytes free ***

Call from DOS or batch file:
FREE [N[:]] [MINREQ] [+]

Examples:
FREE		(displays free space on current drive)
FREE A		(displays free space on drive A)
FREE C: 	(displays free space on drive C)
FREE B: +	(displays free space on drive B & shows disk data)
FREE M		(displays free RAM memory)
FREE 50000	(displays free space on current drive & sets errorlevel
		 to 1 if less than 50,000 bytes are available )
FREE M 256000	(displays free RAM & sets errorlevel 1 if less than 256K free)
*/

#define DOS_FUNC(n)  _rax = ((n) << 8), _doint(0x21)

extern long atol();
extern unsigned _rax, _rbx, _rcx, _rdx, _rds, _res, _carryf;
long free_bytes, minval = 0L;	/* 0 => default minimum required memory */
unsigned topram, pspseg;	/* used in calculating free RAM memory */
int verbose = 0;		/* extra information flag, set by '+' */

main(argc,argv) /* Returns # of free bytes on drive N or the default drive,  */
int  argc;	/* or--if drive spec is "M"--returns free RAM memory.  If a  */
char *argv[];	/* numeric spec. is given, FREE will set the errorlevel to 1 */
{		/* if this amount of free space or memory is not available.  */
		/* If arg of + (plus sign) is included, detailed info given. */
		/* For minimum code size, bind with CSTDIO7.S (no floating   */
		/* point is actually used, so this reduces the size of the   */
		/* routines bound with printf()).   Also, bind with a stack  */
		/* size of 400h (bind free -s400). PUBLIC DOMAIN  V. T. Bly  */

    int  n, drive = 0;		   /*  0 => default (current) drive number */
    char c;

    for (n = argc - 1; n > 0; n--) { /** Scan command line arguments **/
	c = *argv[n];
	if (isdigit(c)) 		/* If argument is numeric, set */
	    minval = atol(argv[n]);	/* minimum required memory value */
	else if (c == '+')              /* set verbose if arg is '+' */
	    verbose = 1;
	else
	    drive = (c & 95) - '@';     /* else, calc. drive number */
    }
    if (drive == 13) {
	/*** CALCULATE FREE RAM MEMORY ***/
		  /* Free ram is computed from the value of the PSP segment  */
		  /* (which starts at the lowest available memory location)  */
		  /* & top of ram value (stored as the 2nd word in the PSP). */
		  /* Shift by 4 (* 16) to convert from paragraphs to bytes.  */
	pspseg = _showcs() - 0x10;		/* trace back to PSP segment */
	topram = _peek(2, pspseg) + (_peek(3, pspseg) << 8);/* top ram in PSP */
	free_bytes = ((long)topram - pspseg) << 4;/* actual memory available */
	puts("\nRAM memory: ");
    } else {
	/*** CALCULATE FREE DISK SPACE ***/
	_rdx = drive;
	DOS_FUNC(0x36); 		/* get disk free space */
	if (_rax == 0xffff) {
	    puts("\n** Invalid Drive **\n");
	    exit(2);
	}
	free_bytes = (long)_rbx * _rax * _rcx;
		/* (free = free clusters * sectors/cluster * bytes/sector) */
	n = _rax;   /* save it */
	if (drive == 0) {	    /* if default drive, calc drive number */
	    DOS_FUNC(0x19);
	    drive = (_rax & 0xff) + 1;
	}
	_rax = n;   /* restore it */
	printf("\nDrive %c: ", (char)drive + '@');
    }
	finish(drive);	    /* print output data & exit to DOS */

}


finish(drive) /* Finish printing free bytes and exit to DOS, giving an error */
int  drive;   /* if there are insufficient bytes to meet minimum required.   */
{	      /* If the verbose flag is set, display additional data */
    long val;

    if (verbose) {
	puts("\n");
	if (drive == 13) {
	    val = ((long)topram << 4) - 1L;
	    printf(" Top of RAM     = %7s [%05lx]\n", lngp(val), val);
	    val = (long)pspseg << 4;
	    printf(" Next free addr = %7s [%05lx]\n", lngp(val), val);
	    printf(" Free bytes     = %7s [%05lx]\n", lngp(free_bytes), free_bytes);
	} else {
	    printf(" Sectors/cluster = %10s [%04x]\n", lngp((long)_rax), _rax);
	    printf(" Bytes/sector    = %10s [%04x]\n", lngp((long)_rcx), _rcx);
	    val = (long)_rax * _rcx;
	    printf(" Bytes/cluster   = %10s [%04lx]\n", lngp(val), val);
	    val = (long)_rax * _rcx * _rdx;
	    printf(" Total clusters  = %10s [%04x]\n", lngp((long)_rdx), _rdx);
	    printf(" Free clusters   = %10s [%04x]\n", lngp((long)_rbx), _rbx);
	    printf(" Total bytes     = %10s [%05lx]\n", lngp(val), val);
	    printf(" Free bytes      = %10s [%05lx]\n", lngp(free_bytes), free_bytes);

	}
    } else {
	printf("%s bytes free\n", lngp(free_bytes));
    }
    exit(free_bytes < minval);
}


lngp(val)   /* Format long integer val for printing with commas */
long val;	    /* value to be formatted */
{
    static char fmtn[13];		/* holds formatted number */
    char buff[13];			/* temporary working buffer */
    int  d, i, j, l;

    sprintf(buff, "%ld", val);          /* first, format w/o commas */
    l = strlen(buff);
    d = l % 3;
    for (i = 0, j = 0; i < l; i++) {	/* copy into s w/commas inserted */
	fmtn[j + i] = buff[i];
	--d;
	if ((i != (l - 1)) && ((d % 3) == 0)) {
	    ++j;
	    fmtn[j + i] = ',';
	}
    }
    fmtn[j + i] = '\0';                 /* add terminating 0 */
    return(fmtn);			/* return pointer to fmtn[0] */
}
