home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
newc_dev
/
info_src.arc
/
info.c
next >
Wrap
C/C++ Source or Header
|
1988-05-27
|
10KB
|
342 lines
/*
* Info Command - C Language Equivalent.
*
* This command looks and feels like the original AmigaDOS Info command
* except that it is written in C and thus available for "forking" with the
* lattice 3.10 compiler. Also since it is written in C it is a somewhat
* larger than it's BCPL counterpart although a good assembly hack could
* probably fix that.
*
* (c) Copyright 1986 Charles McManis, All rights reserved.
* This code may be copied for private use only. It may not be
* included as part of any commercial package in whole or in
* part without the express written permission of the Author.
*
* Permission is granted to distribute this package as part of the AmigaDOS
* Replacement Project (ARP) or as part of the Fish Library.
*
* Compiling and Linking Information - This program was coded to minimize
* the resulting executable size. To that end 99% of all references to
* Lattice's library were removed, what remains are references to _CXD33
* and _CXM33 (some math routines) so you still need lc.lib but it is a
* lot smaller than it normally would be. And Carolyn Scheppner's startup
* code 'TWstartup.asm' was used rather than Lattice's c.o (I assembled
* TWStartup.asm into ac.o.)
*
* The compile command I used to compile the source was :
* LC -r -v info
*
* and the Blink command file (info.lnk) was set up as follows :
* FROM ac.o+info.o
* TO info
* LIB LIB:amiga.lib+LIB:lc.lib
* SMALLCODE
* SMALLDATA
* NODEBUG
* MAP info.map
*
* The blink command to link this file is
* Blink with info.lnk
*
* After you are done you should end up with an executable that is about
* 4336 bytes long. Which compares favorably to the 1708 bytes of the
* assem/BCPL version. The advantage to having a C version are two fold.
* First, you can use the Lattice fork() call or the AmigaDOS Execute()
* call to run this version from your program, and second you get to
* see the source to the info command. Something Commodore wouldn't let
* you do without paying big bucks. So here it is 'info' the C version.
*/
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
#include <ctype.h>
/* Note there is a bug in the Commodore supplied 'info' command. The
* way it calculates the size of the disk is with the following formula
*
* size = ((NumberofBlocks + 2) * 512) / 1024
*
* The actual size is :
*
* size = (NumberofBlocks * NumBytesPerBlock)/ 1024;
*
* define INFO_COMPATIBLE to get it their way, leave it undefined to get the
* correct way.
*/
#define INFO_COMPATIBLE
extern struct DosLibrary *DOSBase;
#ifdef DEBUG
struct RootNode *rn;
struct DeviceList *dl, *t, *t2;
struct DeviceNode *dn;
struct DosInfo *di;
#endif
char *TWspec = "";
/*
* Function cvtnum(num,str)
* This function converts an integer to a string. It returns the length of
* the string created. Change BASE for different bases, make it a variable for
* dynamic base calculations.
*/
#define BASE 10
int
cvtnum(str,num)
char *str;
long num;
{
short int i,j,ndx;
long temp;
char digit;
ndx = 0;
if (num < 0) { *(str+ndx++) = '-'; num = - (num); }
if (num == 0) {
*(str) = '0';
*(str+1) = '\0';
return(1);
}
i = 0;
temp = 1;
while (temp <= num) {
temp *= BASE;
i++;
}
temp /= BASE;
for (j=0; j<i; j++) {
digit = ((num/temp) < 10) ? '0'+ (num/temp) : 'A' + ((num/temp)-10);
*(str+ndx++) = digit;
num %= temp;
temp /= BASE;
}
*(str+ndx) = '\0';
return((int)ndx);
}
/* function strlen(str) - calculate the length of a string
*/
int strlen(str)
char *str;
{
int i;
for (i=0; *(str+i) != 0; i++) ;
return(i);
}
/*
* Function - Pad (buf, size)
* This function will pad a string (usually a number) to be right justifed in
* 'size' spaces.
*/
void
Pad(str,size)
char *str;
int size;
{
short i,j;
j = strlen(str);
if (j >= size) return;
for (i=size; i >= 0; i--)
*(str+i) = (j < 0) ? ' ' : *(str+j--);
}
#define MyExit(cc) Exit(cc)
/* These are some macros that help in dealing with BCPL pointers and strings
* the first is macro converts a BPTR to a C pointer of type struct DeviceList *
* The second two provide the length of a BSTR * and a pointer to it's text.
*/
#define DLPTR(x) ((struct DeviceList *)BADDR(x))
#define LENGTH(x) (*(UBYTE *)BADDR(x))
#define STRING(x) (((char *)BADDR(x))+1)
/*
* Function - FindDevice(ListPtr,DeviceType)
*
* This function will find a device of the specified type in the DeviceInfo
* list and return a pointer to it. If the device List pointer you pass it
* points to a struct of type DeviceType it will simply return that device
* pointer. If you pass it NULL it will return NULL, and if it fails to
* find a matching entry it returns NULL.
*
* Note to find the next device in the list you must update the pointer you
* pass to the next device in the list. See the code below for some examples.
*/
struct DeviceList *
FindDevice(dlp,dlt)
struct DeviceList *dlp; /* Pointer to a device list structure */
long dlt; /* A device type as defined in dos.h */
{
struct DeviceList *t; /* A temporary pointer */
for (t = dlp; ((t != NULL) && (t->dl_Type != dlt)); t = DLPTR(t->dl_Next));
return(t);
}
/* This macro writes out data to the screen bypassing C's printf statement */
#define PutS(str) Write(Output(),str,strlen(str))
/*
* Main code, This is where the code actually implements the Info command.
* it is pretty simple really, first we build a list of all the disk devices
* and do an 'Info' on each one, then list out all of the volumes.
*/
void main()
/* NOARGS */
{
/* The pointers here make it easier later */
#ifndef DEBUG
struct RootNode *rn;
struct DeviceList *dl, *t, *t2;
struct DeviceNode *dn;
struct DosInfo *di;
#endif
struct InfoData info;
struct Process *myp;
APTR oldwinptr;
BPTR l;
char buf[80];
int i,a,u,size;
PutS("Info command substitute v1.0\n");
/* Then we track down the head of the Device list from the Root Node */
rn = (struct RootNode *)DOSBase->dl_Root;
di = (struct DosInfo *)BADDR(rn->rn_Info);
/* dl becomes the anchor point that we always start from */
dl = (struct DeviceList *)BADDR(di->di_DevInfo);
/*
* Ok, now we list out all of the disk devices ...
*
* Pass 1: Print out all of the known volumes, if they have a handler
* task present then they are mounted in a physical device
*/
PutS("Volumes Available:\n");
for (t = FindDevice(DLPTR(di->di_DevInfo),DLT_VOLUME); t != NULL;
t = FindDevice(DLPTR(t->dl_Next),DLT_VOLUME)) {
Write(Output(),STRING(t->dl_Name),LENGTH(t->dl_Name));
if (t->dl_Task != NULL) PutS(" [Mounted]");
PutS("\n");
}
PutS("\n");
/* Pass 2 : Print out all of the disk devices, like the original we
* pretty much assume device names are three characters long.
* (They can be more though.)
*/
/* Disable requesters if no disk present */
myp = (struct Process *) FindTask(NULL);
oldwinptr = myp->pr_WindowPtr;
myp->pr_WindowPtr = (APTR) -1;
PutS("Mounted Disks:\n");
PutS("Unit Size Used Free Full Errs Status Name\n");
i = 0;
for (t = FindDevice(DLPTR(di->di_DevInfo),DLT_DEVICE); t != NULL;
t = FindDevice(DLPTR(t->dl_Next),DLT_DEVICE)) {
dn = (struct DeviceNode *) t;
/* This is supposed to distinguish a disk device (a task pointer) */
if (dn->dn_Task) {
Write(Output(),STRING(dn->dn_Name), LENGTH(dn->dn_Name));
PutS(": ");
for (i=0; i<LENGTH(dn->dn_Name); i++) buf[i] = *(STRING(dn->dn_Name)+i);
buf[i++] = ':'; buf[i] = '\0';
l = Lock(buf,ACCESS_READ);
if (l == NULL) PutS("No disk present\n");
else {
Info(l,&info);
/* Calculate the size in K bytes (add 2 to blocks for) 'reserved'
* blocks which is a *bug* in the original info but what the heck.
*/
a = info.id_NumBlocks;
#ifdef INFO_COMPATIBLE
size = ((a+2) * 512) >> 10;
#else
size = (a * info.id_BytesPerBlock ) >> 10;
#endif
i = cvtnum(buf,size);
/* The following case statement formats the number properly */
buf[3] = 'K'; /* defaults to K bytes */
switch (i) {
case 1 : buf[2] = buf[0];
buf[1] = ' '; /* fill in with spaces */
buf[0] = ' '; /* fill in with spaces */
break;
case 5 : buf[3] = 'M';
case 2 : buf[2] = buf[1];
buf[1] = buf[0];
buf[0] = ' ';
break;
case 6 : buf[3] = 'M';
case 3 : break;
case 4 :
case 7 : buf[3] = (i == 4) ? 'M' : 'G';
buf[2] = buf[1];
buf[1] = '.';
break;
default: buf[0] = 'H'; /* Bigger than 10 Gigabytes */
buf[1] = 'U';
buf[2] = 'G';
buf[3] = 'E';
break;
} /* end switch */
buf[4] = ' '; buf[5] = '\0';
PutS(buf);
u = info.id_NumBlocksUsed;
/* Now build the stats line without sprintf */
cvtnum(buf,u);
Pad(buf,7);
*(buf+7) = ' ';
cvtnum(buf+8,a-u);
Pad(buf+8,7);
*(buf+15) = ' ';
cvtnum(buf+16,((a-(a-u))*100)/a);
Pad(buf+16,3);
*(buf+19) = '%';
*(buf+20) = ' ';
cvtnum(buf+21,info.id_NumSoftErrors);
Pad(buf+21,3);
PutS(buf);
switch (info.id_DiskState) {
case ID_WRITE_PROTECTED : PutS(" Read Only ");
break;
case ID_VALIDATING : PutS(" Validating ");
break;
case ID_VALIDATED : PutS(" Read/Write ");
break;
default: PutS(" Strange ");
break;
} /* state switch */
t2 = DLPTR(info.id_VolumeNode);
if (t2 != NULL) Write(Output(),STRING(t2->dl_Name), LENGTH(t2->dl_Name));
PutS("\n");
UnLock(l);
} /* else had a disk in it */
} /* If it was a disk device */
} /* For loop */
PutS("\n");
myp->pr_WindowPtr = oldwinptr;
MyExit(RETURN_OK); /* Exit with a status of zero */
}