(c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice, and 
is provided "as is" without warranty of any kind, either expressed or implied.  
The entire risk as to the use of this information is assumed by the user.



       Programming For Portability and Compatibility

                  By Bill Koester

In this article I will provide some guidelines for making programs
portable between the current Amiga  models, and hopefully any
future models.  The first rule of compatability is: assume nothing.
There can be many variations among models and particular user setups.


MEMORY

This one should be obvious; the amount of memory varies from one
Amiga to another.  Always check for success when allocating memory.
If you need to know beforehand, the Exec AvailMem() function can be
used to provide a  means  of  checking  CHIP,  FAST,  and LARGEST
memory available during program execution.

FONTS

On floppy based systems, the fonts directory is often deleted by
the user to get extra disk space.  The only fonts you can be
guaranteed to have are Topaz60 and Topaz80 which reside in ROM.
If your program can use other fonts, always check for availability first.
Offer access only to those fonts found in the FONTS directory at run time.


DEVICES

Two other files that frequently get deleted are the clipboard.device
and narrator.device.  Check the value returned when you open these devices.
Of course, you should ALWAYS check for errors when calling any system
function!  If you want to be really friendly,  an error message or
requester is a good idea.  Many speech programs won't run because
the narrator.device or translator.library is not present. This is less
mysterious if you inform the user of the problem.


LIBRARIES

As with devices, never assume the availability of a library.  Check 
the value returned by OpenLibrary() calls and notify the user if there
is a problem.  If your program doesn't run and the user doesn't know why,
you will get the blame (even if the user is at fault).


DRIVE UNITS

With the release of the A2000, many disk drive configurations are
now possible.  One configuration often found on the 2000 is one
internal and one external 3.5" drive.  This  configuration  results
in drive names of df0: and df2:,  NOT  df0:  and  df1: as you might
expect.  With the 2000, there will be many more hard drives, too.
Remember  that additional hard disk partitions or drives have their
own entry in the mountlist.  This means the user has free  reign
over choosing the name.  Lots of file requestors allow the user
to pick df0: or df1: (and possibly ram: or dh0:).  Unfortunately,
very few configure themselves dynamically so that the user can
pick ANY drive in the run-time system.

So what's the  solution?  With  this  article  I  have included a
program called getdisks which  searches  the system for the names
of all mounted disk  devices.  The  program  builds a simple Exec
List with named nodes.  The node names correspond to the names of
the mounted disk devices.  Use this program as a subroutine in
your application so you can find out what drive units are available.
For your old applications that can't be changed, try the public domain
program AssignDev on Fish disk #79.






/*********************************************************************/
/*                                                                   */
/*  Program name:  GetDisks (c) 1987 Commodore-Amiga Inc.            */
/*                                                                   */
/*  Function: To find the names of all available disk devices and    */
/*            return them to the user in a simple exec list. The     */
/*            list is made up of named nodes, the "names" being the  */
/*            device name.                                           */
/*                                                                   */
/*  Purpose:  To provide a way to find out the number of drives      */
/*            installed and their names. Provides a method for       */
/*            programs to change themselves dynamically according    */
/*            to a users particular configuration.                   */
/*                                                                   */
/*  Arguments:  None.                                                */
/*                                                                   */
/*  Programer:  Phillip Lindsay                                      */
/*                                                                   */
/*  Revised 24-Aug-87 for Lattice-C Compiler 3.10 (Bill Koester)     */
/*********************************************************************/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>

#ifdef MANX                                  /* Manx includes        */
#include <functions.h>
#include <stdio.h>
#else
#include <lattice/stdio.h>
#endif

extern struct DosLibrary *DOSBase;

/************************************************************************/
/*                                                                      */
/* NAME                                                                 */
/*                                                                      */
/*    btoc                                                              */
/*                                                                      */
/* SYNOPSIS                                                             */
/*                                                                      */
/*    void btoc(bstring);                                               */
/*    char *bstring;                                                    */
/*                                                                      */
/* PURPOSE                                                              */
/*                                                                      */
/*    To convert the BCPL format string pointed to by bstring, to a     */
/*    NULL terminated C string.                                         */
/*                                                                      */
/* INPUTS                                                               */
/*                                                                      */
/*    char *bstring - Pointer to a string in BCPL format with the       */
/*                    length in the first byte.                         */
/*                                                                      */
/* OUTPUTS                                                              */
/*                                                                      */
/*    None.                                                             */
/*                                                                      */
/* RESULTS                                                              */
/*                                                                      */
/*    The BCPL string (pointed to by bstring) is shifted left one byte  */
/*    and the last character is replaced with the NULL terminator       */
/*    according to C string format.                                     */
/*                                                                      */
/************************************************************************/
void btoc(bstring)
char *bstring;
{
 register UBYTE len,count,*cstring; 
 
 cstring = (UBYTE *) bstring;          /* Get pointer to BSTRING     */
 len = cstring[0];                     /* Length is in first byte    */

 for(count=0;count < len;count++)      /* Shift entire string 1 byte */
  cstring[count] = cstring[count+1];   /* to the left                */

 cstring[count] = '\0';                /* Replace last byte of BSTR  */
                                       /* with required NULL byte    */
}

/************************************************************************/
/*                                                                      */
/* NAME                                                                 */
/*                                                                      */
/*    GetNode                                                           */
/*                                                                      */
/* SYNOPSIS                                                             */
/*                                                                      */
/*    struct Node *GetNode(name,type,pri)                               */
/*    char *name;                                                       */
/*    UBYTE type,pri;                                                   */
/*                                                                      */
/* PURPOSE                                                              */
/*                                                                      */
/*    GetNode() will build a node structure for you. It will append     */
/*    memory to the node structure for the node name passed.            */
/*                                                                      */
/*                                                                      */
/* INPUTS                                                               */
/*                                                                      */
/*    char *name  -  Pointer to C string containg node name.            */
/*    UBYTE type  -  Node type.                                         */
/*    UBYTE pri   -  Node priority.                                     */
/*                                                                      */
/* OUTPUTS                                                              */
/*                                                                      */
/*    struct Node *  -  Pointer to allocated and initialized node.      */
/*                      Returns NULL on memory allocation error.        */
/*                                                                      */
/* RESULTS                                                              */
/*                                                                      */
/*    Allocates a block of memory big enough for the Node structure     */
/*    plus the name string. Copies the name string to the end of the    */
/*    block and adjusts ln_Name to point there. Then set ln_Type and    */
/*    ln_Pri. Finally returns a pointer to the start of the memory      */
/*    block which contains the initialized Node structure with the      */
/*    name appended.                                                    */
/*                                                                      */
/************************************************************************/
struct Node *GetNode(name,type,pri)
char *name;
UBYTE type,pri;
{
 register struct Node   *mynode;
 register char          *myname;
 register UBYTE         *mymemory;
 register ULONG         mynamesize;

 /* Size is 0 if no name else length of string plus NULL terminator  */

 mynamesize =( ((ULONG)strlen(name)) ? (ULONG)strlen(name)+1 : 0L );   

 mynode=0; /* make compiler happy about unitialized auto variable */

 mymemory = (UBYTE *) 
  AllocMem((ULONG)sizeof(*mynode)+mynamesize,MEMF_PUBLIC | MEMF_CLEAR); 

 if(!mymemory) return((struct Node *)NULL);
 
 mynode = (struct Node *) mymemory; 
 if(mynamesize)
  {
   myname = (char *) mymemory+(ULONG)sizeof(*mynode);
   strcpy(myname,name);
   mynode->ln_Name = myname;
  }
 mynode->ln_Type = type;
 mynode->ln_Pri  = pri;

 return(mynode);
}

/************************************************************************/
/*                                                                      */
/* NAME                                                                 */
/*                                                                      */
/*    FreeNode                                                          */
/*                                                                      */
/* SYNOPSIS                                                             */
/*                                                                      */
/*    void FreeNode(mynode);                                            */
/*    struct Node *mynode;                                              */
/*                                                                      */
/* PURPOSE                                                              */
/*                                                                      */
/*    This function assumes you used GetNode() for node initialization. */
/*    Will free all memory used by node. Make sure you remove node from */
/*    any list.                                                         */
/*                                                                      */
/* INPUTS                                                               */
/*                                                                      */
/*    struct Node *mynode  -  Pointer to a node initialized by GetNode. */
/*                                                                      */
/* OUTPUTS                                                              */
/*                                                                      */
/*    None.                                                             */
/*                                                                      */
/* RESULTS                                                              */
/*                                                                      */
/*    All memory associated with the node is freed.                     */
/*                                                                      */
/************************************************************************/
void FreeNode(mynode)
struct Node *mynode;
{
 register ULONG mymemsize;

 mymemsize = (ULONG) sizeof(*mynode);
 mymemsize+= ((mynode->ln_Name) ? (ULONG)strlen(mynode->ln_Name)+1 : 0L);
 
 FreeMem(mynode,mymemsize);
}

/************************************************************************/
/*                                                                      */
/* NAME                                                                 */
/*                                                                      */
/*    getdisks                                                          */
/*                                                                      */
/* SYNOPSIS                                                             */
/*                                                                      */
/*    void getdisks(dlist)                                              */
/*    struct List *dlist;                                               */
/*                                                                      */
/* PURPOSE                                                              */
/*                                                                      */
/*    This routine searches the system device list for all the disk     */
/*    device names. For each disk device found it appends a named node  */
/*    to the list pointed to by dlist. The appended node contains the   */
/*    name of the disk device as its name.                              */
/*                                                                      */
/* GLOBALS NEEDED                                                       */
/*                                                                      */
/*    extern struct DosLibrary *DOSBase;                                */
/*                                                                      */
/* INPUTS                                                               */
/*                                                                      */
/*    struct List *dlist;  -  Pointer to your list to be filled.        */
/*                                                                      */
/* OUTPUTS                                                              */
/*                                                                      */
/*    None.                                                             */
/*                                                                      */
/* RESULTS                                                              */
/*                                                                      */
/*    Named nodes are added to your list where the node names           */
/*    correspond to the disk device names currently mounted.            */
/*                                                                      */
/************************************************************************/
void getdisks(dlist)
struct List *dlist;     /* passed a pointer to an initialized exec list */ 
{
 extern struct DosLibrary     *DOSBase;
 struct RootNode              *rnode; 
 struct DosInfo               *dinfo;
 register struct DeviceNode   *dnode;
 register struct Node         *adisk;
 char                         *bname,name[32];

 rnode   = (struct RootNode *)  DOSBase->dl_Root;
 dinfo   = (struct DosInfo  *)  BADDR(rnode->rn_Info);
 
 Forbid();
 for(dnode = (struct DeviceNode *) BADDR(dinfo->di_DevInfo);BADDR(dnode);
    dnode = (struct DeviceNode *) BADDR(dnode->dn_Next))
  {
   if(!dnode->dn_Type && dnode->dn_Task && BADDR(dnode->dn_Name)) 
    {
     bname = (char *) BADDR(dnode->dn_Name);    
     movmem(bname,name,(ULONG)bname[0]+1L);
     btoc(name);
     if((adisk=GetNode(name,0,0))) AddTail(dlist,adisk);    
    }
  }
 Permit(); 
}

/************************************************************************/
/*                                                                      */
/* NAME                                                                 */
/*                                                                      */
/*    freedisks                                                         */
/*                                                                      */
/* SYNOPSIS                                                             */
/*                                                                      */
/*    void freedisks(dlist)                                             */
/*    struct List *dlist;                                               */
/*                                                                      */
/* PURPOSE                                                              */
/*                                                                      */
/*    Will free all nodes in the list pointed to by dlist. Assumes the  */
/*    nodes where initialized with GetNode().                           */
/*                                                                      */
/* INPUTS                                                               */
/*                                                                      */
/*    struct List *dlist;  -  Pointer to list with the nodes you want   */
/*                            freed.                                    */
/*                                                                      */
/* OUTPUTS                                                              */
/*                                                                      */
/*    None.                                                             */
/*                                                                      */
/* RESULTS                                                              */
/*                                                                      */
/*    All nodes in the list pointed to by dlist are returned to the     */
/*    system free memory list.                                          */
/*                                                                      */
/************************************************************************/
void freedisks(dlist)
struct List *dlist;
{
 register struct Node *disk;

 while((disk= (struct Node *)RemTail(dlist)))
  FreeNode(disk);
}

/************************************************************************/
/*                                                                      */
/*                              M A I N                                 */
/*                                                                      */
/************************************************************************/
main()
{
 struct List disks;
 struct Node *disk;
  
 NewList(&disks);   /* Initialize list header */
 getdisks(&disks);   /* Fill list */
 
/* print any devices in list.... if any */
 if((struct List *)disks.lh_TailPred != (struct List *)&disks)
   for(disk = disks.lh_Head;disk->ln_Succ;disk=disk->ln_Succ)
     puts(disk->ln_Name);

 freedisks(&disks);
 return(0);
}  
