Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/mutt/slist.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * A separated list of strings
4
 *
5
 * @authors
6
 * Copyright (C) 2018-2019 Richard Russon <rich@flatcap.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page mutt_slist A separated list of strings
25
 *
26
 * A separated list of strings
27
 */
28
29
#include "config.h"
30
#include <stddef.h>
31
#include "slist.h"
32
#include "buffer.h"
33
#include "list.h"
34
#include "memory.h"
35
#include "queue.h"
36
#include "string2.h"
37
38
/**
39
 * slist_new - Create a new string list
40
 * @param flags Flag to set, e.g. #SLIST_SEP_COMMA
41
 * @retval ptr New string list
42
 */
43
struct Slist *slist_new(uint32_t flags)
44
0
{
45
0
  struct Slist *list = mutt_mem_calloc(1, sizeof(*list));
46
0
  list->flags = flags;
47
0
  STAILQ_INIT(&list->head);
48
49
0
  return list;
50
0
}
51
52
/**
53
 * slist_add_list - Add a list to another list
54
 * @param list String list to add to
55
 * @param add  String list to add
56
 * @retval ptr Modified list
57
 */
58
struct Slist *slist_add_list(struct Slist *list, const struct Slist *add)
59
0
{
60
0
  if (!add)
61
0
    return list;
62
0
  if (!list)
63
0
    return slist_dup(add);
64
65
0
  struct ListNode *np = NULL;
66
0
  STAILQ_FOREACH(np, &add->head, entries)
67
0
  {
68
0
    mutt_list_insert_tail(&list->head, mutt_str_dup((char *) np->data));
69
0
    list->count++;
70
0
  }
71
0
  return list;
72
0
}
73
74
/**
75
 * slist_add_string - Add a string to a list
76
 * @param list List to modify
77
 * @param str  String to add
78
 * @retval ptr Modified list
79
 */
80
struct Slist *slist_add_string(struct Slist *list, const char *str)
81
0
{
82
0
  if (!list)
83
0
    return NULL;
84
85
0
  if (str && (str[0] == '\0'))
86
0
    str = NULL;
87
88
0
  if (!str && !(list->flags & SLIST_ALLOW_EMPTY))
89
0
    return list;
90
91
0
  mutt_list_insert_tail(&list->head, mutt_str_dup(str));
92
0
  list->count++;
93
94
0
  return list;
95
0
}
96
97
/**
98
 * slist_compare - Compare two string lists
99
 * @param a First list
100
 * @param b Second list
101
 * @retval true They are identical
102
 */
103
bool slist_compare(const struct Slist *a, const struct Slist *b)
104
0
{
105
0
  if (!a && !b) /* both empty */
106
0
    return true;
107
0
  if (!a ^ !b) /* one is empty, but not the other */
108
0
    return false;
109
0
  if (a->count != b->count)
110
0
    return false;
111
112
0
  return mutt_list_compare(&a->head, &b->head);
113
0
}
114
115
/**
116
 * slist_dup - Create a copy of an Slist object
117
 * @param list Slist to duplicate
118
 * @retval ptr New Slist object
119
 */
120
struct Slist *slist_dup(const struct Slist *list)
121
0
{
122
0
  if (!list)
123
0
    return NULL;
124
125
0
  struct Slist *list_new = slist_new(list->flags);
126
127
0
  struct ListNode *np = NULL;
128
0
  STAILQ_FOREACH(np, &list->head, entries)
129
0
  {
130
0
    mutt_list_insert_tail(&list_new->head, mutt_str_dup(np->data));
131
0
  }
132
0
  list_new->count = list->count;
133
0
  return list_new;
134
0
}
135
136
/**
137
 * slist_empty - Empty out an Slist object
138
 * @param list Slist to empty
139
 * @retval ptr New Slist object
140
 */
141
struct Slist *slist_empty(struct Slist **list)
142
0
{
143
0
  if (!list || !*list)
144
0
    return NULL;
145
146
0
  mutt_list_free(&(*list)->head);
147
148
0
  if ((*list)->flags & SLIST_ALLOW_EMPTY)
149
0
  {
150
0
    (*list)->count = 0;
151
0
    return *list;
152
0
  }
153
154
0
  FREE(list);
155
0
  return NULL;
156
0
}
157
158
/**
159
 * slist_free - Free an Slist object
160
 * @param ptr Slist to free
161
 */
162
void slist_free(struct Slist **ptr)
163
29.8k
{
164
29.8k
  if (!ptr || !*ptr)
165
0
    return;
166
167
29.8k
  struct Slist *slist = *ptr;
168
29.8k
  mutt_list_free(&slist->head);
169
170
29.8k
  FREE(ptr);
171
29.8k
}
172
173
/**
174
 * slist_is_empty - Is the slist empty?
175
 * @param list List to check
176
 * @retval true List is empty
177
 */
178
bool slist_is_empty(const struct Slist *list)
179
2.41M
{
180
2.41M
  if (!list)
181
2.41M
    return true;
182
183
0
  return list->count == 0;
184
2.41M
}
185
186
/**
187
 * slist_is_member - Is a string a member of a list?
188
 * @param list List to modify
189
 * @param str  String to find
190
 * @retval true String is in the list
191
 */
192
bool slist_is_member(const struct Slist *list, const char *str)
193
169k
{
194
169k
  if (!list)
195
0
    return false;
196
197
169k
  if (!str && !(list->flags & SLIST_ALLOW_EMPTY))
198
0
    return false;
199
200
169k
  struct ListNode *np = NULL;
201
169k
  STAILQ_FOREACH(np, &list->head, entries)
202
437k
  {
203
437k
    if (mutt_str_equal(np->data, str))
204
9.94k
      return true;
205
437k
  }
206
159k
  return false;
207
169k
}
208
209
/**
210
 * slist_parse - Parse a list of strings into a list
211
 * @param str   String of strings
212
 * @param flags Flags, e.g. #SLIST_ALLOW_EMPTY
213
 * @retval ptr New Slist object
214
 */
215
struct Slist *slist_parse(const char *str, uint32_t flags)
216
29.8k
{
217
29.8k
  char *src = mutt_str_dup(str);
218
29.8k
  if (!src && !(flags & SLIST_ALLOW_EMPTY))
219
0
    return NULL;
220
221
29.8k
  char sep = ' ';
222
29.8k
  if ((flags & SLIST_SEP_MASK) == SLIST_SEP_COMMA)
223
9.94k
    sep = ',';
224
19.8k
  else if ((flags & SLIST_SEP_MASK) == SLIST_SEP_COLON)
225
19.8k
    sep = ':';
226
227
29.8k
  struct Slist *list = mutt_mem_calloc(1, sizeof(struct Slist));
228
29.8k
  list->flags = flags;
229
29.8k
  STAILQ_INIT(&list->head);
230
231
29.8k
  if (!src)
232
0
    return list;
233
234
29.8k
  char *start = src;
235
1.92M
  for (char *p = start; *p; p++)
236
1.89M
  {
237
1.89M
    if ((p[0] == '\\') && (p[1] != '\0'))
238
0
    {
239
0
      p++;
240
0
      continue;
241
0
    }
242
243
1.89M
    if (p[0] == sep)
244
139k
    {
245
139k
      p[0] = '\0';
246
139k
      if (slist_is_member(list, start))
247
9.94k
      {
248
9.94k
        start = p + 1;
249
9.94k
        continue;
250
9.94k
      }
251
129k
      mutt_list_insert_tail(&list->head, mutt_str_dup(start));
252
129k
      list->count++;
253
129k
      start = p + 1;
254
129k
    }
255
1.89M
  }
256
257
29.8k
  if (!slist_is_member(list, start))
258
29.8k
  {
259
29.8k
    mutt_list_insert_tail(&list->head, mutt_str_dup(start));
260
29.8k
    list->count++;
261
29.8k
  }
262
263
29.8k
  FREE(&src);
264
29.8k
  return list;
265
29.8k
}
266
267
/**
268
 * slist_remove_string - Remove a string from a list
269
 * @param list List to modify
270
 * @param str  String to remove
271
 * @retval ptr Modified list
272
 */
273
struct Slist *slist_remove_string(struct Slist *list, const char *str)
274
0
{
275
0
  if (!list)
276
0
    return NULL;
277
0
  if (!str && !(list->flags & SLIST_ALLOW_EMPTY))
278
0
    return list;
279
280
0
  struct ListNode *prev = NULL;
281
0
  struct ListNode *np = NULL;
282
0
  struct ListNode *tmp = NULL;
283
0
  STAILQ_FOREACH_SAFE(np, &list->head, entries, tmp)
284
0
  {
285
0
    if (mutt_str_equal(np->data, str))
286
0
    {
287
0
      if (prev)
288
0
        STAILQ_REMOVE_AFTER(&list->head, prev, entries);
289
0
      else
290
0
        STAILQ_REMOVE_HEAD(&list->head, entries);
291
0
      FREE(&np->data);
292
0
      FREE(&np);
293
0
      list->count--;
294
0
      break;
295
0
    }
296
0
    prev = np;
297
0
  }
298
0
  return list;
299
0
}
300
301
/**
302
 * slist_to_buffer - Export an Slist to a Buffer
303
 * @param list List to export
304
 * @param buf  Buffer for the results
305
 * @retval num Number of strings written to Buffer
306
 */
307
int slist_to_buffer(const struct Slist *list, struct Buffer *buf)
308
0
{
309
0
  if (!list || !buf || (list->count == 0))
310
0
    return 0;
311
312
0
  struct ListNode *np = NULL;
313
0
  STAILQ_FOREACH(np, &list->head, entries)
314
0
  {
315
0
    buf_addstr(buf, np->data);
316
0
    if (STAILQ_NEXT(np, entries))
317
0
    {
318
0
      const int sep = (list->flags & SLIST_SEP_MASK);
319
0
      if (sep == SLIST_SEP_COMMA)
320
0
        buf_addch(buf, ',');
321
0
      else if (sep == SLIST_SEP_COLON)
322
0
        buf_addch(buf, ':');
323
0
      else
324
0
        buf_addch(buf, ' ');
325
0
    }
326
0
  }
327
328
0
  return list->count;
329
0
}