335 lines
9.1 KiB
Text
335 lines
9.1 KiB
Text
|
/*
|
||
|
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;
|
||
|
}
|
||
|
|