cmcmanis@sun.uucp (Chuck McManis) (05/18/87)
The enclosed program is a C version of the AmigaDOS info command. I wrote it as part of a program sponsered by Charlie Heath on BIX who has set out to replace all of the AmigaDOS commands on the Workbench disk with versions written in C and assembler. The purpose being that C commands do not require the funky BCPL setup and can therefore be used in Execute calls and Lattice fork() calls. You also have the advantage of getting source to the AmigaDOS commands. This program was written to minimize the size of the executable, and to do that I have eliminated nearly all of the references to the Lattice library lc.lib, what remains are the calls for 32 bit multiply and divide. I used Carolyn's TWStartup.asm file for the startup code, thus eliminating any reference to the Lattice stdio package however that means compiling with the -v option (disable stack checking). For compactness I also compiled with the -r option which causes the use of PC relative branching rather than long branching. Blink switches include NODEBUG, SMALLCODE and SMALLDATA. see the .lnk command file outline in the main comment. Also I took some artistic license and switched the output of the Volumes available and the Disk statistics. Since I am usually more interested in how much space a given diskette has rather that the fact that it is mounted or not. To make it *really* info compatible you will have to switch the Pass 1 and Pass 2 code. Watch out for the signature at the end of the message... --Chuck -------------------------------------cut here-------------------------- /* * 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 */ } -----------------------also cut here--------------------------- -- --Chuck McManis uucp: {anywhere}!sun!cmcmanis BIX: cmcmanis ARPAnet: cmcmanis@sun.com These opinions are my own and no one elses. But you knew that, didn't you.