home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / newc_dev / info_src.arc / info.c next >
C/C++ Source or Header  |  1988-05-27  |  10KB  |  342 lines

  1. /* 
  2.  * Info Command - C Language Equivalent. 
  3.  *
  4.  *     This command looks and feels like the original AmigaDOS Info command
  5.  *  except that it is written in C and thus available for "forking" with the
  6.  *  lattice 3.10 compiler. Also since it is written in C it is a somewhat 
  7.  *  larger than it's BCPL counterpart although a good assembly hack could 
  8.  *  probably fix that. 
  9.  *
  10.  *  (c) Copyright 1986 Charles McManis, All rights reserved.
  11.  *  This code may be copied for private use only. It may not be
  12.  *  included as part of any commercial package in whole or in
  13.  *  part without the express written permission of the Author.
  14.  *
  15.  *  Permission is granted to distribute this package as part of the AmigaDOS
  16.  *  Replacement Project (ARP) or as part of the Fish Library.
  17.  *  
  18.  *  Compiling and Linking Information - This program was coded to minimize
  19.  *  the resulting executable size. To that end 99% of all references to 
  20.  *  Lattice's library were removed, what remains are references to _CXD33
  21.  *  and _CXM33 (some math routines) so you still need lc.lib but it is a
  22.  *  lot smaller than it normally would be. And Carolyn Scheppner's startup
  23.  *  code 'TWstartup.asm' was used rather than Lattice's c.o (I assembled 
  24.  *  TWStartup.asm into ac.o.) 
  25.  *
  26.  *  The compile command I used to compile the source was :
  27.  *  LC -r -v info
  28.  *
  29.  *  and the Blink command file (info.lnk) was set up as follows :
  30.  *  FROM ac.o+info.o
  31.  *  TO info
  32.  *  LIB LIB:amiga.lib+LIB:lc.lib
  33.  *  SMALLCODE
  34.  *  SMALLDATA
  35.  *  NODEBUG
  36.  *  MAP info.map
  37.  *
  38.  *  The blink command to link this file is 
  39.  *  Blink with info.lnk 
  40.  *
  41.  *  After you are done you should end up with an executable that is about
  42.  *  4336 bytes long. Which compares favorably to the 1708 bytes of the 
  43.  *  assem/BCPL version. The advantage to having a C version are two fold.
  44.  *  First, you can use the Lattice fork() call or the AmigaDOS Execute()
  45.  *  call to run this version from your program, and second you get to 
  46.  *  see the source to the info command. Something Commodore wouldn't let
  47.  *  you do without paying big bucks. So here it is 'info' the C version.
  48.  */
  49.  
  50. #include <exec/types.h>
  51. #include <libraries/dos.h>
  52. #include <libraries/dosextens.h>
  53. #include <libraries/filehandler.h>
  54. #include <ctype.h>
  55.  
  56. /* Note there is a bug in the Commodore supplied 'info' command. The
  57.  * way it calculates the size of the disk is with the following formula
  58.  *
  59.  * size = ((NumberofBlocks + 2) * 512) / 1024
  60.  *
  61.  * The actual size is :
  62.  *
  63.  * size = (NumberofBlocks * NumBytesPerBlock)/ 1024;
  64.  *
  65.  * define INFO_COMPATIBLE to get it their way, leave it undefined to get the
  66.  * correct way.
  67.  */
  68.  
  69. #define INFO_COMPATIBLE
  70.  
  71. extern struct DosLibrary *DOSBase;
  72.  
  73. #ifdef DEBUG
  74.   struct RootNode     *rn;
  75.   struct DeviceList     *dl, *t, *t2;
  76.   struct DeviceNode     *dn;
  77.   struct DosInfo    *di;
  78. #endif
  79. char    *TWspec = "";
  80.  
  81. /*
  82.  * Function cvtnum(num,str)
  83.  *   This function converts an integer to a string.  It returns the length of
  84.  * the string created. Change BASE for different bases, make it a variable for
  85.  * dynamic base calculations.
  86.  */
  87. #define BASE 10 
  88.  
  89. int
  90. cvtnum(str,num)
  91.  
  92. char    *str;
  93. long    num;
  94.  
  95. {
  96.   short int   i,j,ndx;
  97.   long   temp;
  98.   char  digit;
  99.  
  100.   ndx = 0;
  101.   if (num < 0) { *(str+ndx++) = '-'; num = - (num); }
  102.   if (num == 0) {
  103.     *(str) = '0';
  104.     *(str+1) = '\0';
  105.     return(1);
  106.   }
  107.   i = 0;
  108.   temp = 1;
  109.   while (temp <= num) {
  110.     temp *= BASE;
  111.     i++;
  112.   }
  113.   temp /= BASE;
  114.   for (j=0; j<i; j++) {
  115.     digit = ((num/temp) < 10) ? '0'+ (num/temp) : 'A' + ((num/temp)-10);
  116.     *(str+ndx++) = digit;
  117.     num %= temp;
  118.     temp /= BASE;
  119.   }
  120.   *(str+ndx) = '\0';
  121.   return((int)ndx);
  122. }
  123. /* function strlen(str) - calculate the length of a string
  124.  */
  125. int strlen(str)
  126.  
  127. char *str;
  128. {
  129.   int i;
  130.   for (i=0; *(str+i) != 0; i++) ;
  131.   return(i);
  132. }
  133.  
  134. /*
  135.  * Function - Pad (buf, size)
  136.  * This function will pad a string (usually a number) to be right justifed in
  137.  * 'size' spaces. 
  138.  */
  139. void
  140. Pad(str,size)
  141.  
  142. char    *str;
  143. int    size;
  144.  
  145. {
  146.   short i,j;
  147.  
  148.   j = strlen(str);
  149.   if (j >= size) return;
  150.   for (i=size; i >= 0; i--) 
  151.     *(str+i) = (j < 0) ? ' ' : *(str+j--);
  152. }    
  153.   
  154. #define MyExit(cc)    Exit(cc)
  155. /* These are some macros that help in dealing with BCPL pointers and strings
  156.  * the first is macro converts a BPTR to a C pointer of type struct DeviceList *
  157.  * The second two provide the length of a BSTR * and a pointer to it's text.
  158.  */
  159.  
  160. #define DLPTR(x)    ((struct DeviceList *)BADDR(x))
  161. #define LENGTH(x)    (*(UBYTE *)BADDR(x))
  162. #define STRING(x)    (((char *)BADDR(x))+1)
  163.  
  164. /* 
  165.  * Function - FindDevice(ListPtr,DeviceType)
  166.  * 
  167.  * This function will find a device of the specified type in the DeviceInfo
  168.  * list and return a pointer to it. If the device List pointer you pass it
  169.  * points to a struct of type DeviceType it will simply return that device
  170.  * pointer. If you pass it NULL it will return NULL, and if it fails to
  171.  * find a matching entry it returns NULL.
  172.  *
  173.  * Note to find the next device in the list you must update the pointer you
  174.  * pass to the next device in the list. See the code below for some examples.
  175.  */
  176.  
  177. struct DeviceList *
  178. FindDevice(dlp,dlt)
  179.  
  180. struct DeviceList *dlp;        /* Pointer to a device list structure */
  181. long          dlt;        /* A device type as defined in dos.h  */
  182.  
  183. {
  184.   struct DeviceList    *t; /* A temporary pointer */
  185.  
  186.   for (t = dlp; ((t != NULL) && (t->dl_Type != dlt)); t = DLPTR(t->dl_Next));
  187.   return(t);
  188. }
  189.  
  190. /* This macro writes out data to the screen bypassing C's printf statement */
  191.  
  192. #define PutS(str)    Write(Output(),str,strlen(str))
  193.  
  194.  
  195. /*
  196.  * Main code, This is where the code actually implements the Info command.
  197.  * it is pretty simple really, first we build a list of all the disk devices
  198.  * and do an 'Info' on each one, then list out all of the volumes.
  199.  */
  200.  
  201. void main()
  202.  
  203. /* NOARGS */
  204.  
  205. {
  206.   /* The pointers here make it easier later */
  207.  
  208. #ifndef DEBUG
  209.   struct RootNode     *rn;
  210.   struct DeviceList     *dl, *t, *t2;
  211.   struct DeviceNode     *dn;
  212.   struct DosInfo    *di;
  213. #endif
  214.   struct InfoData    info;
  215.   struct Process    *myp;
  216.   APTR            oldwinptr;
  217.   BPTR            l;
  218.   char            buf[80];
  219.   int            i,a,u,size;
  220.  
  221.  
  222.   PutS("Info command substitute v1.0\n");
  223.   /* Then we track down the head of the Device list from the Root Node */
  224.   rn = (struct RootNode *)DOSBase->dl_Root;
  225.   di = (struct DosInfo *)BADDR(rn->rn_Info);
  226.   /* dl becomes the anchor point that we always start from */
  227.   dl = (struct DeviceList *)BADDR(di->di_DevInfo);  
  228.  
  229.   /* 
  230.    * Ok, now we list out all of the disk devices ...
  231.    * 
  232.    * Pass 1: Print out all of the known volumes, if they have a handler
  233.    *         task present then they are mounted in a physical device
  234.    */
  235.   PutS("Volumes Available:\n");
  236.   for (t = FindDevice(DLPTR(di->di_DevInfo),DLT_VOLUME); t != NULL;
  237.        t = FindDevice(DLPTR(t->dl_Next),DLT_VOLUME)) {
  238.     Write(Output(),STRING(t->dl_Name),LENGTH(t->dl_Name));
  239.     if (t->dl_Task != NULL) PutS(" [Mounted]");
  240.     PutS("\n");
  241.   }
  242.   PutS("\n");
  243.  
  244.   /* Pass 2 : Print out all of the disk devices, like the original we 
  245.    * pretty much assume device names are three characters long. 
  246.    * (They can be more though.)
  247.    */
  248.   /* Disable requesters if no disk present */
  249.   myp = (struct Process *) FindTask(NULL);
  250.   oldwinptr = myp->pr_WindowPtr;
  251.   myp->pr_WindowPtr = (APTR) -1;
  252.  
  253.   PutS("Mounted Disks:\n");
  254.   PutS("Unit Size    Used    Free Full Errs   Status   Name\n");
  255.   i = 0;
  256.   for (t = FindDevice(DLPTR(di->di_DevInfo),DLT_DEVICE); t != NULL;
  257.        t = FindDevice(DLPTR(t->dl_Next),DLT_DEVICE)) {
  258.     dn = (struct DeviceNode *) t;
  259.     /* This is supposed to distinguish a disk device (a task pointer) */
  260.     if (dn->dn_Task) { 
  261.       Write(Output(),STRING(dn->dn_Name), LENGTH(dn->dn_Name));
  262.       PutS(": ");
  263.       for (i=0; i<LENGTH(dn->dn_Name); i++) buf[i] = *(STRING(dn->dn_Name)+i);
  264.       buf[i++] = ':'; buf[i] = '\0';
  265.       l = Lock(buf,ACCESS_READ);
  266.       if (l == NULL) PutS("No disk present\n");
  267.       else {
  268.         Info(l,&info);
  269.         /* Calculate the size in K bytes (add 2 to blocks for) 'reserved' 
  270.          * blocks which is a *bug* in the original info but what the heck.
  271.          */
  272.         a = info.id_NumBlocks;
  273. #ifdef INFO_COMPATIBLE
  274.         size = ((a+2) * 512) >> 10;
  275. #else
  276.         size = (a * info.id_BytesPerBlock ) >> 10;
  277. #endif
  278.         i = cvtnum(buf,size);
  279.     /* The following case statement formats the number properly */
  280.         buf[3] = 'K'; /* defaults to K bytes */
  281.     switch (i) {
  282.       case 1 : buf[2] = buf[0]; 
  283.                    buf[1] = ' '; /* fill in with spaces */
  284.                    buf[0] = ' '; /* fill in with spaces */
  285.            break;
  286.           case 5 : buf[3] = 'M';
  287.       case 2 : buf[2] = buf[1];
  288.                    buf[1] = buf[0];
  289.                    buf[0] = ' ';
  290.            break;
  291.           case 6 : buf[3] = 'M';
  292.       case 3 : break;
  293.       case 4 :
  294.       case 7 : buf[3] = (i == 4) ? 'M' : 'G';
  295.              buf[2] = buf[1];
  296.                   buf[1] = '.';
  297.            break;
  298.       default: buf[0] = 'H'; /* Bigger than 10 Gigabytes */
  299.            buf[1] = 'U';
  300.            buf[2] = 'G';
  301.            buf[3] = 'E';
  302.            break;
  303.         } /* end switch */
  304.         buf[4] = ' '; buf[5] = '\0';
  305.         PutS(buf);
  306.         u = info.id_NumBlocksUsed;
  307.     /* Now build the stats line without sprintf */
  308.         cvtnum(buf,u);
  309.         Pad(buf,7);
  310.         *(buf+7) = ' ';
  311.     cvtnum(buf+8,a-u);
  312.     Pad(buf+8,7);
  313.     *(buf+15) = ' ';
  314.     cvtnum(buf+16,((a-(a-u))*100)/a);
  315.     Pad(buf+16,3);
  316.     *(buf+19) = '%';
  317.     *(buf+20) = ' ';
  318.     cvtnum(buf+21,info.id_NumSoftErrors);
  319.     Pad(buf+21,3);    
  320.         PutS(buf);
  321.         switch (info.id_DiskState) {
  322.           case ID_WRITE_PROTECTED : PutS("  Read Only  ");
  323.                           break;
  324.           case ID_VALIDATING      : PutS("  Validating ");
  325.                       break;
  326.           case ID_VALIDATED       : PutS("  Read/Write ");
  327.                       break;
  328.          default:            PutS("  Strange    ");
  329.                       break;
  330.           } /* state switch */
  331.         t2 = DLPTR(info.id_VolumeNode);
  332.         if (t2 != NULL) Write(Output(),STRING(t2->dl_Name), LENGTH(t2->dl_Name));
  333.         PutS("\n");
  334.         UnLock(l);
  335.       } /* else had a disk in it */
  336.     } /* If it was a disk device */
  337.   } /* For loop */
  338.   PutS("\n");
  339.   myp->pr_WindowPtr = oldwinptr;
  340.   MyExit(RETURN_OK); /* Exit with a status of zero */
  341. }
  342.