home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 106 / EnigmaAmiga106CD.iso / software / sviluppo / ahisrc / ahi / support.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-04-22  |  17.8 KB  |  619 lines

  1. /*
  2.      AHI - The AHI preferences program
  3.      Copyright (C) 1996-1999 Martin Blom <martin@blom.org>
  4.      
  5.      This program is free software; you can redistribute it and/or
  6.      modify it under the terms of the GNU General Public License
  7.      as published by the Free Software Foundation; either version 2
  8.      of the License, or (at your option) any later version.
  9.      
  10.      This program is distributed in the hope that it will be useful,
  11.      but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.      GNU General Public License for more details.
  14.      
  15.      You should have received a copy of the GNU General Public License
  16.      along with this program; if not, write to the Free Software
  17.      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19.  
  20. /* $Id: support.c,v 4.6 1999/04/22 19:41:25 lcs Exp $
  21.  * $Log: support.c,v $
  22.  * Revision 4.6  1999/04/22 19:41:25  lcs
  23.  * Removed SAS/C smakefile.
  24.  * I had the copyright date screwed up: Changed to 1996-1999 (which is only
  25.  * partly correct, but still better than 1997-1999....)
  26.  *
  27.  * Revision 4.5  1999/03/28 22:30:51  lcs
  28.  * AHI is now GPL/LGPL software.
  29.  * Make target bindist work correctly when using a separate build directory.
  30.  * Small first steps towards a WarpOS PPC version.
  31.  *
  32.  * Revision 4.4  1999/01/09 23:14:16  lcs
  33.  * Switched from SAS/C to gcc
  34.  *
  35.  * Revision 4.3  1997/06/24 21:49:49  lcs
  36.  * Increased version number to match the catalogs (4.5).
  37.  *
  38.  * Revision 4.2  1997/04/07 01:36:51  lcs
  39.  * Localized it, bug fixes
  40.  *
  41.  */
  42.  
  43.  
  44. #include <config.h>
  45. #include <CompilerSpecific.h>
  46.  
  47. #include <devices/ahi.h>
  48. #include <exec/memory.h>
  49. #include <prefs/prefhdr.h>
  50. #include <workbench/workbench.h>
  51. #include <proto/ahi.h>
  52. #include <proto/dos.h>
  53. #include <proto/exec.h>
  54. #include <proto/icon.h>
  55. #include <proto/iffparse.h>
  56. #include <proto/utility.h>
  57. #include <math.h>
  58. #include <stdio.h>
  59. #include <string.h>
  60.  
  61. #include "ahi.h"
  62. #include "ahiprefs_Cat.h"
  63. #include "support.h"
  64.  
  65. static BOOL AddUnit(struct List *, int);
  66. static void FillUnitName(struct UnitNode *);
  67.  
  68. struct AHIGlobalPrefs     globalprefs;
  69.  
  70. struct Library           *LocaleBase= NULL;
  71. struct Library           *AHIBase   = NULL;
  72. static struct MsgPort    *AHImp     = NULL;
  73. static struct AHIRequest *AHIio     = NULL;
  74. static BYTE               AHIDevice = -1;
  75.  
  76.  
  77. static char deftoolname[] = {"AHI"};
  78.  
  79. static UWORD DiskI1Data[] =
  80. {
  81. /* Plane */
  82.   0x0000,0x0000,0x0004,0x0000,0x0000,0x0000,0x0001,0x0000,
  83.   0x0000,0x07ff,0x8000,0x4000,0x0000,0x1800,0x6000,0x1000,
  84.   0x0000,0x20fc,0x1000,0x0800,0x0000,0x4102,0x0800,0x0c00,
  85.   0x0000,0x4082,0x0800,0x0c00,0x0000,0x4082,0x0800,0x0c00,
  86.   0x0000,0x2104,0x0800,0x0c00,0x0000,0x1e18,0x1000,0x0c00,
  87.   0x0000,0x0060,0x2000,0x0c00,0x0000,0x0080,0xc000,0x0c00,
  88.   0x0000,0x0103,0x0000,0x0c00,0x0000,0x021c,0x0000,0x0c00,
  89.   0x0000,0x0108,0x0000,0x0c00,0x0000,0x00f0,0x0000,0x0c00,
  90.   0x0000,0x0108,0x0000,0x0c00,0x0000,0x0108,0x0000,0x0c00,
  91.   0x4000,0x00f0,0x0000,0x0c00,0x1000,0x0000,0x0000,0x0c00,
  92.   0x0400,0x0000,0x0000,0x0c00,0x01ff,0xffff,0xffff,0xfc00,
  93. /* Plane */
  94.   0xffff,0xffff,0xfff8,0x0000,0xd555,0x5555,0x5556,0x0000,
  95.   0xd555,0x5000,0x5555,0x8000,0xd555,0x47ff,0x9555,0x6000,
  96.   0xd555,0x5f03,0xe555,0x5000,0xd555,0x3e55,0xf555,0x5000,
  97.   0xd555,0x3f55,0xf555,0x5000,0xd555,0x3f55,0xf555,0x5000,
  98.   0xd555,0x5e53,0xf555,0x5000,0xd555,0x4147,0xe555,0x5000,
  99.   0xd555,0x551f,0xd555,0x5000,0xd555,0x557f,0x1555,0x5000,
  100.   0xd555,0x54fc,0x5555,0x5000,0xd555,0x55e1,0x5555,0x5000,
  101.   0xd555,0x54f5,0x5555,0x5000,0xd555,0x5505,0x5555,0x5000,
  102.   0xd555,0x54f5,0x5555,0x5000,0xd555,0x54f5,0x5555,0x5000,
  103.   0x3555,0x5505,0x5555,0x5000,0x0d55,0x5555,0x5555,0x5000,
  104.   0x0355,0x5555,0x5555,0x5000,0x0000,0x0000,0x0000,0x0000
  105.  
  106. };
  107.  
  108. static struct Image image1 =
  109. {
  110.     0,0,
  111.     54,22,2,
  112.     DiskI1Data,
  113.     0x0004,0x0000,
  114.     NULL
  115. };
  116.  
  117. static char *toolTypes[] = {
  118.   "ACTION=USE",
  119.   NULL
  120. };
  121.  
  122. static struct DiskObject projIcon = {
  123.   WB_DISKMAGIC,                   /* Magic Number */
  124.   WB_DISKVERSION,                 /* Version */
  125.   {                               /* Embedded Gadget Structure */
  126.     NULL,                         /* Next Gadget Pointer */
  127.     97,12,52,23,                  /* Left,Top,Width,Height */
  128.     GADGIMAGE|GADGHBOX,           /* Flags */
  129.     GADGIMMEDIATE|RELVERIFY,      /* Activation Flags */
  130.     BOOLGADGET,                   /* Gadget Type */
  131.     (APTR)&image1,                /* Render Image */
  132.     NULL,                         /* Select Image */
  133.     NULL,                         /* Gadget Text */
  134.     NULL,                         /* Mutual Exclude */
  135.     NULL,                         /* Special Info */
  136.     0,                            /* Gadget ID */
  137.     NULL                          /* User Data */
  138.   },  
  139.   WBPROJECT,                      /* Icon Type */
  140.   deftoolname,                    /* Default Tool */
  141.   toolTypes,                      /* Tool Type Array */
  142.   NO_ICON_POSITION,               /* Current X */
  143.   NO_ICON_POSITION,               /* Current Y */
  144.   NULL,                           /* Drawer Structure */
  145.   NULL,                           /* Tool Window */
  146.   4096                            /* Stack Size */
  147. };
  148.  
  149.  
  150. /******************************************************************************
  151. **** Call to open ahi.device etc. *********************************************
  152. ******************************************************************************/
  153.  
  154. BOOL Initialize(void) {
  155.   LocaleBase = OpenLibrary("locale.library", 38);
  156.  
  157.   OpenahiprefsCatalog();
  158.  
  159.   AHImp=CreateMsgPort();
  160.  
  161.   if( AHImp != NULL ) {
  162.     AHIio = (struct AHIRequest *)CreateIORequest(
  163.         AHImp,sizeof(struct AHIRequest));
  164.  
  165.     if( AHIio != NULL ) {
  166.       AHIio->ahir_Version = 4;
  167.       AHIDevice = OpenDevice(AHINAME,AHI_NO_UNIT,(struct IORequest *)AHIio,NULL);
  168.       if(AHIDevice == 0) {
  169.         AHIBase   = (struct Library *)AHIio->ahir_Std.io_Device;
  170.         return TRUE;
  171.       }
  172.     }
  173.   }
  174.  
  175.   Printf((char *) msgTextNoOpen, (ULONG) "ahi.device", 4);
  176.   Printf("\n");
  177.   return FALSE;
  178. }
  179.  
  180. /******************************************************************************
  181. **** Call to close ahi.device etc. ********************************************
  182. ******************************************************************************/
  183.  
  184. void CleanUp(void) {
  185.   if(!AHIDevice)
  186.     CloseDevice((struct IORequest *)AHIio);
  187.   DeleteIORequest((struct IORequest *)AHIio);
  188.   DeleteMsgPort(AHImp);
  189.  
  190.   AHIBase = NULL; AHImp = NULL; AHIio = NULL; AHIDevice = -1;
  191.  
  192.   CloseahiprefsCatalog();
  193.  
  194.   CloseLibrary(LocaleBase);
  195.   LocaleBase = NULL;
  196. }
  197.  
  198.  
  199. /***** Local function to create a Unit structure  ****************************/
  200.  
  201. static BOOL AddUnit(struct List *list, int unit) {
  202.   struct UnitNode *u;
  203.  
  204.   u = AllocVec(sizeof(struct UnitNode), MEMF_CLEAR);
  205.  
  206.   if(u == NULL) {
  207.     return FALSE;
  208.   }
  209.  
  210.   u->prefs.ahiup_Unit           = unit;
  211.   u->prefs.ahiup_Channels       = 1;
  212.   u->prefs.ahiup_AudioMode      = AHI_BestAudioID(AHIDB_Realtime, TRUE, TAG_DONE);
  213.   u->prefs.ahiup_Frequency      = 8000;
  214.   u->prefs.ahiup_MonitorVolume  = 0x00000;
  215.   u->prefs.ahiup_InputGain      = 0x10000;
  216.   u->prefs.ahiup_OutputVolume   = 0x10000;
  217.   u->prefs.ahiup_Input          = 0;
  218.   u->prefs.ahiup_Output         = 0;
  219.  
  220.   FillUnitName(u);
  221.  
  222.   u->node.ln_Pri = -unit;
  223.   Enqueue(list, (struct Node *) u);
  224.  
  225.   return TRUE;
  226. }
  227.  
  228. /***** Local function to create a Unit name **********************************/
  229.  
  230. static void FillUnitName(struct UnitNode *u) {
  231.   if(u->prefs.ahiup_Unit != AHI_NO_UNIT) {
  232.     sprintf((char *) &u->name, msgUnitDevice, u->prefs.ahiup_Unit);
  233.   }
  234.   else {
  235.     sprintf((char *) &u->name, msgUnitMusic);
  236.   }
  237.   u->node.ln_Name = (char *) &u->name;
  238. }
  239.  
  240. /******************************************************************************
  241. **** Returns a list with all avalable units, fills globalprefs ****************
  242. ******************************************************************************/
  243.  
  244. #define UNITNODES 4
  245.  
  246. // Call with name == NULL to get defaults
  247.  
  248. struct List *GetUnits(char *name) {
  249.   struct List *list;
  250.   struct IFFHandle *iff;
  251.   BOOL devnodes[UNITNODES] = { FALSE, FALSE, FALSE, FALSE } ;
  252.   BOOL lownode = FALSE;
  253.   int i;
  254.  
  255.   globalprefs.ahigp_MaxCPU = (90 << 16) / 100;
  256.  
  257.   list = AllocVec(sizeof(struct List), MEMF_CLEAR);
  258.   
  259.   if(list) {
  260.     NewList(list);
  261.     
  262.     if(name && (iff = AllocIFF())) {
  263.       iff->iff_Stream = Open(name, MODE_OLDFILE);
  264.       if(iff->iff_Stream) {
  265.         InitIFFasDOS(iff);
  266.         if(!OpenIFF(iff, IFFF_READ)) {
  267.           if(!(PropChunk      (iff, ID_PREF, ID_AHIG) ||
  268.                CollectionChunk(iff, ID_PREF, ID_AHIU) ||
  269.                StopOnExit     (iff, ID_PREF, ID_FORM))) {
  270.             if(ParseIFF(iff, IFFPARSE_SCAN) == IFFERR_EOC) {
  271.               struct StoredProperty *global = FindProp(iff, ID_PREF, ID_AHIG);
  272.               struct CollectionItem *ci = FindCollection(iff, ID_PREF, ID_AHIU);
  273.  
  274.               if(global != NULL) {
  275.                 CopyMem(global->sp_Data, &globalprefs, 
  276.                     min( sizeof(struct AHIGlobalPrefs), global->sp_Size ));
  277.               }
  278.  
  279.               while(ci) {
  280.                 struct AHIUnitPrefs *p = ci->ci_Data;
  281.                 struct UnitNode     *u;
  282.  
  283.                 u = AllocVec(sizeof(struct UnitNode), MEMF_CLEAR);
  284.                 if(u == NULL)
  285.                   break;
  286.                 CopyMem(p, &u->prefs, 
  287.                     min( sizeof(struct AHIUnitPrefs), ci->ci_Size ));
  288.  
  289.                 FillUnitName(u);
  290.                 
  291.                 u->node.ln_Pri = -(u->prefs.ahiup_Unit);
  292.                 Enqueue(list, (struct Node *) u);
  293.                 
  294.                 if(u->prefs.ahiup_Unit == AHI_NO_UNIT) {
  295.                   lownode = TRUE;
  296.                 }
  297.                 else if(u->prefs.ahiup_Unit < UNITNODES) {
  298.                   devnodes[u->prefs.ahiup_Unit] = TRUE;
  299.                 }
  300.                 
  301.                 ci=ci->ci_Next;
  302.               }
  303.             }
  304.           }
  305.           CloseIFF(iff);
  306.         }
  307.         Close(iff->iff_Stream);
  308.       }
  309.       FreeIFF(iff);
  310.     }
  311.  
  312.  
  313.     // Fill up to lowlevel + UNITNODES device nodes, if not found in prefs file
  314.  
  315.     if(!lownode) AddUnit(list, AHI_NO_UNIT);
  316.     for(i = 0; i < UNITNODES; i++) {
  317.       if(!devnodes[i]) AddUnit(list, i);
  318.     }
  319.  
  320.   }
  321.  
  322.   return list;
  323. }
  324.  
  325. /******************************************************************************
  326. **** Returns a list with all available modes **********************************
  327. ******************************************************************************/
  328.  
  329. struct List *GetModes(struct AHIUnitPrefs *prefs) {
  330.   struct List *list;
  331.  
  332.   list = AllocVec(sizeof(struct List), MEMF_CLEAR);
  333.   
  334.   if(list) {
  335.     ULONG id = AHI_NextAudioID(AHI_INVALID_ID);
  336.  
  337.     NewList(list);
  338.  
  339.     while(id != AHI_INVALID_ID) {
  340.       struct ModeNode *t;
  341.       struct Node     *node;
  342.       
  343.       t = AllocVec( sizeof(struct ModeNode), MEMF_CLEAR);
  344.  
  345.       if( t != NULL ) {
  346.         LONG realtime;
  347.  
  348.         t->node.ln_Name = t->name;
  349.         t->ID = id;
  350.         
  351.         realtime = FALSE;
  352.         
  353.         AHI_GetAudioAttrs(id, NULL,
  354.             AHIDB_BufferLen,  80,
  355.             AHIDB_Name,       (ULONG) t->node.ln_Name,
  356.             AHIDB_Realtime,   (ULONG) &realtime,
  357.             TAG_DONE);
  358.  
  359.         if((prefs->ahiup_Unit == AHI_NO_UNIT) || realtime ) {
  360.           // Insert node alphabetically
  361.           for(node = list->lh_Head;
  362.               node->ln_Succ;
  363.               node = node->ln_Succ) {
  364.             if(Stricmp(t->node.ln_Name,node->ln_Name) < 0)
  365.               break;
  366.           }
  367.           Insert(list, (struct Node *) t, node->ln_Pred);
  368.         }
  369.         else {
  370.           FreeVec(t);
  371.         }
  372.       }
  373.       id = AHI_NextAudioID(id);
  374.     }
  375.   }
  376.   return list;
  377. }
  378.  
  379. /******************************************************************************
  380. **** Creates a char* array from a list ****************************************
  381. ******************************************************************************/
  382.  
  383. char **List2Array(struct List *list) {
  384.   char **strings, **rstrings;
  385.   int i;
  386.   struct Node *n;
  387.  
  388.   for(n = list->lh_Head, i = 0; n->ln_Succ; n = n->ln_Succ) {
  389.     i++;
  390.   }
  391.  
  392.   strings = AllocVec(sizeof(char *) * (i + 1), MEMF_CLEAR);
  393.   rstrings = strings;
  394.  
  395.   if(strings) {
  396.     for(n = list->lh_Head; n->ln_Succ; n = n->ln_Succ) {
  397.       *strings++ = n->ln_Name;
  398.     }
  399.     *strings = NULL;
  400.   }
  401.   return rstrings;
  402. }
  403.  
  404. /******************************************************************************
  405. **** Returns a char* array with inputs names **********************************
  406. ******************************************************************************/
  407.  
  408. char **GetInputs(ULONG id) {
  409.   char **strings, **rstrings;
  410.   LONG inputs = 0, i;
  411.  
  412.  
  413.   AHI_GetAudioAttrs(id, NULL,
  414.       AHIDB_Inputs, (ULONG) &inputs,
  415.       TAG_DONE);
  416.  
  417.   strings = AllocVec(sizeof(char *) * (inputs + 1) + (32 * inputs), MEMF_CLEAR);
  418.   rstrings = strings;
  419.  
  420.   if(strings) {
  421.     char *string = (char *) &strings[inputs + 1];
  422.  
  423.     for(i = 0; i < inputs; i++) {
  424.       if(AHI_GetAudioAttrs(id, NULL,
  425.           AHIDB_BufferLen, 32,
  426.           AHIDB_InputArg,  i,
  427.           AHIDB_Input,     (ULONG) string,
  428.           TAG_DONE)) {
  429.         *strings++ = string;
  430.         while(*string++);
  431.       }
  432.     }
  433.     *strings = NULL;
  434.   }
  435.   return rstrings;
  436. }
  437.  
  438. /******************************************************************************
  439. **** Returns a char* array with outputs names *********************************
  440. ******************************************************************************/
  441.  
  442. char **GetOutputs(ULONG id) {
  443.   char **strings, **rstrings;
  444.   LONG outputs = 0, i;
  445.  
  446.  
  447.   AHI_GetAudioAttrs(id, NULL,
  448.       AHIDB_Outputs, (ULONG) &outputs,
  449.       TAG_DONE);
  450.  
  451.   strings = AllocVec(sizeof(char *) * (outputs + 1) + (32 * outputs), MEMF_CLEAR);
  452.   rstrings = strings;
  453.   
  454.   if(strings) {
  455.     char *string = (char *) &strings[outputs + 1];
  456.  
  457.     for(i = 0; i < outputs; i++) {
  458.       if(AHI_GetAudioAttrs(id, NULL,
  459.           AHIDB_BufferLen, 32,
  460.           AHIDB_OutputArg,  i,
  461.           AHIDB_Output,     (ULONG) string,
  462.           TAG_DONE)) {
  463.         *strings++ = string;
  464.         while(*string++);
  465.       }
  466.     }
  467.     *strings = NULL;
  468.   }
  469.   return rstrings;
  470. }
  471.  
  472. /******************************************************************************
  473. **** Saves all units and globalprefs ******************************************
  474. ******************************************************************************/
  475.  
  476. BOOL SaveSettings(char *name, struct List *list) {
  477.   struct IFFHandle  *iff;
  478.   struct PrefHeader  header = { 0, 0, 0 };
  479.   struct Node       *n;
  480.   BOOL               success = FALSE;
  481.  
  482.   if(name && (iff = AllocIFF())) {
  483.     iff->iff_Stream = Open(name, MODE_NEWFILE);
  484.     if(iff->iff_Stream) {
  485.       InitIFFasDOS(iff);
  486.       if(!OpenIFF(iff, IFFF_WRITE)) {
  487.         if(! PushChunk(iff, ID_PREF, ID_FORM, IFFSIZE_UNKNOWN)) {
  488.  
  489.           success = TRUE;
  490.  
  491.           // Prefs header
  492.           if(! PushChunk(iff, ID_PREF, ID_PRHD, sizeof header)) {
  493.             WriteChunkBytes(iff, &header, sizeof header);
  494.             PopChunk(iff);
  495.           }
  496.           else success = FALSE;
  497.  
  498.           // Global prefs
  499.           if(! PushChunk(iff, ID_PREF, ID_AHIG, sizeof globalprefs)) {
  500.             WriteChunkBytes(iff, &globalprefs, sizeof globalprefs);
  501.             PopChunk(iff);
  502.           }
  503.           else success = FALSE;
  504.  
  505.           // Units
  506.           if(list != NULL) {    
  507.             for(n = list->lh_Head; n->ln_Succ; n = n->ln_Succ) {
  508.               if(! PushChunk(iff, ID_PREF, ID_AHIU, sizeof(struct AHIUnitPrefs))) {
  509.                 WriteChunkBytes(iff, &((struct UnitNode *) n)->prefs,
  510.                     sizeof(struct AHIUnitPrefs));
  511.                 PopChunk(iff);
  512.               }
  513.               else success = FALSE;
  514.             }
  515.           }
  516.         }
  517.         CloseIFF(iff);
  518.       }
  519.       Close(iff->iff_Stream);
  520.     }
  521.     FreeIFF(iff);
  522.   }
  523.   return success;
  524. }
  525.  
  526.  
  527. /******************************************************************************
  528. **** Write a project icon to disk *********************************************
  529. ******************************************************************************/
  530.  
  531. BOOL WriteIcon(char *name) {
  532.   struct DiskObject *dobj;
  533.   char *olddeftool;
  534.   char **oldtooltypes;
  535.   BOOL success = FALSE;
  536.  
  537.   /* Use the already present icon */
  538.   
  539.   dobj=GetDiskObject(name);
  540.   
  541.   if( dobj != NULL ) {
  542.     oldtooltypes = dobj->do_ToolTypes;
  543.     olddeftool = dobj->do_DefaultTool;
  544.  
  545.     dobj->do_ToolTypes = toolTypes;
  546.     dobj->do_DefaultTool = deftoolname;
  547.  
  548.     success = PutDiskObject(name,dobj);
  549.  
  550.     /* we must restore the original pointers before freeing */
  551.     dobj->do_ToolTypes = oldtooltypes;
  552.     dobj->do_DefaultTool = olddeftool;
  553.     FreeDiskObject(dobj);
  554.   }
  555.  
  556.   /* Try the user's default prefs icon */
  557.   if((! success) && (dobj=GetDiskObject("ENV:Sys/def_Pref"))) {
  558.     oldtooltypes = dobj->do_ToolTypes;
  559.     olddeftool = dobj->do_DefaultTool;
  560.  
  561.     dobj->do_ToolTypes = toolTypes;
  562.     dobj->do_DefaultTool = deftoolname;
  563.  
  564.     success = PutDiskObject(name,dobj);
  565.  
  566.     /* we must restore the original pointers before freeing */
  567.     dobj->do_ToolTypes = oldtooltypes;
  568.     dobj->do_DefaultTool = olddeftool;
  569.     FreeDiskObject(dobj);
  570.   }
  571.  
  572.   /* Else, put our default icon */
  573.   if(! success) {
  574.     success = PutDiskObject(name,&projIcon);
  575.   }
  576.  
  577.   return success;
  578. }
  579.  
  580.  
  581. /******************************************************************************
  582. **** Frees a list and all nodes ***********************************************
  583. ******************************************************************************/
  584.  
  585. void FreeList(struct List *list) {
  586.   struct Node *n;
  587.  
  588.   if(list == NULL)
  589.     return;
  590.  
  591.   for( n = RemHead(list);
  592.        n != NULL;
  593.        n = RemHead(list) )
  594.   {
  595.     FreeVec(n);
  596.   }
  597.  
  598.   FreeVec(list);
  599. }
  600.  
  601. /******************************************************************************
  602. **** Returns the index:th node in a list  *************************************
  603. ******************************************************************************/
  604.  
  605. struct Node *GetNode(int index, struct List *list) {
  606.   struct Node *n;
  607.  
  608.   if(list == NULL)
  609.     return NULL;
  610.     
  611.   for(n = list->lh_Head; n->ln_Succ; n = n->ln_Succ) {
  612.     if(index == 0) {
  613.       break;
  614.     }
  615.     index--;
  616.   }
  617.   return n;
  618. }
  619.