Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 68K/1.0X SOURCES/v102a/cutils/stat.c
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

2086 lines
62 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************/
/* */
/* S T A T C O M M A N D */
/* ----------------------- */
/* */
/* Common module for MP/M 2.0, MP/M-86 2.0, CP/M-80 2.2, CP/M-86 1.1 */
/* and Portable CP/M implementation (P-CP/M) */
/* */
/****************************************************************************/
/****************************************************************************/
/* */
/* Copyright(C) 1975, 1976, 1977, 1978, 1979, 1980, 1981 */
/* Digital Research */
/* Box 579 */
/* Pacific Grove, Ca */
/* 93950 */
/* */
/* Revised: */
/* 20 Jan 80 by Thomas Rolander */
/* 29 July 81 by Doug Huskey (for MP/M 2.0) */
/* 02 Sept 81 (for MP/M-86) */
/* 14 Nov 81 by Doug Huskey (for CP/M-86) */
/* 21 Sept 82 by Zilog (translate to C) */
/* */
/* */
/* Modified 10/30/78 to fix the space computation */
/* Modified 01/28/79 to remove despool dependencies */
/* Modified 07/26/79 to operate under CP/M 2.0 */
/* Modified 12/19/83 to fix C operator precedence bug */
/* Modified 12/21/83 to fix "value used before set" bug in display */
/* */
/****************************************************************************/
#include "copyrt.lit"
/****************************************************************************/
/* */
/* Sign-on message to indicate release - PLEASE UPDATE!!! */
/* */
/****************************************************************************/
char signonmsg[] = "Zilog CP/M-Z8000 STAT v1.0C 01/03/84\n";
/****************************************************************************/
/* */
/* Note: In an attempt to have a common source for all DRI O.S. */
/* shared utilities the version is tested and a number of conditionally*/
/* executed statements have been added for compatibility between MP/M 2*/
/* and CP/M. */
/* */
/****************************************************************************/
/****************************************************************************/
/* Generation instructions */
/****************************************************************************/
/* To be provided later for Z8000 version */
/****************************************************************************/
/* */
/* H O W I T W O R K S A N D W H A T I T D O E S */
/* --------------------------------------------------------- */
/* */
/* */
/* STAT gives information about disk drives, files and devices. It */
/* can also change the attributes of files and disk drives, and the */
/* assignment of logical devices to physical devices. STAT knows */
/* little about the extended file control information and disk */
/* labels available under MP/M and CP/M 3.0, but is still required */
/* under these operating systems because many of its functions are */
/* not duplicated by other, more recent, utilities such as SET and */
/* SHOW. */
/* */
/* The command takes several forms: */
/* */
/* STAT VAL: */
/* Display valid forms of the STAT command (ie a summary of */
/* the information set out below) */
/* */
/* STAT */
/* Display status of all checked drives (that is, drives */
/* which have been explicitly or implicitly referenced since */
/* the last warm boot), saying how much free space is on */
/* each and whether it is read/write or read-only */
/* */
/* STAT d: */
/* Display status as above for a specified disk drive d: */
/* */
/* STAT DSK: */
/* STAT d:DSK: */
/* Same as first two cases above, except that considerably */
/* more information is displayed: capacity in 128 byte records,*/
/* capacity in Kbytes, total number of directory entries, */
/* number used, number of records allocated per directory */
/* entry (physical extent), number of records per block and */
/* per track and the number of reserved tracks */
/* */
/* STAT d:=RO */
/* Set a specified disk drive to read-only status. It retains */
/* this status until the next warm boot or until... */
/* */
/* STAT d:=RW */
/* Set a specified disk drive to read-write status. Cancels */
/* a previous STAT d:=RO */
/* */
/* STAT USR: */
/* STAT d:USR: */
/* Lists the users who own files on the named disk drive. If */
/* no drive is named, the current drive is assumed */
/* */
/* STAT DEV: */
/* Display the current logical to physical device assignments */
/* (Not applicable to MP/M) */
/* */
/* STAT log:=phy: */
/* Assign logical device log: to physical device phy:, for */
/* example STAT CON:=TTY: (Not applicable to MP/M) */
/* */
/* STAT filespec */
/* STAT filespec SIZE */
/* Display detailed information about the file or files */
/* referenced by filespec (filespec may be ambiguous or */
/* unambiguous.) Information is number of physical records, */
/* physical size in Kbytes, number of FCB's (directory */
/* entries) used for this file and attributes. If the word */
/* SIZE follows filespec, the logical record count is also */
/* printed. This may be greater than the physical record */
/* count if the file is sparce (not all logical records used.) */
/* */
/* STAT filespec RO | RW | SYS | DIR */
/* Sets or clears attributes of specified file or files. RO */
/* (read-only) and RW (read/write) are mutually exclusive, as */
/* are SYS (system file - does not normally appear in */
/* directory listings) and DIR (directory file - can be seen */
/* in directory listings.) Thus, no more than two attributes */
/* may be specified on the same command line. The order in */
/* which attributes are specified is not important. */
/* */
/* */
/* To find out how this program implements these functions, it is best */
/* to start at the end of this listing, which defines the top-level */
/* function, and work backwards through progressively lower-level */
/* functions as and when it becomes necessary. */
/* */
/* For historical reasons, much of the communication between functions */
/* uses global variables rather than parameters. The main things to */
/* remember are that all global variables are defined at the top of */
/* the program before any function definitions, and that all globals */
/* NEED to be global because they are referenced by more than one */
/* function. A cross reference listing of some sort is useful in */
/* tracking interdependencies caused by global variable usage. */
/* */
/* Dominic Dunlop, Zilog Inc. 10/20/82*/
/* */
/****************************************************************************/
/****************************************************************************/
/* */
/* E X T E R N A L A N D B D O S I N T E R F A C E */
/* ----------------------------------------------------- */
/* */
/****************************************************************************/
#include "portab.h" /* Portable C definitions */
#include "bdos.h" /* CP/M, MP/M call def's */
#include "basepage.h" /* CP/M basepage structure */
/****************************************************************************/
/* External data and functions */
/****************************************************************************/
extern char *sbrk(); /* Memory allocation */
extern char *_break; /* Top of allocated memory */
struct fcbtab *fcb; /* Ptr to 1st basepage FCB */
char *buff; /* Ptr to basepage DMA buff */
/****************************************************************************/
/* Version dependencies */
/****************************************************************************/
/* Version number fields */
#define CPM 0x0000 /* Vanilla flavor CP/M */
#define MPM 0x1000 /* MP/M */
#define PCPM 0x2000 /* Portable CP/M */
#define ONE_X 0x00 /* Version 1.x */
#define TWO_X 0x20 /* Version 2.x */
#define THREE_X 0x30 /* Version 3.x */
#define VOID_GET_DPB(x) (((x) & 0xff00) == PCPM)
#define HAS_GET_DFS(x) ((((x) & 0xff00) != CPM) || (((x) & 0xf0) >= THREE_X))
#define HAS_GSET_SCB(x) (((x) & 0xf0) >= THREE_X)
/****************************************************************************/
/* Definitions */
/****************************************************************************/
/* Absolute value (beware */
/* side effects!) */
#define abs(a) (((a) >= 0) ? (a) : -(a))
#define SAFETY 0x400 /* Stack expansion space */
/* after memory allocation*/
#define WIDTH 72 /* Default console width */
#define SPKSHF 3 /* log base 2 of sectors/K */
#define DELETED 0xe5 /* Deleted file mark */
#define T_SIZE 4 /* Size of command token */
#define NSIZE (sizeof fcb->fname) /* Size of fname */
#define FNAM (NSIZE + sizeof fcb->ftype)/* " + size of ftype */
#define ROFILE ftype[0] /* Read-only file */
#define SYSFILE ftype[1] /* "System" (invisible) file*/
#define ARCHIV ftype[2] /* Archived file */
#define HAS_XFCB s1 /* Has extended FCB */
#define ATTRB1 fname[0] /* Attribute F1' */
#define ATTRB2 fname[1] /* Attribute F2' */
#define ATTRB3 fname[2] /* Attribute F3' */
#define ATTRB4 fname[3] /* Attribute F4' */
/****************************************************************************/
/* Miscellaneous global data */
/****************************************************************************/
char cdisk; /* Current disk */
char token[T_SIZE]; /* Parsed cmd line token */
char user_code; /* Current user code */
BOOLEAN sizeset = FALSE; /* TRUE if printing size */
BOOLEAN set_attribute = FALSE; /* TRUE if setting attribute*/
BOOLEAN error_free = TRUE; /* No duplicate block error */
BOOLEAN word_blks; /* FCB block addr's 16 bits */
int kpb; /* Kbytes per disk block */
int scase1, scase2; /* Attributes required */
UWORD ver; /* OS version number */
UWORD dcnt; /* Current directory code */
UWORD rodisk; /* Read only disk vector */
UWORD nfcbs = 0; /* Total number of FCB's */
/****************************************************************************/
/* Data associated with disk allocation */
/* */
/* NOTE: The local copy of the disk parameter block is used only in those */
/* CP/M implementations where BDOS function 31 returns a copy of the */
/* DPB. Other implementations access the BIOS' own copy of the DPB. */
/* */
/* The length of the undimensioned array ALLOC is fixed at runtime */
/* according to the number of blocks on the disk being examined */
/****************************************************************************/
struct dpbs dpb; /* Local copy of DPB */
struct dpbs *dpba = &dpb; /* Ptr to disk parameter blk*/
BYTE *alloc; /* Disk map, one bit / block*/
/****************************************************************************/
/* Data associated with collecting and sorting filenames */
/* */
/* NOTE: The lengths of the two undimensioned arrays are fixed at runtime */
/* according to the amount of free memory available */
/****************************************************************************/
UWORD fcbn; /* FCB's collected so far */
UWORD fcbmax; /* Size of the arrays below */
struct fcbhalf /* First 16 bytes of FCB: */
{ /* used in sorting files */
BYTE drive; /* and gathering data */
BYTE fname[8];
BYTE ftype[3];
BYTE extent;
BYTE s1;
UWORD kcnt; /* Kilobyte count ) Not from*/
UWORD rcnt; /* Record count ) FCB */
} *fcbs; /* Base for collecting FCB's*/
struct fcbhalf **finx; /* Array of FCB addresses */
struct fcbhalf *fcbsa; /* Index into FCB's */
struct fcbtab *bfcba; /* Ptr to directory entry */
struct block_no /* FCB pointer to disk block*/
{ /* May be either byte or */
BYTE first; /* 8080-ordered word */
BYTE second;
};
/****************************************************************************/
/* Messages to suit (almost) every occasion */
/* (all these messages are used more than once - messages used only once */
/* are coded in line) */
/****************************************************************************/
char drivename[] = " Drive ";
char readonly[] = "Read Only (RO)";
char readwrite[] = "Read Write (RW)";
char entries[] = " Directory Entries";
char filename[] = "d:filename.typ";
char use[] = "Use: STAT ";
char invalid[] = "Invalid Assignment";
char set_to[] = " set to ";
char record_msg[] = "128 Byte Record";
char sattrib[] = "[RO] [RW] [SYS] or [DIR]";
/****************************************************************************/
/* Valid first tokens on command line */
/****************************************************************************/
char devl[] = "CON:AXI:AXO:LST:DEV:VAL:USR:DSK:";
#define L_SIZE ((sizeof devl - 1) / T_SIZE)/* Number of entries */
#define OPT_USR 7 /* Positions (starting at 1)*/
#define OPT_DSK 8 /* of some options */
/****************************************************************************/
/* Valid attributes for files, disks */
/****************************************************************************/
char attribl[] = "RO RW SIZESYS DIR ";
#define A_SIZE ((sizeof attribl - 1) / T_SIZE)/* Number of entries */
#define OPT_RO 1 /* Positions of attributes */
#define OPT_RW 2 /* NOTE: do not change this */
#define OPT_SIZE 3 /* order: error checking */
#define OPT_SYS 4 /* depends on it */
#define OPT_DIR 5
/****************************************************************************/
/* Valid names for physical devices */
/****************************************************************************/
char devr[] =
"TTY:CRT:BAT:UC1:TTY:PTR:UR1:UR2:TTY:PTP:UP1:UP2:TTY:CRT:LPT:UL1:";
#define P_SIZE ((sizeof devr - 1) / T_SIZE)/* Number of entries */
/****************************************************************************/
/* */
/* L O W _ L E V E L F U N C T I O N S */
/* ------------------------------------- */
/* */
/****************************************************************************/
/********************************/
/* */
/* B L A N K S */
/* */
/********************************/
VOID /* Print b blanks on the */
blanks(b) /* console */
int b;
{
while (b--)
_conout(' ');
}
/********************************/
/* */
/* P R I N T X */
/* */
/********************************/
VOID /* Print a null-terminated */
printx(a) /* string on the console */
char *a;
{
while (*a)
_conout(*a++);
}
/********************************/
/* */
/* N E W _ L N */
/* */
/********************************/
VOID /* Send carriage-return, */
new_ln() /* line-feed to console */
{
_conout('\n');
_conout('\r');
}
/********************************/
/* */
/* T E S T _ K B D _ E S C */
/* */
/********************************/
VOID /* If a character has been */
test_kbd_esc() /* entered at the console,*/
{ /* discard it and exit */
if (_constat()) /* from STAT at once */
{
_conin(); /* Read & discard character */
new_ln();
printx("* Aborted *");
_exit(1);
}
}
/********************************/
/* */
/* C R L F */
/* */
/********************************/
VOID /* Send CR - LF sequence */
crlf() /* then check if user */
{ /* wants to abort (has */
new_ln(); /* hit any key) */
test_kbd_esc();
}
/********************************/
/* */
/* P R I N T */
/* */
/********************************/
VOID /* Print a null-terminated */
print(a) /* string on a new line */
char *a;
{
crlf();
printx(a);
}
/********************************/
/* */
/* C O L U M N S */
/* */
/********************************/
int /* Returns the number of */
columns() /* print columns on the */
{ /* console (gives default */
static struct scbpb colpb = /* if not BDOS 3) */
{
0x1a, /* (Gives offest of con_w) */
GET
};
if (HAS_GSET_SCB(ver))
#ifdef HILO /* _gset_scb returns a word */
return (_gset_scb(&colpb) >> 8);/* We want more significant */
#else /* byte on HILO machines, */
return (_gset_scb(&colpb)); /* less significant on */
#endif /* LOHI */
else
return (WIDTH);
}
/********************************/
/* */
/* S E L E C T */
/* */
/********************************/
VOID /* Select the disk d, making*/
select(d) /* it the current disk */
int d;
{
rodisk = _get_ro(); /* Get current Read-Only vec*/
cdisk = d;
_sel_disk(d);
}
/********************************/
/* */
/* S E T _ K P B */
/* */
/********************************/
VOID /* Calculate the size of a */
set_kpb() /* disk block in kilobytes*/
{ /* Also decide whether disk */
/* block addresses are 8 */
/* or 16 bits long */
/****************************************************************************/
/* */
/* We want kpb (kbytes per block) so that each time we find */
/* a block address we can add kpb k to the kilobyte accumulator */
/* for file size. We derive kpb from bls - the base 2 logarithm of the */
/* number of 128-byte records per block - as follows: */
/* */
/* BLS RECS/BLK K/BLK BLS - 3 */
/* 3 8 1 0 */
/* 4 16 2 1 */
/* 5 32 4 2 */
/* 6 64 8 3 */
/* 7 128 16 4 */
/* */
/****************************************************************************/
if (VOID_GET_DPB(ver)) /* Get DPB (just how depends*/
{ /* on CP/M implementation)*/
_get_dpb(&dpb);
}
else
{
dpba = (struct dpbs *) _get_dpa();
}
kpb = 1 << (dpba->bls - SPKSHF); /* Calculate K per block */
word_blks = (dpba->mxa > 255);
}
/********************************/
/* */
/* S E L E C T _ D I S K */
/* */
/********************************/
VOID /* Select a disk and */
select_disk(d) /* calculate how many */
int d; /* kbytes/block it has */
{
select(d);
set_kpb();
}
/********************************/
/* */
/* C O U N T */
/* */
/********************************/
UWORD /* Returns the number of */
count() /* KBYTES remaining on */
{ /* current disk */
register UWORD k, all_blks;
register char *all_vec, *all_end;
LONG maxall;
if (HAS_GET_DFS(ver)) /* BDOS may do most of the */
{ /* work for us */
maxall = 0;
_setdma(&maxall);
_get_dfs(cdisk);
return(maxall >> SPKSHF); /* Convert from records to */
/* K (3 places) */
}
/****************************/
/* */
/* BEWARE! _get_alloc() */
/* does not work well on */
/* systems with bank-switch,*/
/* segmented and/or protec- */
/* ted memory. Such systems*/
/* MUST use _get_dfs() above*/
/* */
/****************************/
all_vec = (char *) _get_alloc(); /* Find where allocation vec*/
all_end = &all_vec[dpba->mxa / 8]; /* starts and ends */
for (all_blks = 0; all_vec <= all_end; all_vec++)
{ /* Count up number of bits */
k = *all_vec; /* set in vector (use */
do /* incrementing pointer */
{ /* because quicker than */
all_blks += k & 1; /* incrementing index) */
} while (k >>= 1); /* At end, we have count of */
} /* allocated blocks */
if ((maxall = dpba->mxa) <= all_blks) /* If disk is full, return 0*/
return (0);
return (kpb * (maxall - all_blks + 1)); /* Not full: multiply blocks*/
} /* free by kbytes/block */
/********************************/
/* */
/* F I L L */
/* */
/********************************/
VOID /* Fill the string s with */
fill(s,f,c) /* the character f for c */
register char *s; /* positions */
register int f;
register UWORD c;
{
while (c--)
*s++ = f;
}
/****************************************************************************/
/* */
/* M E M O R Y A L L O C A T I O N */
/* --------------------------------- */
/* */
/* A word about how memory is allocated (a picture is worth a thousand,*/
/* so it is rumored): */
/* */
/* +-------------------------------+ <--- Address zero */
/* | | */
/* | S T A T | Only this area is allocated */
/* | | when STAT is initially */
/* | C O D E , D A T A, B S S | loaded by CP/M (STAT is */
/* | | very approx 8k in length) */
/* | | */
/* +-------------------------------+ <--- alloc points here */
/* | | */
/* | DISK ALLOCATION MAP | Allocated by all_map() */
/* | | */
/* +-------------------------------+ <--- finx points here */ /* | | */
/* | ARRAY OF POINTERS INTO fcbs | Allocated by all_fcb */
/* | | */
/* +-------------------------------+ <--- fcbs points here */
/* | | */
/* | ARRAY OF FILE CONTROL BLOCKS | Allocated by all_fcb */
/* | | */
/* +-------------------------------+ */
/* | | */
/* | UNALLOCATED MEMORY | */
/* */
/* STAT only grows beyond its load size if asked to examine files. */
/* The disk allocation map is typically very short. The array of */
/* pointers and the array of file control blocks take up between them */
/* the rest of available memory or enough to be able to accomodate the */
/* maximum number of directory entries possible on the disk under */
/* scrutiny, whichever is smaller. This typically means that, taken */
/* together, they total about 60% of the length of a disk track. */
/* Note that only 18 bytes (not 32 or 36) are stored for each file */
/* (not for each extent.) */
/* */
/****************************************************************************/
/********************************/
/* */
/* A L L _ M A P */
/* */
/********************************/
char * /* Allocate memory to hold */
all_map() /* disk allocation map */
{ /* Delivers the address of */
register char *addr; /* the map */
/* NOTE: must be called */
/* BEFORE all_fcb */
/* Disk has mxa blocks. */
/* Take a bit for each */
if ((int) (addr = sbrk(dpba->mxa / 8 + 1) ) == -1)
{
print("Insufficient Memory For Allocation Map");
_exit(1);
}
fill(addr, 0, dpba->mxa / 8); /* Clear space out */
return (addr);
}
/********************************/
/* */
/* A L L _ F C B */
/* */
/********************************/
UWORD /* Allocate memory to hold */
all_fcb() /* FCB's and FCB pointers */
{
register UWORD dimension; /* Sets pointers to base of */
/* each array. Returns */
/* no. of array elements */
/* How much room between */
/* start of free memory */
/* and stack end (leave */
/* a safety margin)? */
dimension = ((UWORD) _base->freelen - SAFETY) /
(sizeof *finx + sizeof (struct fcbhalf));
if (dimension == 0)
{
print("Insufficient Memory for FCB's");
_exit(1);
}
dimension = (dpba->dmx + 1 < dimension ) ?/* We actually only need */
dpba->dmx + 1 : dimension;/* enough for maximum no. */
/* of directory entries */
/* Allocate the space */
finx = (struct fcbhalf **) sbrk((sizeof *finx) * dimension);
fcbs = fcbsa = (struct fcbhalf *)
sbrk((sizeof (struct fcbhalf)) * dimension);
/* Clear it out */
fill((char *) finx, 0, dimension *
(sizeof *finx + sizeof (struct fcbhalf)));
return (dimension);
}
/****************************************************************************/
/* */
/* C O M M A N D L I N E S C A N N I N G */
/* ----------------------------------------- */
/* */
/****************************************************************************/
/********************************/
/* */
/* S C A N */
/* */
/********************************/
VOID /* Put the next input value */
scan() /* into the token accum- */
{ /* ulator */
static int ibp = 1; /* Input buffer pointer */
int b, scandex;
while (buff[ibp++] == ' '); /* Skip leading spaces */
if (buff[--ibp] == '[') /* Back up to first char */
ibp++ ; /* which is not ' ' or '['*/
scandex = 0; /* Initialize accum index */
fill(token, ' ', T_SIZE); /* & clear accumulator */
while ((b = buff[ibp]) > 1) /* Get char: exit if 0 or 1 */
{
switch (b) /* Does character terminate */
{ /* the token? */
case ' ':
case ',':
case ':':
case '[':
case '=':
buff[ibp] = 1; /* Yes: set termination */
break; /* condition */
default: /* No (unless it's a control*/
if (b < ' ') /* character) */
buff[ibp] = 1;
else
ibp++ ; /* Point to next char */
}
switch (b) /* Add char to accumulator */
{ /* unless it's one of */
case '/': /* these */
case '_':
case ']':
case ',':
break;
default: /* Put next character of */
if (scandex < T_SIZE) /* token (which my be no */
token[scandex] = b;/*longer than 4 chars) */
scandex++; /* into accumulator */
}
}
if (b != 0) /* At end of command line? */
ibp++; /* No: bump pointer */
}
/********************************/
/* */
/* P A R S E _ A S S I G N */
/* */
/********************************/
BOOLEAN /* Parse an assignment into */
parse_assign() /* the accumulator */
{
scan(); /* Get a token */
if (token[0] != '=') /* Is it '='? */
return (FALSE); /* No: not an assignment */
scan(); /* Yes: get token to be */
return (TRUE); /* assigned */
}
/********************************/
/* */
/* P A R S E _ N E X T */
/* */
/********************************/
BOOLEAN /* Parse next item into */
parse_next() /* accumulator */
{
scan();
if (token[0] == ' ') /* Was it just a delimiter? */
{
scan(); /* Yes: try again */
if (token[0] == ' ') /* Another delimiter (or */
return (FALSE); /* line end): give up */
}
return (TRUE);
}
/********************************/
/* */
/* M A T C H */
/* */
/********************************/
int /* Try to match 4-character */
match(va, vl) /* token in accumulator to*/
register char *va; /* entry in table of vl */
int vl; /* entries at va */
{ /* Return index into table */
/* (> 0) if match found */
register int j, k, sync; /* else 0 */
BOOLEAN found;
j = 0; /* j indexes devices table */
for (sync = 1; sync <= vl; sync++) /* sync counts devices tried*/
{
found = TRUE; /* Be optimistic! */
for (k = 0; k < T_SIZE; ) /* Compare characters */
{
if ((va[j] == ' ') /* Don't attempt to match */
&& found) /* trailing blanks on */
break; /* table entry - match OK */
if (va[j++] != token[k++])/* Make comparison */
found = FALSE; /* It failed. Stay in inner*/
} /* loop to skip to next */
/* possible value */
if (found) /* Success! */
return (sync);
}
return (0); /* Failure */
}
/****************************************************************************/
/* */
/* D I S P L A Y F U N C T I O N S */
/* --------------------------------- */
/* */
/****************************************************************************/
/********************************/
/* */
/* P D E C I M A L */
/* */
/********************************/
VOID /* Print long unsigned value*/
pdecimal(v, prec, zerosup) /* to set precision */
/* 1 = 1 place, 10 = 2 */
/* places etc.) */
UWORD v; /* Value to print */
UWORD prec; /* Precision */
BOOLEAN zerosup; /* Zero suppression flag */
{
int d; /* Current decimal digit */
while (prec != 0)
{
d = v / prec; /* Get next digit */
v %= prec; /* Get remainder back to v */
if (((prec /= 10) != 0) /* Is this a supressed */
&& zerosup) { /* leading zero? */
if (d == 0)
_conout(' '); /* Yes: print space */
else
{ /* No: print digit */
zerosup = FALSE;
_conout('0' + d);
}
}
else _conout('0' + d);
}
}
/********************************/
/* */
/* P _ L O N G */
/* */
/********************************/
VOID /* Print an unsigned long */
p_long(value) /* value to seven digit */
LONG value; /* precision, with commas */
{ /* every three digits */
UWORD thous[4]; /* 3-digit chunks of value */
register int j;
register int zerosup;
zerosup = TRUE;
for (j = sizeof thous / sizeof (UWORD); --j >= 0;)
/* Break the number up into */
{ /* 3 digit chunks (most */
thous[j] = value % 1000; /* significant thous[0]) */
value /= 1000;
}
for (j = 0; j < (sizeof thous / sizeof (UWORD)); j++)
/* Print out chunks, most */
{ /* significant first */
switch(j) /* Handle special cases (all*/
{ /* cases are special!) */
case 0:
break; /* Billions: don't print */
case 1: /* Millions: just one digit */
if (thous[j] == 0) /* or spaces if zero */
printx(" ");
else
{
pdecimal(thous[j], 1, zerosup);
_conout(',');
zerosup = FALSE;
}
break;
case ((sizeof thous / sizeof (UWORD)) - 1):
/* Hundreds, tens, units: */
pdecimal(thous[j], 100, zerosup);
/* always print */
zerosup = FALSE;
break;
default: /* The rest: print zero as */
if (thous[j]) /* spaces, follow digits */
{ /* with comma */
pdecimal(thous[j], 100, zerosup);
_conout(',');
zerosup = FALSE;
}
else
printx(" ");
}
}
}
/********************************/
/* */
/* P _ U N L */
/* */
/********************************/
VOID /* Print unsigned long value*/
p_unl(value) /* on a new line followed */
LONG value; /* by ": " */
{
crlf();
p_long(value);
printx(": ");
}
/********************************/
/* */
/* S H O W _ D V */
/* S H O W _ D R I V E */
/* */
/********************************/
VOID /* Display name (A: - P:) */
show_dv() /* of current drive */
{
_conout(cdisk + 'A');
_conout(':');
}
VOID /* Same as above, followed */
show_drive() /* by space */
{
show_dv();
_conout(' ');
}
/********************************/
/* */
/* S H O W _ U S R */
/* */
/********************************/
VOID /* Display current user */
show_usr(user) /* number */
char user;
{
printx("User :");
pdecimal((UWORD) user, 100, TRUE);
}
/********************************/
/* */
/* D R I V E S T A T U S */
/* */
/********************************/
VOID /* Display status of current*/
drivestatus() /* drive */
{
LONG space;
print(" ");
show_drive();
printx("Drive Characteristics"); /* 128 byte records */
space = (LONG) (dpba->mxa + 1) * kpb;
p_unl(space * 8);
printx(record_msg);
printx(" Capacity");
p_unl(space); /* = Kbytes */
printx("Kilobyte Drive Capacity");
p_unl((LONG) dpba->dmx + 1); /* Directory slots */
printx("32 Byte");
printx(entries);
p_unl((LONG) dpba->cks * 4); /* Slots checked to find if */
printx("Checked"); /* disk has been changed */
printx(entries);
p_unl(((LONG) dpba->exm + 1) * 128); /* Records allocated / slot */
printx(record_msg);
printx("s / Directory Entry");
p_unl((LONG) 1 << dpba->bls); /* Records / block */
printx(record_msg);
printx("s / Block");
p_unl((LONG) dpba->spt); /* Records / track */
printx(record_msg);
printx("s / Track");
p_unl((LONG) dpba->ofs); /* Reserved tracks */
printx("Reserved Tracks");
crlf();
}
/********************************/
/* */
/* U S E R S T A T U S */
/* */
/********************************/
VOID /* Display active user no */
userstatus() /* and user no's which */
{ /* own files on current */
register UWORD i; /* drive */
BOOLEAN user[16];
crlf(); /* Show current user & drive*/
show_drive();
printx("Active ");
show_usr(user_code);
crlf();
show_drive();
printx("Active Files:"); /* Find out who owns files */
for (i = 0 ; i < sizeof user / sizeof (BOOLEAN);
user[i++] = FALSE); /* (Assume nobody at start) */
_setdma(buff);
dcnt = _srch_1st("?"); /* Find first FCB on disk */
while ( dcnt != 255 ) /* While more FCB's */
{ /* This expression gets user*/
/* no of current file from*/
/* directory buffer */
if ((i = ((struct fcbtab *) &buff[(dcnt & 3) * 32])->drive &
0xff) != DELETED) /* Not deleted entry */
user[i & 0x0f] = TRUE; /* User has at least 1 file */
dcnt = _srch_next(); /* Find next FCB */
}
/* Print users with files */
for (i = 0; i < sizeof user / sizeof (BOOLEAN); i++)
if (user[i])
pdecimal(i, 100, TRUE);
crlf();
}
/********************************/
/* */
/* D I S K S T A T U S */
/* */
/********************************/
VOID /* Display status of logged */
diskstatus() /* in disk drives */
{
int d;
register UWORD login; /* *UNSIGNED* login vector */
/* (ensure logical shift)*/
login = _ret_login(); /* Which disks logged in? */
d = 0;
do /* While more logged drives */
{
if (login & 1) /* Bit zero shows this */
{ /* drive is logged */
select_disk(d);
drivestatus(); /* Tell user about it */
}
d++ ; /* Try next drive */
} while (login >>= 1);
}
/********************************/
/* */
/* P R N A M E */
/* */
/********************************/
VOID
prname(a) /* Print the device name at */
register char *a; /* a. ':' terminates name*/
{
do
_conout(*a);
while (*a++ != ':');
}
/********************************/
/* */
/* D E V S T A T U S */
/* */
/********************************/
VOID /* Print logical - physical */
devstatus() /* device mapping (from */
{ /* iobyte) */
register UWORD iobyte; /* Iobyte needs unsigned var*/
register int j, k;
iobyte = _get_iob();
j = 0; /* j indexes phys dev group */
for (k = 0; k < 4; k++) /* Iobyte maps four logical */
{ /* devices (2 bits each) */
prname(&devl[k * 4]); /* Display logical dev name */
printx(" is "); /* Each maps to one of four */
/* physical devices */
prname(&devr[((iobyte & 3) * 4) + j]);
j += 16; /* Index next phys group */
iobyte >>= 2; /* & next logical device */
crlf();
}
}
/********************************/
/* */
/* V A L U E S */
/* */
/********************************/
VOID /* Tell the user what STAT */
values() /* can do */
{
register int j, k;
printx("STAT 2.2");
crlf();
print("File Status : ");
printx(filename);
printx(" [SIZE]");
print("Read Only Disk: d:=RO");
print("Set Attribute : ");
printx(filename);
printx(sattrib);
print("Disk Status : DSK: d:DSK:");
print("User Status : USR: d:USR:");
print("Iobyte Value : DEV:");
print("Iobyte Assign :");
for (j = 0; j < 4; j++) /* Print four lines, each */
{ /* showing one logical */
crlf(); /* device and four phys- */
prname(&devl[j * 4]); /* ical devices */
printx(" =");
for (k = 0; k <= 12; k += 4)
{
_conout(' ');
prname(&devr[(j * 16) + k]);
}
}
crlf();
}
/********************************/
/* */
/* P R C O U N T */
/* */
/********************************/
VOID /* Print the amount of space*/
prcount() /* remaining on the */
{ /* current disk */
LONG free; /* The no of free sectors */
free = 0;
if (HAS_GET_DFS(ver)) /* BDOS can return free */
{ /* space into curent DMA */
_setdma(&free); /* buffer */
_get_dfs(cdisk); /* Covert from record count */
free >>= SPKSHF; /* to kbytes (8 secs/k) */
}
else /* Not BDOS 3: we must */
{ /* get the info from DPB */
free = count();
}
p_long(free);
_conout('k');
}
/********************************/
/* */
/* P R A L L O C */
/* */
/********************************/
VOID /* Print allocation for the */
pralloc() /* current disk */
{
crlf();
show_drive(); /* Is current disk read-only*/
printx(((rodisk >> cdisk) & 1) ? "RO" : "RW");
printx(", Free Space: ");
prcount();
}
/********************************/
/* */
/* P R S T A T U S */
/* */
/********************************/
VOID /* Print the status of the */
prstatus() /* disk system */
{
register UWORD login;
register int d;
login = _ret_login(); /* Login vector set */
d = 0; /* Start on drive A */
while (login) /* While more drives */
{
if (login & 1) /* This disk is logged */
{
select_disk(d); /* Find out about it */
pralloc(); /* Tell the user about it */
login -= 1; /* clear the bit */
}
login >>= 1; /* Next disk to login lsb */
d++;
}
crlf();
}
/********************************/
/* */
/* D O T S */
/* */
/********************************/
VOID /* Output a line of i dots */
dots(i)
register int i;
{
crlf();
while (i--)
_conout('.');
}
/********************************/
/* */
/* P R I N T F N */
/* */
/********************************/
VOID /* Print current file name */
printfn(a) /* (pointed to by a) */
struct fcbtab *a;
{
register int k;
show_dv(); /* Drive preceeds file name */
for (k = 0; k < FNAM; k++) /* Print name in form */
{ /* "NNNNNNNN.EEE" */
if (k == NSIZE)
_conout('.');
_conout(a->fname[k] & 0x7f);
}
}
/****************************************************************************/
/* */
/* F I L E H A N D L I N G */
/* ------------------------- */
/* */
/****************************************************************************/
/********************************/
/* */
/* A T T R I B U T E */
/* */
/********************************/
/* Return TRUE if the file */
/* described by fcbsa has */
/* attribute a set */
/* NOTE: this is a macro */
#define attribute(a) (fcbsa->a & 0x80)
/********************************/
/* */
/* A L L O C A T E */
/* */
/********************************/
BOOLEAN /* Make an allocation vector*/
allocate(block_ptr) /* and check for duplicate*/
struct block_no *block_ptr; /* blocks in directory */
{ /* Return TRUE if allocation*/
/* consitent (no duplic- */
/* ates), FALSE otherwise */
/* Builds an allocation map,*/
/* one bit per disk block */
/* in memory starting at */
/* alloc, setting bits as */
/* it finds blocks alloc- */
/* ated */
UWORD block, vbyte, amask;
if (word_blks) /* Block addresses are 16 */
{ /* bits long */
#ifdef HILO /* Processor is Z8000, 68K..*/
block = ((UWORD) block_ptr->second << 8) +
( (UWORD) block_ptr->first & 0xff) ;
#else /* It's 8080, 8086, PDP11...*/
block = *(UWORD *) block_ptr;
#endif
}
else /* 8-bit block addresses */
block = (block_ptr->first) & 0xff; /* to prevent sign-ext */
vbyte = block / 8; /* Index into alloc vector */
amask = 1 << (block % 8); /* Bit in vector byte */
if (amask & alloc[vbyte]) /* Block already allocated? */
{ /* Yes */
if (error_free) /* Disk previously innocent?*/
{ /* Yes: show its guilt */
error_free = FALSE;
print("Bad Directory on ");
show_dv();
print("Space Allocation Conflict:");
}
crlf(); /* Tell user which file is */
show_usr(bfcba->drive); /* corrupt (drive field */
blanks(8); /* in FCB holds user #) */
printfn(bfcba);
return (FALSE);
}
alloc[vbyte] |= amask; /* Block not previously used*/
return (TRUE); /* mark it allocated */
}
/********************************/
/* */
/* N A M E _ D I F F */
/* */
/********************************/
int
name_diff(a ,b) /* Check for matching file */
register struct fcbtab *a; /* names. Returns -1 if */
register struct fcbhalf *b; /* name of file a lexico- */
{ /* graphically before that*/
register int i, c_a, c_b; /* of b, 0 (FALSE) if */
/* same, 1 if after */
/* Wildcards ('?') in name */
for (i = 0 ; i < FNAM; i++) /* a match any character */
{ /* in name b */
if ((c_a = a->fname[i] & 0x7f) == '?')
continue;
if (c_a < (c_b = b->fname[i] & 0x7f))
return (-1);
if (c_a > c_b)
return (1);
}
return (0); /* Names NOT different (ie */
} /* they ARE the same) */
/********************************/
/* */
/* I N D _ N A M E _ D I F F */
/* */
/********************************/
int /* Return ordering info for */
ind_name_diff(p_a, p_b) /* for two FCB's (see */
struct fcbhalf **p_a, **p_b; /* name_diff above) given */
{ /* the addresses of two */
/* pointers to FCB's */
return (name_diff((struct fcbtab *) *p_a, *p_b));
} /* Note: double indirection */
/* Used by qsort function */
/********************************/
/* */
/* C O U N T _ B L K S */
/* */
/********************************/
VOID /* Either check extent at */
count_blks(allo) /* bfcba for consistent */
BOOLEAN allo; /* allocation (allo TRUE) */
{ /* Or add length of extent */
register int i, mb; /* in Kbytes to *fcbma */
i = sizeof bfcba->resvd; /* Start at end of block */
while ((i -= (word_blks) ? 2 : 1) >= 0) /* pointers and work */
{ /* towards beginning */
mb = bfcba->resvd[i]; /* Is the current single */
if (word_blks) /* or double length */
mb |= bfcba->resvd[i + 1];/* pointer zero? */
if (mb != 0)
{ /* No: block is allocted */
if (allo) /* See if consistent */
{
if (! allocate((struct block_no *)
(&bfcba->resvd[i])))
return; /* (give up if not) */
}
else /* or tot up size in */
fcbsa->kcnt += kpb;/* Kbytes */
}
}
}
/********************************/
/* */
/* C H E C K _ U S E R */
/* */
/********************************/
VOID /* Find next file matching */
check_user() /* current name and user */
{ /* Check all intervening */
/* directory entries for */
/* consistent allocation */
while (dcnt != 255) /* Until directory end */
{ /* Get pointer to this FCB */
/* in default DMA buffer */
bfcba = (struct fcbtab *) &buff[dcnt * 32];
/* Not deleted file or XFCB?*/
/* (beware sign extension)*/
if ((UWORD) (bfcba->drive & 0xff) < 0x20)
{
count_blks(TRUE); /* Check allocation legal */
if ((! name_diff(fcb, bfcba)) /* Name & user match? */
&& ((bfcba->drive & 0x0f) == user_code))
return; /* Yes: return */
}
dcnt = _srch_next(); /* Try next directory entry */
}
}
/********************************/
/* */
/* S E T F S T A T U S */
/* */
/********************************/
BOOLEAN /* Parse file attribute */
setfstatus() /* assignment. Return */
{ /* TRUE if valid assign */
/* found. */
if (! parse_next()) /* No more tokens? */
return (FALSE);
if (token[0] == '=') /* Skip optional '=' */
scan();
/* STAT filename SIZE ? */
if ((scase1 = match(attribl, A_SIZE)) == OPT_SIZE)
{ /* Yes: not an attribute */
sizeset = TRUE; /* assignment */
return (FALSE);
}
if (scase1 != 0) /* If valid attribute does */
{ /* another follow? */
if (parse_next()) /* If so, is it reasonable? */
{ /* RO RO, SYS DIR etc are */
/* rejected */
if (((scase2 = match(attribl, A_SIZE)) != 0)
&& (abs(scase2 - scase1) > 1))
return (TRUE); /* Two good attributes */
}
else
return (TRUE); /* One good attribute */
}
print(invalid); /* Something wrong. Print */
print(use); /* the bad news */
printx(filename);
printx(" [SIZE] ");
printx(sattrib);
_exit(1); /* User screwed up: abort */
}
/********************************/
/* */
/* S E T _ S A T T R I B */
/* */
/********************************/
VOID /* Set/reset selected att- */
set_sattrib(scase) /* ributes of current file*/
int scase;
{
switch(scase)
{
case OPT_RO: /* Read-only */
fcbsa->ROFILE |= 0x80;
printx(readonly);
break;
case OPT_RW: /* Read-write */
fcbsa->ROFILE &= 0x7f;
printx(readwrite);
break;
case OPT_SYS: /* "System" (does not appear*/
fcbsa->SYSFILE |= 0x80; /* in directory listing */
printx("System (Sys)");
break;
case OPT_DIR: /* "Directory" (appears in */
fcbsa->SYSFILE &= 0x7f; /* directory listing */
printx("Directory (Dir)");
break;
default:
print(invalid);
}
}
/********************************/
/* */
/* C O M P A R E _ F C B */
/* */
/********************************/
BOOLEAN /* Check whether current FCB*/
compare_fcb() /* refers to a file we've */
{ /* already encountered */
register UWORD i; /* Return index of matching */
/* entry or free slot */
fcbsa = fcbs;
for (i = 0; i < fcbn; fcbsa++, i++) /* Points fcbsa at the */
{ /* matching name, or at */
if (! name_diff(fcbsa, bfcba)) /* next empty slot */
break;
}
return (i);
}
/********************************/
/* */
/* C O P Y _ F C B */
/* */
/********************************/
VOID /* Add the current directory*/
copy_fcb() /* entry to the list of */
{ /* known files (used when */
/* entry has name not */
/* previously encountered)*/
fcbn++; /* Increment count */
if (fcbn > fcbmax) /* Too many files? */
{
print("Too Many Files");
_exit(1); /* Fatal error */
}
finx[fcbn - 1] = fcbsa; /* Save index for later sort*/
*fcbsa = *((struct fcbhalf *) bfcba); /* Copy FCB. Clear extent, */
fcbsa->extent = fcbsa->kcnt = /* byte and record count */
fcbsa->rcnt = 0;
}
/********************************/
/* */
/* A D D _ F C B _ B L K S */
/* */
/********************************/
VOID /* Update the statistics of */
add_fcb_blks() /* current file with the */
{ /* information from the */
register int kb; /* current FCB */
if (bfcba->drive < 0x10) /* Drive field holds user no*/
{
nfcbs++; /* Increment fcb count */
for (kb = 0; kb < FNAM; kb++) /* Turn on any attributes */
{ /* that are set in case */
/* missing in previous */
/* extents */
if (bfcba->fname[kb] & 0x80)
fcbsa->fname[kb] |= 0x80;
}
if (bfcba->ARCHIV & 0x80) /* Turn of archiving if any */
fcbsa->ARCHIV &= 0x7f; /* extent not archived */
fcbsa->extent++; /* Prepare for next extent */
/* Tot up logical file size */
/* (beware sign extension)*/
fcbsa->rcnt += (bfcba->reccnt & 0xff) +
(bfcba->extent & dpba->exm) * 128;
count_blks(FALSE); /* & physcal blocks used */
}
else if (bfcba->drive < 0x20) /* This is an extended FCB */
{
fcbsa->s1 |= 0x80; /* Set XFCB exists flag */
}
/* If bfcba->drive >= 0x20, */
/* directory entry free */
}
/********************************/
/* */
/* D I S P L A Y */
/* */
/********************************/
VOID /* Display file details */
display() /* (STAT afn [SIZE]) */
{
register int add, sizecols; /* Layout variables */
UWORD kblks; /* Total number of 1k blks */
UWORD tall; /* Total allocation */
BOOLEAN wide, xfcbfound;
register int l;
add = sizecols = 0; /* Calculate layout for */
if ((wide = (columns() > 48))) /* displayed data */
add = 7;
if (sizeset) /* Printing physical size? */
add += (sizecols = 10);
print(drivename); /* Show the drive name */
show_drive();
blanks(17 + add);
show_usr(user_code); /* and user code */
if (sizeset) /* Print appropriate header */
print(" Size "); /* according to data */
else /* requested and screen */
crlf(); /* width */
printx(" Recs Bytes FCBs Attrib");
if (wide)
printx("utes ");
printx(" Name");
tall = kblks = 0; /* kfg - added in response */
/* to lint complaints. */
for (l = 0; l < fcbn; l++) /* For each matched file */
{
/*Move FCB to full-size FCB*/
fcbsa = finx[l];
*((struct fcbhalf *) fcb) = *fcbsa;
crlf();
fcb->drive = 0;
if (sizeset) /* Need to print size? */
{
_filsiz(fcb); /* Have BDOS calculate */
/* virtual file size */
#ifdef HILO
p_long((LONG) fcb->record & 0xffffff);
#else /* On LOHI machine, shift */
p_long((LONG) fcb->record >> 8);/* result to low */
#endif /* end of long word */
_conout(' ');
}
/* Following expression gets*/
/* index in size table by */
/* finding index of curr- */
/* ent FCB in name table */
pdecimal(fcbsa->rcnt, 10000, TRUE);
/* Display physical record */
_conout(' '); /* count */
kblks += (fcbsa->rcnt + 7) / 8; /* Tot up total Kbytes, */
/* rounding fractions up */
/* Print size in Kbytes */
pdecimal((fcbsa->rcnt + 7) / 8, 10000, TRUE);
tall += fcbsa->kcnt;
printx("k ");
xfcbfound = attribute(HAS_XFCB);/* Does this file have XFCB?*/
fcbsa->s1 &= 0x7f; /* Now we know, clear flag */
/* === Why? === */
/* Print no of FCB's */
pdecimal((UWORD) fcbsa->extent, 1000, TRUE);
/* Display attributes */
printx((attribute(SYSFILE)) ? " Sys " : " Dir ");
printx((attribute(ROFILE)) ? "RO " : "RW ");
if (wide) /* Enough room for more? */
{ /* Yes: show more attributes*/
_conout((xfcbfound) ? 'X' : ' ');
_conout((attribute(ARCHIV)) ? 'A' : ' ');
_conout((attribute(ATTRB1)) ? '1' : ' ');
_conout((attribute(ATTRB2)) ? '2' : ' ');
_conout((attribute(ATTRB3)) ? '3' : ' ');
printx((attribute(ATTRB4)) ? "4 " : " ");
}
printfn(fcbsa); /* At last, the filename! */
}
/* All file info printed: */
/* now do totals */
dots(39 + add); /* Line up columns neatly */
print("Total:");
blanks(sizecols);
pdecimal(tall, 10000, TRUE); /* Total kbytes */
_conout('k');
pdecimal(nfcbs, 10000, TRUE); /* Total number of FCB's */
printx(" ("); /* of files */
pdecimal(fcbn, 1000, TRUE);
printx((fcbn == 1) ? " file" : " files");
if (wide) /* Room for more? */
{
printx((fcbn == 1) ? ", " : ",");
pdecimal(kblks, 10000, TRUE); /* Print kbytes used */
printx("-1k blocks");
}
_conout(')');
}
/********************************/
/* */
/* S E T F A T T */
/* */
/********************************/
VOID /* Set the attribtes of all */
setfatt() /* matched files to those */
{ /* requested */
register int l;
for (l = 0; l < fcbn; l++) /* For each matched file */
{
crlf();
printfn(fcbsa = finx[l]); /* Print its name */
printx(set_to); /* and what its attributes*/
set_sattrib(scase1); /* will be */
if (scase2 != 0)
{
printx(", ");
set_sattrib(scase2);
}
fcbsa->drive = 0; /* Clear user no. (would be */
/* interpreted as drive) */
_set_att(fcbsa); /* Go fix attributes */
}
}
/********************************/
/* */
/* G E T F I L E */
/* */
/********************************/
VOID /* Process request involving*/
getfile() /* (possibly ambiguous) */
{ /* filename. The CCP has */
/* parsed name and put it */
/* in basepage FCB for us */
if ((set_attribute = setfstatus()) /* Find out what to do from */
&& (scase1 == 0)) /* command line. Give up */
return; /* on error */
alloc = all_map(); /* Allocate disk map space */
fcbmax = all_fcb(); /* and fcb space */
fcbn = fcb->drive = 0; /* Search for first file on */
fcb->extent = fcb->s2 = '?'; /* current drive */
dcnt = _srch_1st("?"); /* Read first directory */
/* sector from current drv*/
check_user(); /* Do we have a match? */
while (dcnt != 255) /* While more directory FCBs*/
{
/* Get address of this FCB */
/* in directory buffer */
bfcba = (struct fcbtab *) &buff[(dcnt & 0x3) * 32];
if (compare_fcb() >= fcbn) /* Is this an extent of a */
/* file we already met? */
copy_fcb(); /* Yes: add name to list */
add_fcb_blks(); /* Adjust file's block count*/
dcnt = _srch_next(); /* Find next directory entry*/
check_user();
test_kbd_esc(); /* Give user chance to abort*/
}
if (! error_free) /* Allocation inconsitent? */
_exit(1); /* Yes: user must clean up! */
if (fcbn == 0) /* Did we find any files? */
{
print("File Not Found"); /* No: tell user */
return;
}
if (set_attribute) /* Are we setting file */
{ /* attributes? */
setfatt(); /* Yes */
return;
}
/* User must want display of*/
/* collected data. First */
/* sort names then display*/
qsort(finx, fcbn, sizeof (struct fcbhalf *), ind_name_diff);
display();
pralloc();
}
/********************************/
/* */
/* P R D R I V E */
/* */
/********************************/
VOID /* Show current drive and */
prdrive(a) /* status to be assigned */
char *a;
{
print(&drivename[1]); /* print("Drive "), */
show_dv(); /* name, */
printx(set_to);
printx(a); /* and status */
}
/********************************/
/* */
/* S E T D R I V E S T A T U S */
/* */
/********************************/
VOID /* Set current drive to RO */
setdrivestatus() /* or RW status */
{
switch (match(attribl, A_SIZE))
{
case OPT_RO:
_wr_protd();
prdrive(readonly);
break;
case OPT_RW:
if ((_rs_drive(1 << cdisk)) != 0)
print("Disk Reset Denied");
else
prdrive(readwrite);
break;
default:
print(invalid);
print(use);
printx("d:=RO");
}
}
/****************************************************************************/
/* */
/* C O M M A N D P R O C E S S I N G */
/* ----------------------------------- */
/* */
/****************************************************************************/
/********************************/
/* */
/* P A R S E _ I T */
/* */
/********************************/
VOID /* Process command line in */
parse_it() /* one of three ways: */
{
switch (match(devl, L_SIZE))
{
case OPT_USR: /* Request user status */
userstatus();
break;
case OPT_DSK: /* Set drive status */
drivestatus();
break;
default: /* Set/display file details */
getfile();
}
}
/********************************/
/* */
/* D E V R E Q */
/* */
/********************************/
BOOLEAN /* Process a device request */
devreq() /* Return true if valid */
{
register UWORD iomask;
BOOLEAN first;
register int j, k;
first = TRUE;
FOREVER /* Process each arg in turn */
{ /* Is this a device name? */
if ((j = match(devl, L_SIZE)) == 0)
{ /* No: error unless first */
if (! first) /* token */
goto error;
return (FALSE); /* Not a device request */
}
first = FALSE; /* Found first/next item */
switch (j) /* What did we find? */
{
case 5: /* Device status request */
devstatus();
break;
case 6: /* List possible assignment */
values();
break;
case 7: /* List user status values */
userstatus(); /* Exit when done (search */
_exit(0); /* zaps rest of cmd line) */
case 8: /* Show the disks' status */
diskstatus();
break;
/****************************/
/* B E W A R E ! */
/* Many hard coded constants*/
/* below. Unless iobyte */
/* changes size, they're */
/* OK */
/****************************/
default: /* Logical-physical device */
/* assignment */
/* Scan table item[j - 1] */
k = --j * 16; /* Index to valid devices */
if (! parse_assign()) /* for assignment */
goto error; /* Not assignment: error */
if ((k = match(&devr[k], 4) - 1) < 0)
goto error; /* Not valid phys device */
iomask = ~3; /* Mask has two bits clear */
while (j--) /* Find correct iobyte field*/
{ /* (shift left, 1's fill) */
iomask = (iomask << 2) | 3;
k <<= 2; /* k holds required mapping */
}
/* Replace designated iobyte*/
/* field with value in k */
_set_iob((_get_iob() & iomask) | k);
} /* end of switch */
if (! parse_next()) /* If no more tokens, return*/
return (TRUE);
} /* End of forever */
error: /* Invalid command line */
print(invalid);
return (TRUE);
}
/****************************************************************************/
/* */
/* S T A T : M A I N F U N C T I O N */
/* -------------------------------------- */
/* */
/****************************************************************************/
VOID
_main()
{
printx(signonmsg); /* Sign on with release # */
crlf();
ver = _version(); /* Get CP/M version number */
fcb = &_base->fcb1; /* Set up pointers to first */
/* basepage FCB */
buff = _base->buff; /* and to DMA buffer */
cdisk = _ret_cdisk(); /* Find out current drive */
user_code = _gset_ucode(0xff); /* and the user */
if (! parse_next()) /* If command line empty, */
{
prstatus(); /* print status */
_exit(0);
}
if (token[1] == ':') /* Not empty: drive name? */
{
select_disk(token[0] - 'A'); /* Yes: select it */
if (! parse_next()) /* Was that all? */
{
pralloc(); /* Yes: display allocation */
_exit(0);
}
if (token[0] == '=') /* No. Perhaps attribute */
{ /* assignment? */
scan(); /* Yes: get parameter */
setdrivestatus(); /* and assign */
_exit(0);
}
parse_it(); /* Neither of above. Choose*/
_exit(0); /* from: drive status */
/* user status */
} /* file operation */
set_kpb(); /* Get current disk details */
if (! devreq()) /* Try to perform device */
/* request. If that fails*/
getfile(); /* must be file request on*/
/* current drive */
}