--- /dev/null
+/*
+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;
+}
+