1 /*
2  *
3  *   Gutenprint list functions.  A doubly-linked list implementation,
4  *   with callbacks for freeing, sorting, and retrieving nodes by name
5  *   or long name.
6  *
7  *   Copyright 2002 Roger Leigh (rleigh@debian.org)
8  *
9  *   This program is free software; you can redistribute it and/or modify it
10  *   under the terms of the GNU General Public License as published by the Free
11  *   Software Foundation; either version 2 of the License, or (at your option)
12  *   any later version.
13  *
14  *   This program is distributed in the hope that it will be useful, but
15  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17  *   for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This file must include only standard C header files.  The core code must
25  * compile on generic platforms that don't support glib, gimp, etc.
26  */
27 
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 #include <gutenprint/gutenprint.h>
33 #include "gutenprint-internal.h"
34 #include <gutenprint/gutenprint-intl-internal.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 
39 /** The internal representation of an stp_list_item_t list node. */
40 struct stp_list_item
41 {
42   void *data;			/*!< Data		*/
43   struct stp_list_item *prev;	/*!< Previous node	*/
44   struct stp_list_item *next;	/*!< Next node		*/
45 };
46 
47 /** The internal representation of an stp_list_t list. */
48 struct stp_list
49 {
50   struct stp_list_item *start;			/*!< Start node				*/
51   struct stp_list_item *end;			/*!< End node				*/
52   struct stp_list_item *index_cache_node;	/*!< Cached node (for index)		*/
53   char *name_cache;				/*!< Cached name			*/
54   struct stp_list_item *name_cache_node;	/*!< Cached node (for name)		*/
55   char *long_name_cache;			/*!< Cached long name			*/
56   struct stp_list_item *long_name_cache_node;	/*!< Cached node (for long name)	*/
57   stp_node_freefunc freefunc;			/*!< Callback to free node data		*/
58   stp_node_copyfunc copyfunc;			/*!< Callback to copy node		*/
59   stp_node_namefunc namefunc;			/*!< Callback to get node name		*/
60   stp_node_namefunc long_namefunc;		/*!< Callback to get node long name	*/
61   stp_node_sortfunc sortfunc;			/*!< Callback to compare (sort) nodes	*/
62   int index_cache;				/*!< Cached node index			*/
63   int length;					/*!< Number of nodes			*/
64 };
65 
66 /**
67  * Cache a list node by its short name.
68  * @param list the list to use.
69  * @param name the short name.
70  * @param cache the node to cache.
71  */
72 static void
set_name_cache(stp_list_t * list,const char * name,stp_list_item_t * cache)73 set_name_cache(stp_list_t *list,
74 	       const char *name,
75 	       stp_list_item_t *cache)
76 {
77   if (list->name_cache)
78     stp_free(list->name_cache);
79   list->name_cache = NULL;
80   if (name)
81     list->name_cache = stp_strdup(name);
82   list->name_cache_node = cache;
83 }
84 
85 /**
86  * Cache a list node by its long name.
87  * @param list the list to use.
88  * @param long_name the long name.
89  * @param cache the node to cache.
90  */
91 static void
set_long_name_cache(stp_list_t * list,const char * long_name,stp_list_item_t * cache)92 set_long_name_cache(stp_list_t *list,
93 		    const char *long_name,
94 		    stp_list_item_t *cache)
95 {
96   if (list->long_name_cache)
97     stp_free(list->long_name_cache);
98   list->long_name_cache = NULL;
99   if (long_name)
100     list->long_name_cache = stp_strdup(long_name);
101   list->long_name_cache_node = cache;
102 }
103 
104 /**
105  * Clear cached nodes.
106  * @param list the list to use.
107  */
108 static inline void
clear_cache(stp_list_t * list)109 clear_cache(stp_list_t *list)
110 {
111   list->index_cache = 0;
112   list->index_cache_node = NULL;
113   set_name_cache(list, NULL, NULL);
114   set_long_name_cache(list, NULL, NULL);
115 }
116 
117 void
stp_list_node_free_data(void * item)118 stp_list_node_free_data (void *item)
119 {
120   stp_free(item);
121   stp_deprintf(STP_DBG_LIST, "stp_list_node_free_data destructor\n");
122 }
123 
124 /** Check the validity of a list. */
125 #define check_list(List) STPI_ASSERT(List != NULL, NULL)
126 
127 /* List head functions.
128  * These functions operate on the list as a whole, and not the
129  * individual nodes in a list. */
130 
131 /* create a new list */
132 stp_list_t *
stp_list_create(void)133 stp_list_create(void)
134 {
135   stp_list_t *list =
136     stp_malloc(sizeof(stp_list_t));
137 
138   /* initialise an empty list */
139   list->index_cache = 0;
140   list->length = 0;
141   list->start = NULL;
142   list->end = NULL;
143   list->index_cache_node = NULL;
144   list->freefunc = NULL;
145   list->namefunc = NULL;
146   list->long_namefunc = NULL;
147   list->sortfunc = NULL;
148   list->copyfunc = NULL;
149   list->name_cache = NULL;
150   list->name_cache_node = NULL;
151   list->long_name_cache = NULL;
152   list->long_name_cache_node = NULL;
153 
154   stp_deprintf(STP_DBG_LIST, "stp_list_head constructor\n");
155   return list;
156 }
157 
158 stp_list_t *
stp_list_copy(const stp_list_t * list)159 stp_list_copy(const stp_list_t *list)
160 {
161   stp_list_t *ret;
162   stp_node_copyfunc copyfunc = stp_list_get_copyfunc(list);
163   stp_list_item_t *item = list->start;
164 
165   check_list(list);
166 
167   ret = stp_list_create();
168   stp_list_set_copyfunc(ret, stp_list_get_copyfunc(list));
169   /* If we use default (shallow) copy, we can't free the elements of it */
170   if (stp_list_get_copyfunc(list))
171     stp_list_set_freefunc(ret, stp_list_get_freefunc(list));
172   stp_list_set_namefunc(ret, stp_list_get_namefunc(list));
173   stp_list_set_long_namefunc(ret, stp_list_get_long_namefunc(list));
174   stp_list_set_sortfunc(ret, stp_list_get_sortfunc(list));
175   while (item)
176     {
177       void *data = item->data;
178       if (copyfunc)
179 	stp_list_item_create (ret, NULL, (*copyfunc)(data));
180       else
181 	stp_list_item_create(ret, NULL, data);
182       item = stp_list_item_next(item);
183     }
184   return ret;
185 }
186 
187 /* free a list, freeing all child nodes first */
188 int
stp_list_destroy(stp_list_t * list)189 stp_list_destroy(stp_list_t *list)
190 {
191   stp_list_item_t *cur;
192   stp_list_item_t *next;
193 
194   check_list(list);
195   clear_cache(list);
196   cur = list->start;
197   while(cur)
198     {
199       next = cur->next;
200       stp_list_item_destroy(list, cur);
201       cur = next;
202     }
203   stp_deprintf(STP_DBG_LIST, "stp_list_head destructor\n");
204   stp_free(list);
205 
206   return 0;
207 }
208 
209 int
stp_list_get_length(const stp_list_t * list)210 stp_list_get_length(const stp_list_t *list)
211 {
212   check_list(list);
213   return list->length;
214 }
215 
216 /* find a node */
217 
218 /* get the first node in the list */
219 
220 stp_list_item_t *
stp_list_get_start(const stp_list_t * list)221 stp_list_get_start(const stp_list_t *list)
222 {
223   return list->start;
224 }
225 
226 /* get the last node in the list */
227 
228 stp_list_item_t *
stp_list_get_end(const stp_list_t * list)229 stp_list_get_end(const stp_list_t *list)
230 {
231   return list->end;
232 }
233 
234 static inline stp_list_t *
deconst_list(const stp_list_t * list)235 deconst_list(const stp_list_t *list)
236 {
237   return (stp_list_t *) stpi_cast_safe(list);
238 }
239 
240 /* get the node by its place in the list */
241 stp_list_item_t *
stp_list_get_item_by_index(const stp_list_t * list,int idx)242 stp_list_get_item_by_index(const stp_list_t *list, int idx)
243 {
244   stp_list_item_t *node = NULL;
245   stp_list_t *ulist = deconst_list(list);
246   int i; /* current index */
247   int d = 0; /* direction of list traversal, 0=forward */
248   int c = 0; /* use cache? */
249   check_list(list);
250 
251   if (idx >= list->length)
252     return NULL;
253 
254   /*
255    * Optimize the most likely cases of looking for the same, next,
256    * or previou item
257    * */
258   if (ulist->index_cache_node)
259     {
260       if (idx == ulist->index_cache)
261 	return ulist->index_cache_node;
262       else if (idx == ulist->index_cache + 1)
263 	{
264 	  ulist->index_cache = idx;
265 	  ulist->index_cache_node = ulist->index_cache_node->next;
266 	  return ulist->index_cache_node;
267 	}
268       else if (idx == ulist->index_cache - 1)
269 	{
270 	  ulist->index_cache = idx;
271 	  ulist->index_cache_node = ulist->index_cache_node->prev;
272 	  return ulist->index_cache_node;
273 	}
274     }
275   /*
276    * See if using the cache is worthwhile.  If the desired index is closer
277    * to the cached index than it is to the start or end, it will be faster
278    * to start from the cached element.
279    *
280    * Otherwise, decide which direction is best to start from.
281    */
282   if (list->index_cache)
283     {
284       if (idx < (list->length/2))
285 	{
286 	  if (idx > abs(idx - list->index_cache))
287 	    c = 1;
288 	  else
289 	    d = 0;
290 	}
291       else
292 	{
293 	  if (list->length - 1 - idx >
294 	      abs (list->length - 1 - idx - list->index_cache))
295 	    c = 1;
296 	  else
297 	    d = 1;
298 	}
299     }
300 
301   /* use the cached index and node */
302   if (c)
303     {
304       if (idx > list->index_cache) /* forward */
305 	d = 0;
306       else /* backward */
307 	d = 1;
308       i = list->index_cache;
309       node = list->index_cache_node;
310     }
311   else /* start from one end of the list */
312     {
313       if (d)
314 	{
315 	  i = list->length - 1;
316 	  node = list->end;
317 	}
318       else
319 	{
320 	  i = 0;
321 	  node = list->start;
322 	}
323     }
324 
325   while (node && i != idx)
326     {
327       if (d)
328 	{
329 	  i--;
330 	  node = node->prev;
331 	}
332       else
333 	{
334 	  i++;
335 	  node = node->next;
336 	}
337     }
338 
339   /* update cache */
340   ulist->index_cache = i;
341   ulist->index_cache_node = node;
342 
343   return node;
344 }
345 
346 /**
347  * Find an item in a list by its name.
348  * This internal helper is not optimised to use any caching.
349  * @param list the list to use.
350  * @param name the name to find.
351  * @returns a pointer to the list item, or NULL if the name is
352  * invalid or the list is empty.
353  */
354 static stp_list_item_t *
stp_list_get_item_by_name_internal(const stp_list_t * list,const char * name)355 stp_list_get_item_by_name_internal(const stp_list_t *list, const char *name)
356 {
357   stp_list_item_t *node = list->start;
358   while (node && strcmp(name, list->namefunc(node->data)))
359     {
360       node = node->next;
361     }
362   return node;
363 }
364 
365 /* get the first node with name; requires a callback function to
366    read data */
367 stp_list_item_t *
stp_list_get_item_by_name(const stp_list_t * list,const char * name)368 stp_list_get_item_by_name(const stp_list_t *list, const char *name)
369 {
370   stp_list_item_t *node = NULL;
371   stp_list_t *ulist = deconst_list(list);
372   check_list(list);
373 
374   if (!list->namefunc || !name)
375     return NULL;
376 
377   if (list->name_cache && list->name_cache_node)
378     {
379       const char *new_name;
380       node = list->name_cache_node;
381       /* Is this the item we've cached? */
382       if (strcmp(name, list->name_cache) == 0 &&
383 	  strcmp(name, list->namefunc(node->data)) == 0)
384 	return node;
385 
386       /* If not, check the next item in case we're searching the list */
387       node = node->next;
388       if (node)
389 	{
390 	  new_name = list->namefunc(node->data);
391 	  if (strcmp(name, new_name) == 0)
392 	    {
393 	      set_name_cache(ulist, new_name, node);
394 	      return node;
395 	    }
396 	}
397       /* If not, check the index cache */
398       node = list->index_cache_node;
399       if (node)
400 	{
401 	  new_name = list->namefunc(node->data);
402 	  if (strcmp(name, new_name) == 0)
403 	    {
404 	      set_name_cache(ulist, new_name, node);
405 	      return node;
406 	    }
407 	}
408     }
409 
410   node = stp_list_get_item_by_name_internal(list, name);
411 
412   if (node)
413     set_name_cache(ulist, name, node);
414 
415   return node;
416 }
417 
418 
419 /**
420  * Find an item in a list by its long name.
421  * This internal helper is not optimised to use any caching.
422  * @param list the list to use.
423  * @param long_name the long name to find.
424  * @returns a pointer to the list item, or NULL if the long name is
425  * invalid or the list is empty.
426  */
427 static stp_list_item_t *
stp_list_get_item_by_long_name_internal(const stp_list_t * list,const char * long_name)428 stp_list_get_item_by_long_name_internal(const stp_list_t *list,
429 					 const char *long_name)
430 {
431   stp_list_item_t *node = list->start;
432   while (node && strcmp(long_name, list->long_namefunc(node->data)))
433     {
434       node = node->next;
435     }
436   return node;
437 }
438 
439 /* get the first node with long_name; requires a callack function to
440    read data */
441 stp_list_item_t *
stp_list_get_item_by_long_name(const stp_list_t * list,const char * long_name)442 stp_list_get_item_by_long_name(const stp_list_t *list, const char *long_name)
443 {
444   stp_list_item_t *node = NULL;
445   stp_list_t *ulist = deconst_list(list);
446   check_list(list);
447 
448   if (!list->long_namefunc || !long_name)
449     return NULL;
450 
451   if (list->long_name_cache && list->long_name_cache_node)
452     {
453       const char *new_long_name;
454       node = list->long_name_cache_node;
455       /* Is this the item we've cached? */
456       if (strcmp(long_name, list->long_name_cache) == 0 &&
457 	  strcmp(long_name, list->long_namefunc(node->data)) == 0)
458 	return node;
459 
460       /* If not, check the next item in case we're searching the list */
461       node = node->next;
462       if (node)
463 	{
464 	  new_long_name = list->long_namefunc(node->data);
465 	  if (strcmp(long_name, new_long_name) == 0)
466 	    {
467 	      set_long_name_cache(ulist, new_long_name, node);
468 	      return node;
469 	    }
470 	}
471       /* If not, check the index cache */
472       node = list->index_cache_node;
473       if (node)
474 	{
475 	  new_long_name = list->long_namefunc(node->data);
476 	  if (strcmp(long_name, new_long_name) == 0)
477 	    {
478 	      set_long_name_cache(ulist, new_long_name, node);
479 	      return node;
480 	    }
481 	}
482     }
483 
484   node = stp_list_get_item_by_long_name_internal(list, long_name);
485 
486   if (node)
487     set_long_name_cache(ulist, long_name, node);
488 
489   return node;
490 }
491 
492 
493 /* callback for freeing data */
494 void
stp_list_set_freefunc(stp_list_t * list,stp_node_freefunc freefunc)495 stp_list_set_freefunc(stp_list_t *list, stp_node_freefunc freefunc)
496 {
497   check_list(list);
498   list->freefunc = freefunc;
499 }
500 
501 stp_node_freefunc
stp_list_get_freefunc(const stp_list_t * list)502 stp_list_get_freefunc(const stp_list_t *list)
503 {
504   check_list(list);
505   return list->freefunc;
506 }
507 
508 /* callback for copying data */
509 void
stp_list_set_copyfunc(stp_list_t * list,stp_node_copyfunc copyfunc)510 stp_list_set_copyfunc(stp_list_t *list, stp_node_copyfunc copyfunc)
511 {
512   check_list(list);
513   list->copyfunc = copyfunc;
514 }
515 
516 stp_node_copyfunc
stp_list_get_copyfunc(const stp_list_t * list)517 stp_list_get_copyfunc(const stp_list_t *list)
518 {
519   check_list(list);
520   return list->copyfunc;
521 }
522 
523 /* callback for getting data name */
524 void
stp_list_set_namefunc(stp_list_t * list,stp_node_namefunc namefunc)525 stp_list_set_namefunc(stp_list_t *list, stp_node_namefunc namefunc)
526 {
527   check_list(list);
528   list->namefunc = namefunc;
529 }
530 
531 stp_node_namefunc
stp_list_get_namefunc(const stp_list_t * list)532 stp_list_get_namefunc(const stp_list_t *list)
533 {
534   check_list(list);
535   return list->namefunc;
536 }
537 
538 /* callback for getting data long_name */
539 void
stp_list_set_long_namefunc(stp_list_t * list,stp_node_namefunc long_namefunc)540 stp_list_set_long_namefunc(stp_list_t *list, stp_node_namefunc long_namefunc)
541 {
542   check_list(list);
543   list->long_namefunc = long_namefunc;
544 }
545 
546 stp_node_namefunc
stp_list_get_long_namefunc(const stp_list_t * list)547 stp_list_get_long_namefunc(const stp_list_t *list)
548 {
549   check_list(list);
550   return list->long_namefunc;
551 }
552 
553 /* callback for sorting nodes */
554 void
stp_list_set_sortfunc(stp_list_t * list,stp_node_sortfunc sortfunc)555 stp_list_set_sortfunc(stp_list_t *list, stp_node_sortfunc sortfunc)
556 {
557   check_list(list);
558   list->sortfunc = sortfunc;
559 }
560 
561 stp_node_sortfunc
stp_list_get_sortfunc(const stp_list_t * list)562 stp_list_get_sortfunc(const stp_list_t *list)
563 {
564   check_list(list);
565   return list->sortfunc;
566 }
567 
568 
569 /* list item functions */
570 
571 /* these functions operate on individual nodes in a list */
572 
573 /*
574  * create a new node in list, before next (may be null e.g. if sorting
575  * next is calculated automatically, else defaults to end).  Must be
576  * initialised with data (null nodes are disallowed).  The
577  * stp_list_item_t type can not exist unless it is associated with an
578  * stp_list_t list head.
579  */
580 int
stp_list_item_create(stp_list_t * list,stp_list_item_t * next,const void * data)581 stp_list_item_create(stp_list_t *list,
582 		     stp_list_item_t *next,
583 		     const void *data)
584 {
585   stp_list_item_t *ln; /* list node to add */
586   stp_list_item_t *lnn; /* list node next */
587 
588   check_list(list);
589 
590   clear_cache(list);
591 
592   ln = stp_malloc(sizeof(stp_list_item_t));
593   ln->prev = ln->next = NULL;
594 
595   if (data)
596     ln->data = stpi_cast_safe(data);
597   else
598     {
599       stp_free(ln);
600       return 1;
601     }
602 
603   if (list->sortfunc)
604     {
605       /* set np to the previous node (before the insertion */
606       lnn = list->end;
607       while (lnn)
608 	{
609 	  if (list->sortfunc(lnn->data, ln->data) <= 0)
610 	    break;
611 	  lnn = lnn->prev;
612 	}
613     }
614   else
615     lnn = next;
616 
617   /* got lnp; now insert the new ln */
618 
619   /* set next */
620   ln->next = lnn;
621 
622   if (!ln->prev) /* insert at start of list */
623     {
624       if (list->start) /* list not empty */
625 	ln->prev = list->end;
626       else
627 	list->start = ln;
628       list->end = ln;
629     }
630 
631   /* set prev (already set if at start of list) */
632 
633   if (!ln->prev && ln->next) /* insert at end of list */
634     ln->prev = ln->next->prev;
635 
636   if (list->start == ln->next) /* prev was old end */
637     {
638       list->start = ln;
639     }
640 
641   /* set next->prev */
642   if (ln->next)
643     ln->next->prev = ln;
644 
645   /* set prev->next */
646   if (ln->prev)
647     ln->prev->next = ln;
648 
649   /* increment reference count */
650   list->length++;
651 
652   stp_deprintf(STP_DBG_LIST, "stp_list_node constructor\n");
653   return 0;
654 }
655 
656 /* remove a node from list */
657 int
stp_list_item_destroy(stp_list_t * list,stp_list_item_t * item)658 stp_list_item_destroy(stp_list_t *list, stp_list_item_t *item)
659 {
660   check_list(list);
661 
662   clear_cache(list);
663   /* decrement reference count */
664   list->length--;
665 
666   if (list->freefunc)
667     list->freefunc((void *) item->data);
668   if (item->prev)
669     item->prev->next = item->next;
670   else
671     list->start = item->next;
672   if (item->next)
673     item->next->prev = item->prev;
674   else
675     list->end = item->prev;
676   stp_free(item);
677 
678   stp_deprintf(STP_DBG_LIST, "stp_list_node destructor\n");
679   return 0;
680 }
681 
682 /* get previous node, but don't update the cache */
683 stp_list_item_t *
stp_list_item_prev(const stp_list_item_t * item)684 stp_list_item_prev(const stp_list_item_t *item)
685 {
686   return item->prev;
687 }
688 
689 /* get next node, but don't update the cache */
690 stp_list_item_t *
stp_list_item_next(const stp_list_item_t * item)691 stp_list_item_next(const stp_list_item_t *item)
692 {
693   return item->next;
694 }
695 
696 /* get data for node */
697 void *
stp_list_item_get_data(const stp_list_item_t * item)698 stp_list_item_get_data(const stp_list_item_t *item)
699 {
700   return item->data;
701 }
702 
703 /* set data for node */
704 int
stp_list_item_set_data(stp_list_item_t * item,void * data)705 stp_list_item_set_data(stp_list_item_t *item, void *data)
706 {
707   if (data)
708     {
709       item->data = data;
710       return 0;
711     }
712   return 1; /* return error if data was NULL */
713 }
714