(patched) gocr-0.44
[swftools.git] / lib / gocr / list.c
diff --git a/lib/gocr/list.c b/lib/gocr/list.c
new file mode 100644 (file)
index 0000000..332d2bd
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+This is a Optical-Character-Recognition program
+Copyright (C) 2000-2006  Joerg Schulenburg
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+ see README for email address
+ ***********************************IMPORTANT*********************************
+ Notes to the developers: read the following notes before using these
+ functions.
+   * Be careful when using for_each_data() recursively and calling list_del.
+    It may mangle with the current[] pointers, and possibly segfault or do an
+    unpredictable or just undesirable behavior. We have been working on a 
+    solution for this problem, and solved some of the biggest problems.
+     In a few words, the problem is this: when you delete a node, it may be
+    the current node of a lower level loop. The current code takes care of
+    access to previous/next elements of the now defunct node. So, if you do
+    something like:
+  
+    for_each_data(l) {
+      for_each_data(l) {
+       list_del(l, header_data);
+       free(header_data);
+      } end_for_each(l);
++     tempnode = list_cur_next(l);
+    } end_for_each(l);
+
+    It will work, even though the current node in the outer loop was deleted.
+    However, if you replace the line marked with + with the following code:
+
+      tempnode = list_next(l, list_get_current(l));
+
+    it will break, since list_get_current is likely to return NULL or garbage,
+    since you deleted header_data().
+     Conclusion: use list_del carefully. The best way to avoid this problem is
+    to not use list_del inside a big stack of loops.
+   * If you have two elements with the same data, the functions will assume 
+    that the first one is the wanted one. Not a bug, a feature. ;-)
+   * avoid calling list_prev and list_next. They are intensive and slow 
+    functions. Keep the result in a variable or, if you need something more,
+    use list_get_element_from_data.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "list.h"
+#include "progress.h"
+
+void list_init( List *l ) {
+  if ( !l )
+     return;
+
+  l->start.next     = &l->stop;
+  l->stop.previous  = &l->start;
+  l->start.previous = l->stop.next = NULL;
+  l->start.data = l->stop.data = NULL;
+  l->current = NULL;
+  l->level = -1;
+  l->n = 0;
+}
+
+/* inserts data before data_after. If data_after == NULL, appends.
+   Returns 1 on error, 0 if OK. */
+int list_ins( List *l, void *data_after, void *data) {
+  Element *e, *after_element;
+
+  /* test arguments */
+  if ( !l || !data )
+    return 1;
+
+  if ( !data_after || !l->n )
+    return list_app(l, data);
+
+  /* get data_after element */
+  if ( !(after_element = list_element_from_data(l, data_after)) )
+    return 1;
+
+  /* alloc a new element */
+  if( !(e = (Element *)malloc(sizeof(Element))) )
+    return 1;
+  e->data     = data;
+  e->next     = after_element;
+  e->previous = after_element->previous;
+  after_element->previous->next = e;
+  after_element->previous       = e;
+  l->n++;
+
+  return 0;
+}
+
+/* appends data to the list. Returns 1 on error, 0 if OK. */
+/* same as list_ins(l,NULL,data) ??? */
+int list_app( List *l, void *data ) {
+  Element *e;
+  
+  if ( !l || !data )
+     return 1;
+  if ( !(e = (Element *)malloc(sizeof(Element))) )
+    return 1;
+  
+  e->data     = data;
+  e->previous = l->stop.previous;
+  e->next     = l->stop.previous->next;
+  l->stop.previous->next = e;
+  l->stop.previous       = e;
+  l->n++;
+  return 0;
+}
+
+/* returns element associated with data. */
+Element *list_element_from_data( List *l, void *data ) {
+  Element *temp;
+
+  if ( !l || !data || !l->n)
+    return NULL;
+
+  temp = l->start.next;
+
+  while ( temp->data != data ) {
+    if ( !temp || temp==&l->stop )
+      return NULL;
+    temp = temp->next;
+  }
+  return temp;
+}
+
+/* deletes (first) element with data from list. User must free data.
+   Returns 0 if OK, 1 on error.
+   This is the internal version, that shouldn't be called usually. Use the
+   list_del() macro instead.
+ */
+int list_del( List *l, void *data ) {
+  Element *temp;
+  int i;
+
+  if (!data) return 1; /* do not delete start or stop element */
+
+  /* find element associated with data */
+  if ( !(temp = list_element_from_data(l, data)) )
+    return 1;
+
+  /* test if the deleted node is current in some nested loop, and fix it. */
+  for ( i = l->level; i >= 0; i-- ) {
+    if ( l->current[i] == temp ) {
+      l->current[i] = temp->previous;
+    }
+  }
+
+  temp->previous->next = temp->next;
+  temp->next->previous = temp->previous;
+  temp->previous = temp->next = NULL; /* mark as freed */
+/*
+  fprintf(stderr,"\n# list_del=%p start=%p stop=%p",temp,&l->start,&l->stop);
+*/
+
+  /* and free stuff */
+  free(temp); /* element pointing to data, fixed mem-leak 0.41 */
+  l->n--;
+  return 0;
+}
+
+/* frees list. See also list_and_data_free() */
+void list_free( List *l ) {
+  Element *temp, *temp2;
+
+  if ( !l || !l->n )
+    return;
+
+  if ( l->current ) {
+    free(l->current);
+  }
+  l->current = NULL;
+
+  temp = l->start.next;
+  while ( temp && temp!=&l->stop) {
+    temp2 = temp->next;
+    free(temp);
+    temp = temp2;
+  }
+  l->start.next    = &l->stop;
+  l->stop.previous = &l->start;
+}
+
+/* setup a new level of for_each */
+int list_higher_level( List *l ) {
+  Element **newcur;
+  
+  if ( !l ) return(1);
+
+  /*
+     Security-check: NULL pointer passed to realloc.
+      ANSI allows this, but it may cause portability problems.
+  */    
+  newcur = (Element **)realloc(l->current, (l->level+2)*sizeof(Element *));
+  if (newcur) {
+    l->current = newcur;
+    l->level++;
+    l->current[l->level] = l->start.next;
+  }
+  g_debug(fprintf(stderr, " level++=%d current[]=%p\n",
+     l->level, l->current);)
+  if ( !newcur  ) {
+    fprintf(stderr, " realloc failed! abort\n"); return(1);
+  }
+  return 0;
+}
+
+void list_lower_level( List *l ) {
+  if ( !l ) 
+    return;
+
+  if (!l->level) {
+    free(l->current); /* calm -lefence */
+    l->current = NULL; /* could be important */
+  } else {
+    l->current = (Element **)realloc(l->current, l->level*sizeof(Element *));
+  }
+  l->level--;
+  g_debug(fprintf(stderr, " level--=%d current[]=%p\n", l->level, 
+      l->current);)
+}
+
+/* returns the next item data */
+void *list_next( List *l, void *data ) {
+  Element *temp;
+
+  if ( !l || !(temp = list_element_from_data(l, data)) )
+    return NULL;
+  if( !temp->next ) return NULL;
+  return (temp->next->data);
+}
+
+/* returns the previous item data */
+void *list_prev( List *l, void *data ) {
+  Element *temp;
+
+  if ( !l || !(temp = list_element_from_data(l, data)) )
+    return NULL;
+  if( !temp->previous ) return NULL;
+  return (temp->previous->data);
+}
+
+/* Similar to qsort. Sorts list, using the (*compare) function, which is 
+  provided by the user. The comparison function must return an integer less 
+  than, equal to, or greater than zero if the first argument is considered to 
+  be respectively less than, equal to, or greater than the second. 
+  Uses the bubble sort algorithm.
+  */
+void list_sort( List *l, int (*compare)(const void *, const void *) ) {
+  Element *temp, *prev;
+  int i, sorted;
+  progress_counter_t *pc = NULL;
+
+  if ( !l )
+    return;
+
+  /* start progress meter, sorting is slow for huge number of elements */
+  /* l->n is the worst case, real time is less or equal estimated time */
+  pc = open_progress(l->n,"list_sort");
+  
+  for (i = 0; i < l->n; i++ ) {
+    sorted = 1; /* Flag for early break */
+    for ( temp = l->start.next->next;
+          temp != NULL && temp != &l->stop; temp = temp->next ) {
+      if ( temp->previous == &l->start ) continue;
+      if ( compare((const void *)temp->previous->data,
+                   (const void *)temp->data) > 0 ) {
+
+        sorted = 0;  /* rest flag */
+       /* swap with the previous node */
+       prev = temp->previous;
+       prev->previous->next = temp;
+        temp->next->previous = prev;
+       temp->previous = prev->previous;
+       prev->next     = temp->next;
+       prev->previous = temp;
+       temp->next     = prev;
+       /* and make sure the node in the for loop is correct */
+       temp = prev;
+
+#ifdef SLOWER_BUT_KEEP_BY_NOW
+/* this is a slower version, but guaranteed to work */
+        void *data;
+
+       data = temp->data;
+       prev = temp->previous;
+       list_del(l, data);
+       list_ins(l, prev->data, data);
+       temp = prev;
+#endif
+      }
+    }
+    if (sorted) break;
+    progress(i,pc); /* progress meter */
+  }
+
+  close_progress(pc);
+  g_debug(fprintf(stderr, "list_sort()\n");)
+}
+
+/* calls free_data() for each data in list l, 
+ * before free list with list_free() */
+int list_and_data_free( List *l, void (*free_data)(void *data)) {
+  void *data;
+
+  if ( !l ) return 0;
+  if ( !free_data ) return 1;
+
+  for_each_data(l) {
+    if ((data = list_get_current(l)))
+      free_data(data);
+  } end_for_each(l);
+  
+  list_free(l);
+
+  g_debug(fprintf(stderr, "list_and_data_free()\n");)
+
+  return 0;
+}
+