1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 3 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General
15    Public License along with this library.  If not, see
16    <http://www.gnu.org/licenses/>. */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <mailutils/sys/list.h>
25 #include <mailutils/sys/iterator.h>
26 #include <mailutils/errno.h>
27 
28 /* Iterator interface */
29 
30 struct list_iterator
31 {
32   mu_list_t list;
33   struct list_data *cur;
34   int backwards; /* true if iterating backwards */
35 };
36 
37 static int
first(void * owner)38 first (void *owner)
39 {
40   struct list_iterator *itr = owner;
41   if (itr->backwards)
42     itr->cur = itr->list->head.prev;
43   else
44     itr->cur = itr->list->head.next;
45   return 0;
46 }
47 
48 static int
next(void * owner)49 next (void *owner)
50 {
51   struct list_iterator *itr = owner;
52   if (itr->backwards)
53     itr->cur = itr->cur->prev;
54   else
55     itr->cur = itr->cur->next;
56   return 0;
57 }
58 
59 static int
getitem(void * owner,void ** pret,const void ** pkey)60 getitem (void *owner, void **pret, const void **pkey)
61 {
62   struct list_iterator *itr = owner;
63   *pret = itr->cur->item;
64   if (pkey)
65     *pkey = NULL;
66   return 0;
67 }
68 
69 static int
finished_p(void * owner)70 finished_p (void *owner)
71 {
72   struct list_iterator *itr = owner;
73   return itr->cur == &itr->list->head;
74 }
75 
76 static int
destroy(mu_iterator_t iterator,void * data)77 destroy (mu_iterator_t iterator, void *data)
78 {
79   struct list_iterator *itr = data;
80   mu_iterator_detach (&itr->list->itr, iterator);
81   free (data);
82   return 0;
83 }
84 
85 static int
delitem(void * owner,void * item)86 delitem (void *owner, void *item)
87 {
88   struct list_iterator *itr = owner;
89   return itr->cur == item ? MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
90 }
91 
92 static int
list_data_dup(void ** ptr,void * owner)93 list_data_dup (void **ptr, void *owner)
94 {
95   *ptr = malloc (sizeof (struct list_iterator));
96   if (*ptr == NULL)
97     return ENOMEM;
98   memcpy (*ptr, owner, sizeof (struct list_iterator));
99   return 0;
100 }
101 
102 static int
list_itrctl(void * owner,enum mu_itrctl_req req,void * arg)103 list_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
104 {
105   struct list_iterator *itr = owner;
106   mu_list_t list = itr->list;
107   struct list_data *ptr;
108 
109   switch (req)
110     {
111     case mu_itrctl_tell:
112       /* Return current position in the object */
113       if (itr->cur == NULL)
114 	return MU_ERR_NOENT;
115       else
116 	{
117 	  size_t count;
118 
119 	  for (count = 0, ptr = list->head.next; ptr != &list->head;
120 	       ptr = ptr->next, count++)
121 	    {
122 	      if (ptr == itr->cur)
123 		{
124 		  *(size_t*)arg = count;
125 		  return 0;
126 		}
127 	    }
128 	  return MU_ERR_NOENT;
129 	}
130       break;
131 
132     case mu_itrctl_delete:
133     case mu_itrctl_delete_nd:
134       /* Delete current element */
135       if (itr->cur == NULL)
136 	return MU_ERR_NOENT;
137       else
138 	{
139 	  struct list_data *prev;
140 
141 	  ptr = itr->cur;
142 	  prev = ptr->prev;
143 
144 	  mu_iterator_delitem (list->itr, ptr);
145 	  prev->next = ptr->next;
146 	  ptr->next->prev = prev;
147 	  if (req == mu_itrctl_delete)
148 	    DESTROY_ITEM (list, ptr);
149 	  free (ptr);
150 	  list->count--;
151 	}
152       break;
153 
154     case mu_itrctl_replace:
155     case mu_itrctl_replace_nd:
156       /* Replace current element */
157       if (itr->cur == NULL)
158 	return MU_ERR_NOENT;
159       if (!arg)
160 	return EINVAL;
161       ptr = itr->cur;
162       if (req == mu_itrctl_replace)
163 	  DESTROY_ITEM (list, ptr);
164       ptr = itr->cur;
165       ptr->item = arg;
166       break;
167 
168     case mu_itrctl_insert:
169       /* Insert new element in the current position */
170       if (itr->cur == NULL)
171 	return MU_ERR_NOENT;
172       if (!arg)
173 	return EINVAL;
174       return _mu_list_insert_item (list, itr->cur, arg, 0);
175 
176     case mu_itrctl_insert_list:
177       /* Insert a list of elements */
178       if (itr->cur == NULL)
179 	return MU_ERR_NOENT;
180       if (!arg)
181 	return EINVAL;
182       else
183 	{
184 	  mu_list_t new_list = arg;
185 	  _mu_list_insert_sublist (list, itr->cur,
186 				   new_list->head.next, new_list->head.prev,
187 				   new_list->count,
188 				   0);
189 	  _mu_list_clear (new_list);
190 	}
191       break;
192 
193     case mu_itrctl_qry_direction:
194       if (!arg)
195 	return EINVAL;
196       else
197 	*(int*)arg = itr->backwards;
198       break;
199 
200     case mu_itrctl_set_direction:
201       if (!arg)
202 	return EINVAL;
203       else
204 	itr->backwards = !!*(int*)arg;
205       break;
206 
207     case mu_itrctl_count:
208       if (!arg)
209 	return EINVAL;
210       return mu_list_count (itr->list, arg);
211 
212     default:
213       return ENOSYS;
214     }
215   return 0;
216 }
217 
218 int
mu_list_get_iterator(mu_list_t list,mu_iterator_t * piterator)219 mu_list_get_iterator (mu_list_t list, mu_iterator_t *piterator)
220 {
221   mu_iterator_t iterator;
222   int status;
223   struct list_iterator *itr;
224 
225   if (!list)
226     return EINVAL;
227 
228   itr = calloc (1, sizeof *itr);
229   if (!itr)
230     return ENOMEM;
231   itr->list = list;
232   itr->cur = NULL;
233 
234   status = mu_iterator_create (&iterator, itr);
235   if (status)
236     {
237       free (itr);
238       return status;
239     }
240 
241   mu_iterator_set_first (iterator, first);
242   mu_iterator_set_next (iterator, next);
243   mu_iterator_set_getitem (iterator, getitem);
244   mu_iterator_set_finished_p (iterator, finished_p);
245   mu_iterator_set_delitem (iterator, delitem);
246   mu_iterator_set_destroy (iterator, destroy);
247   mu_iterator_set_dup (iterator, list_data_dup);
248   mu_iterator_set_itrctl (iterator, list_itrctl);
249 
250   mu_iterator_attach (&list->itr, iterator);
251 
252   *piterator = iterator;
253   return 0;
254 }
255