Main Page   Compound List   File List   Compound Members   File Members  

list.c

00001 /*
00002 GOCR Copyright (C) 2000  Joerg Schulenburg Joerg.Schulenburg@physik.uni-magdeburg.de 
00003 GOCR API Copyright (C) 2001 Bruno Barberi Gnecco <brunobg@sourceforge.net>
00004 
00005 This program is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU General Public License
00007 as published by the Free Software Foundation; either version 2
00008 of the License, or (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU General Public License for more details.
00014 
00015 You should have received a copy of the GNU General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00018 */
00019 
00020 /***********************************IMPORTANT*********************************
00021  Notes to the developers: read the following notes before using these
00022  functions.
00023    * Be careful when using for_each_data() recursively and calling list_del.
00024     It may mangle with the current[] pointers, and possibly segfault or do an
00025     unpredictable or just undesirable behavior. We have been working on a 
00026     solution for this problem, and solved some of the biggest problems.
00027      In a few words, the problem is this: when you delete a node, it may be
00028     the current node of a lower level loop. The current code takes care of
00029     access to previous/next elements of the now defunct node. So, if you do
00030     something like:
00031   
00032     for_each_data(l) {
00033       for_each_data(l) {
00034         list_del(l, header_data);
00035         free(header_data);
00036       } end_for_each(l);
00037 +     tempnode = list_cur_next(l);
00038     } end_for_each(l);
00039 
00040     It will work, even though the current node in the outer loop was deleted.
00041     However, if you replace the line marked with + with the following code:
00042 
00043       tempnode = list_next(l, list_get_current(l));
00044 
00045     it will break, since list_get_current is likely to return NULL or garbage,
00046     since you deleted header_data().
00047      Conclusion: use list_del carefully. The best way to avoid this problem is
00048     to not use list_del inside a big stack of loops.
00049    * If you have two elements with the same data, the functions will assume 
00050     that the first one is the wanted one. Not a bug, a feature. ;-)
00051    * avoid calling list_prev and list_next. They are intensive and slow 
00052     functions. Keep the result in a variable or, if you need something more,
00053     use list_get_element_from_data.
00054 
00055  */
00056 
00057 #include <stdio.h>
00058 #include <stdlib.h>
00059 #include "list.h"
00060 #include "_gocr.h"
00061 
00062 void list_init( List *l ) {
00063   if ( !l )
00064      return;
00065 
00066   l->header = l->tail = NULL;
00067   l->current = NULL;
00068   l->fix = NULL;
00069   l->level = -1;
00070   l->n = 0;
00071 }
00072 
00073 /* appends data to the list. Returns 1 on error, 0 if OK. */
00074 int list_app( List *l, void *data ) {
00075   Element *e;
00076   
00077   if ( !l || !data )
00078      return 1;
00079   if ( !(e = (Element *)malloc(sizeof(Element))) )
00080     return 1;
00081   
00082   e->data = data;
00083   if ( !l->header ) {
00084     l->header = l->tail = e;
00085     l->n = 1;
00086     e->previous = e->next = NULL;
00087     return 0;
00088   }
00089 
00090   l->tail->next = e;
00091   e->previous = l->tail;
00092   l->tail = e;
00093   e->next = NULL;
00094   l->n++;
00095   return 0;
00096 }
00097 
00098 /* inserts data before data_after. If data_after == NULL, appends.
00099    Returns 1 on error, 0 if OK. */
00100 int list_ins( List *l, void *data_after, void *data) {
00101   Element *e, *after_element;
00102 
00103   /* test arguments */
00104   if ( !l || !data )
00105     return 1;
00106 
00107   if ( !data_after || !l->header )
00108     return list_app(l, data);
00109 
00110   /* alloc a new element */
00111   if( !(e = (Element *)malloc(sizeof(Element))) )
00112     return 1;
00113   e->data = data;
00114 
00115   /* get data_after element */
00116   if ( !(after_element = list_element_from_data(l, data_after)) )
00117     return 1;
00118 
00119   e->next = after_element;
00120   e->previous = after_element->previous;
00121   if ( after_element->previous ) /* i.e., if after_element != list->header */
00122     after_element->previous->next = e;
00123   else /* update list->header */
00124     l->header = e;
00125   after_element->previous = e;
00126   l->n++;
00127 
00128   return 0;
00129 }
00130 
00131 /* returns element associated with data. */
00132 Element *list_element_from_data( List *l, void *data ) {
00133   Element *temp;
00134 
00135   if ( !l || !data )
00136     return NULL;
00137 
00138   if ( !(temp = l->header) )
00139     return NULL;
00140 
00141   while ( temp->data != data ) {
00142     temp = temp->next;
00143     if ( !temp )
00144       return NULL;
00145   }
00146   return temp;
00147 }
00148 
00149 /* deletes (first) element with data from list. User must free data.
00150    Returns 0 if OK, 1 on error.
00151    This is the internal version, that shouldn't be called usually. Use the
00152    list_del() macro instead.*/
00153 int list_del( List *l, void *data ) {
00154   Element *temp;
00155   int i;
00156 
00157   /* find element associated with data */
00158   if ( !(temp = list_element_from_data(l, data)) )
00159     return 1;
00160 
00161   /* test if the deleted node is current in some nested loop, and fix it. */
00162   for ( i = l->level; i >= 0; i-- ) {
00163     if ( l->current[i] == temp ) {
00164       l->fix[i] = temp;
00165     }
00166   }
00167 
00168   /* fix previous */
00169   if ( temp == l->header ) {
00170     l->header = temp->next;
00171     l->header->previous = NULL;
00172   }
00173   else
00174     temp->previous->next = temp->next;
00175 
00176   /* fix next */
00177   if ( temp == l->tail ) {
00178     l->tail = temp->previous;
00179     l->tail->next = NULL;
00180   }
00181   else
00182     temp->next->previous = temp->previous;
00183 
00184   /* and free stuff */
00185   l->n--;
00186   return 0;
00187 }
00188 
00189 /* frees list. Should we receive a pointer to a function that frees data? */
00190 void list_free( List *l ) {
00191   Element *temp, *temp2;
00192 
00193   if ( !l )
00194     return;
00195   if ( !l->header )
00196     return;
00197 
00198   if ( l->current ) {
00199     free(l->current);
00200   }
00201   l->current = NULL;
00202 
00203   if ( l->fix ) {
00204     free(l->fix);
00205   }
00206   l->fix = NULL;
00207 
00208   temp = l->header;
00209   while ( temp ) {
00210     temp2 = temp->next;
00211     free(temp);
00212     temp = temp2;
00213   }
00214   l->header = l->tail = NULL;
00215 }
00216 
00217 /* setup a new level of for_each */
00218 int list_higher_level( List *l ) {
00219   Element **newcur;
00220   Element **newfix;
00221   
00222   if ( !l || !l->header ) 
00223     return 1;
00224     
00225   newcur = (Element **)realloc(l->current, (l->level+1)*sizeof(Element *));
00226   newfix = (Element **)realloc(l->fix    , (l->level+1)*sizeof(Element *));
00227   if ( !newcur || !newfix ) { /* doesn't blow everything */
00228 /*    _gocr_debug(1, fprintf(_data.error, " realloc failed! level++=%d current[]=%p fix[]=%p\n", 
00229         l->level, l->current, l->fix);) */
00230     return 1;
00231   }
00232   l->current = newcur;
00233   l->fix = newfix;
00234   l->level++;
00235   l->current[l->level] = l->header;
00236   l->fix[l->level] = NULL;
00237 /*  _gocr_debug(3, fprintf(_data.error, " level++=%d current[]=%p fix[]=%p\n", 
00238       l->level, l->current, l->fix);) */
00239   return 0;
00240 }
00241 
00242 void list_lower_level( List *l ) {
00243   if ( !l ) 
00244     return;
00245 
00246   l->current = (Element **)realloc(l->current, l->level*sizeof(Element *));
00247   l->fix     = (Element **)realloc(l->fix    , l->level*sizeof(Element *));
00248   l->level--;
00249 /*  _gocr_debug(3, fprintf(_data.error, " level--=%d current[]=%p fix[]=%p\n",
00250       l->level, l->current, l->fix);) */
00251 }
00252 
00253 /* returns the next item data */
00254 void *list_next( List *l, void *data ) {
00255   Element *temp;
00256 
00257   if ( !l || !(temp = list_element_from_data(l, data)) )
00258     return NULL;
00259   if( !temp->next ) return NULL;
00260   return (temp->next->data);
00261 }
00262 
00263 /* returns the previous item data */
00264 void *list_prev( List *l, void *data ) {
00265   Element *temp;
00266 
00267   if ( !l || !(temp = list_element_from_data(l, data)) )
00268     return NULL;
00269   if( !temp->previous ) return NULL;
00270   return (temp->previous->data);
00271 }
00272 
00273 /* Similar to qsort. Sorts list, using the (*compare) function, which is 
00274   provided by the user. The comparison function must return an integer less 
00275   than, equal to, or greater than zero if the first argument is considered to 
00276   be respectively less than, equal to, or greater than the second. 
00277   Uses the bubble sort algorithm.
00278   */
00279 void list_sort( List *l, int (*compare)(const void *, const void *) ) {
00280   Element *temp, *prev;
00281   int i;
00282 
00283   if ( !l )
00284     return;
00285 
00286   for (i = 0; i < l->n; i++ ) {
00287     for ( temp = l->header->next; temp != NULL; temp = temp->next ) {
00288       if ( compare((const void *)temp->previous->data, (const void *)temp->data) > 0 ) {
00289 
00290         /* swap with the previous node */
00291         prev = temp->previous;
00292         
00293         if ( prev->previous )
00294           prev->previous->next = temp;
00295         else /* update header */
00296           l->header = temp;
00297 
00298         temp->previous = prev->previous;
00299         prev->previous = temp;
00300         prev->next = temp->next;
00301         if ( temp->next )
00302           temp->next->previous = prev;
00303         else /* update tail */
00304           l->tail = prev;
00305         temp->next = prev;
00306 
00307         /* and make sure the node in the for loop is correct */
00308         temp = prev;
00309 
00310 #ifdef SLOWER_BUT_KEEP_BY_NOW
00311 /* this is a slower version, but guaranteed to work */
00312         void *data;
00313 
00314         data = temp->data;
00315         prev = temp->previous;
00316         list_del(l, data);
00317         list_ins(l, prev->data, data);
00318         temp = prev;
00319 #endif
00320       }
00321     }
00322   }
00323 
00324 /*  _gocr_debug(3, fprintf(_data.error, "list_sort()\n");) */
00325 }

Generated at Thu Mar 1 10:05:32 2001 for GOCR API by doxygen1.2.2 written by Dimitri van Heesch, © 1997-2000