Coverage Report

Created: 2025-04-22 06:17

/src/neomutt/mutt/buffer.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * General purpose object for storing and parsing strings
4
 *
5
 * @authors
6
 * Copyright (C) 2017 Ian Zimmerman <itz@primate.net>
7
 * Copyright (C) 2017-2023 Richard Russon <rich@flatcap.org>
8
 * Copyright (C) 2019-2023 Pietro Cerutti <gahr@gahr.ch>
9
 * Copyright (C) 2023 Anna Figueiredo Gomes <navi@vlhl.dev>
10
 * Copyright (C) 2023 Simon Reichel <simonreichel@giese-optik.de>
11
 * Copyright (C) 2024 Dennis Schön <mail@dennis-schoen.de>
12
 *
13
 * @copyright
14
 * This program is free software: you can redistribute it and/or modify it under
15
 * the terms of the GNU General Public License as published by the Free Software
16
 * Foundation, either version 2 of the License, or (at your option) any later
17
 * version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
 * details.
23
 *
24
 * You should have received a copy of the GNU General Public License along with
25
 * this program.  If not, see <http://www.gnu.org/licenses/>.
26
 */
27
28
/**
29
 * @page mutt_buffer Helper object for storing and parsing strings
30
 *
31
 * The Buffer object make parsing and manipulating strings easier.
32
 *
33
 * The following unused functions were removed:
34
 * - buf_upper()
35
 */
36
37
#include "config.h"
38
#include <errno.h>
39
#include <stdarg.h>
40
#include <stdbool.h>
41
#include <stdint.h>
42
#include <stdio.h>
43
#include <string.h>
44
#include "buffer.h"
45
#include "exit.h"
46
#include "logging2.h"
47
#include "memory.h"
48
#include "message.h"
49
#include "string2.h"
50
51
/// When increasing the size of a Buffer, add this much extra space
52
static const int BufferStepSize = 128;
53
54
/**
55
 * buf_init - Initialise a new Buffer
56
 * @param buf Buffer to initialise
57
 * @retval ptr Initialised Buffer
58
 *
59
 * This must not be called on a Buffer that already contains data.
60
 */
61
struct Buffer *buf_init(struct Buffer *buf)
62
0
{
63
0
  if (!buf)
64
0
    return NULL;
65
0
  memset(buf, 0, sizeof(struct Buffer));
66
0
  return buf;
67
0
}
68
69
/**
70
 * buf_reset - Reset an existing Buffer
71
 * @param buf Buffer to reset
72
 *
73
 * This can be called on a Buffer to reset the pointers,
74
 * effectively emptying it.
75
 */
76
void buf_reset(struct Buffer *buf)
77
6.39M
{
78
6.39M
  if (!buf || !buf->data || (buf->dsize == 0))
79
0
    return;
80
6.39M
  memset(buf->data, 0, buf->dsize);
81
6.39M
  buf_seek(buf, 0);
82
6.39M
}
83
84
/**
85
 * buf_addstr_n - Add a string to a Buffer, expanding it if necessary
86
 * @param buf Buffer to add to
87
 * @param s   String to add
88
 * @param len Length of the string
89
 * @retval num Bytes written to Buffer
90
 * @retval 0   Error
91
 *
92
 * Dynamically grow a Buffer to accommodate s, in increments of 128 bytes.
93
 * Always one byte bigger than necessary for the NUL-terminator, and the
94
 * buffer is always NUL-terminated
95
 */
96
size_t buf_addstr_n(struct Buffer *buf, const char *s, size_t len)
97
9.91M
{
98
9.91M
  if (!buf || !s)
99
0
    return 0;
100
101
9.91M
  if (len > (SIZE_MAX - BufferStepSize))
102
0
  {
103
    // LCOV_EXCL_START
104
0
    mutt_error("%s", strerror(ENOMEM));
105
0
    mutt_exit(1);
106
    // LCOV_EXCL_STOP
107
0
  }
108
109
9.91M
  if (!buf->data || !buf->dptr || ((buf->dptr + len + 1) > (buf->data + buf->dsize)))
110
1.64M
    buf_alloc(buf, buf->dsize + MAX(BufferStepSize, len + 1));
111
112
9.91M
  memcpy(buf->dptr, s, len);
113
9.91M
  buf->dptr += len;
114
9.91M
  *(buf->dptr) = '\0';
115
9.91M
  return len;
116
9.91M
}
117
118
/**
119
 * buf_vaprintf - Format a string into a Buffer
120
 * @param buf Buffer
121
 * @param fmt printf-style format string
122
 * @param ap  Arguments to be formatted
123
 * @retval num Characters written
124
 * @retval 0   Error
125
 */
126
static int buf_vaprintf(struct Buffer *buf, const char *fmt, va_list ap)
127
38.4k
{
128
38.4k
  if (!buf || !fmt)
129
0
    return 0; /* LCOV_EXCL_LINE */
130
131
38.4k
  buf_alloc(buf, 128);
132
133
38.4k
  int doff = buf->dptr - buf->data;
134
38.4k
  int blen = buf->dsize - doff;
135
136
38.4k
  va_list ap_retry;
137
38.4k
  va_copy(ap_retry, ap);
138
139
38.4k
  int len = vsnprintf(buf->dptr, blen, fmt, ap);
140
38.4k
  if (len >= blen)
141
0
  {
142
0
    buf_alloc(buf, buf->dsize + len - blen + 1);
143
0
    len = vsnprintf(buf->dptr, len + 1, fmt, ap_retry);
144
0
  }
145
38.4k
  if (len > 0)
146
38.4k
    buf->dptr += len;
147
148
38.4k
  va_end(ap_retry);
149
150
38.4k
  return len;
151
38.4k
}
152
153
/**
154
 * buf_printf - Format a string overwriting a Buffer
155
 * @param buf Buffer
156
 * @param fmt printf-style format string
157
 * @param ... Arguments to be formatted
158
 * @retval num Characters written
159
 * @retval -1  Error
160
 */
161
int buf_printf(struct Buffer *buf, const char *fmt, ...)
162
38.4k
{
163
38.4k
  if (!buf || !fmt)
164
0
    return -1;
165
166
38.4k
  va_list ap;
167
168
38.4k
  va_start(ap, fmt);
169
38.4k
  buf_reset(buf);
170
38.4k
  int len = buf_vaprintf(buf, fmt, ap);
171
38.4k
  va_end(ap);
172
173
38.4k
  return len;
174
38.4k
}
175
176
/**
177
 * buf_fix_dptr - Move the dptr to end of the Buffer
178
 * @param buf Buffer to alter
179
 *
180
 * Ensure buffer->dptr points to the end of the buffer.
181
 */
182
void buf_fix_dptr(struct Buffer *buf)
183
9.62k
{
184
9.62k
  if (!buf)
185
0
    return;
186
187
9.62k
  buf_seek(buf, 0);
188
189
9.62k
  if (buf->data && (buf->dsize > 0))
190
9.62k
  {
191
9.62k
    buf->data[buf->dsize - 1] = '\0';
192
9.62k
    buf->dptr = strchr(buf->data, '\0');
193
9.62k
  }
194
9.62k
}
195
196
/**
197
 * buf_add_printf - Format a string appending a Buffer
198
 * @param buf Buffer
199
 * @param fmt printf-style format string
200
 * @param ... Arguments to be formatted
201
 * @retval num Characters written
202
 * @retval -1  Error
203
 */
204
int buf_add_printf(struct Buffer *buf, const char *fmt, ...)
205
0
{
206
0
  if (!buf || !fmt)
207
0
    return -1;
208
209
0
  va_list ap;
210
211
0
  va_start(ap, fmt);
212
0
  int len = buf_vaprintf(buf, fmt, ap);
213
0
  va_end(ap);
214
215
0
  return len;
216
0
}
217
218
/**
219
 * buf_addstr - Add a string to a Buffer
220
 * @param buf Buffer to add to
221
 * @param s   String to add
222
 * @retval num Bytes written to Buffer
223
 *
224
 * If necessary, the Buffer will be expanded.
225
 */
226
size_t buf_addstr(struct Buffer *buf, const char *s)
227
1.60M
{
228
1.60M
  if (!buf || !s)
229
0
    return 0;
230
1.60M
  return buf_addstr_n(buf, s, mutt_str_len(s));
231
1.60M
}
232
233
/**
234
 * buf_addch - Add a single character to a Buffer
235
 * @param buf Buffer to add to
236
 * @param c   Character to add
237
 * @retval num Bytes written to Buffer
238
 *
239
 * If necessary, the Buffer will be expanded.
240
 */
241
size_t buf_addch(struct Buffer *buf, char c)
242
6.81M
{
243
6.81M
  if (!buf)
244
0
    return 0;
245
6.81M
  return buf_addstr_n(buf, &c, 1);
246
6.81M
}
247
248
/**
249
 * buf_insert - Add a string in the middle of a buffer
250
 * @param buf    Buffer
251
 * @param offset Position for the insertion
252
 * @param s      String to insert
253
 * @retval num Characters written
254
 * @retval -1  Error
255
 */
256
size_t buf_insert(struct Buffer *buf, size_t offset, const char *s)
257
0
{
258
0
  if (!buf || !s || (*s == '\0'))
259
0
  {
260
0
    return -1;
261
0
  }
262
263
0
  const size_t slen = mutt_str_len(s);
264
0
  const size_t curlen = buf_len(buf);
265
0
  buf_alloc(buf, curlen + slen + 1);
266
267
0
  if (offset > curlen)
268
0
  {
269
0
    for (size_t i = curlen; i < offset; i++)
270
0
    {
271
0
      buf_addch(buf, ' ');
272
0
    }
273
0
    buf_addstr(buf, s);
274
0
  }
275
0
  else
276
0
  {
277
0
    memmove(buf->data + offset + slen, buf->data + offset, curlen - offset);
278
0
    memcpy(buf->data + offset, s, slen);
279
0
    buf->data[curlen + slen] = '\0';
280
0
    buf->dptr = buf->data + curlen + slen;
281
0
  }
282
283
0
  return buf_len(buf) - curlen;
284
0
}
285
286
/**
287
 * buf_is_empty - Is the Buffer empty?
288
 * @param buf Buffer to inspect
289
 * @retval true Buffer is empty
290
 */
291
bool buf_is_empty(const struct Buffer *buf)
292
5.80M
{
293
5.80M
  if (!buf || !buf->data)
294
1.18k
    return true;
295
296
5.80M
  return (buf->data[0] == '\0');
297
5.80M
}
298
299
/**
300
 * buf_new - Allocate a new Buffer
301
 * @param str String to initialise the buffer with, can be NULL
302
 * @retval ptr Pointer to new buffer
303
 */
304
struct Buffer *buf_new(const char *str)
305
75.6k
{
306
75.6k
  struct Buffer *buf = MUTT_MEM_CALLOC(1, struct Buffer);
307
308
75.6k
  if (str)
309
75.5k
    buf_addstr(buf, str);
310
20
  else
311
20
    buf_alloc(buf, 1);
312
75.6k
  return buf;
313
75.6k
}
314
315
/**
316
 * buf_free - Deallocates a buffer
317
 * @param ptr Buffer to free
318
 */
319
void buf_free(struct Buffer **ptr)
320
216k
{
321
216k
  if (!ptr || !*ptr)
322
141k
    return;
323
324
75.5k
  struct Buffer *buf = *ptr;
325
75.5k
  buf_dealloc(buf);
326
327
75.5k
  FREE(ptr);
328
75.5k
}
329
330
/**
331
 * buf_alloc - Make sure a buffer can store at least new_size bytes
332
 * @param buf      Buffer to change
333
 * @param new_size New size
334
 *
335
 * @note new_size will be rounded up to #BufferStepSize
336
 */
337
void buf_alloc(struct Buffer *buf, size_t new_size)
338
1.68M
{
339
1.68M
  if (!buf)
340
0
    return;
341
342
1.68M
  if (buf->data && (new_size <= buf->dsize))
343
38.4k
  {
344
    // Extra sanity-checking
345
38.4k
    if (!buf->dptr || (buf->dptr < buf->data) || (buf->dptr > (buf->data + buf->dsize)))
346
0
      buf->dptr = buf->data; // LCOV_EXCL_LINE
347
38.4k
    return;
348
38.4k
  }
349
350
1.64M
  if (new_size > (SIZE_MAX - BufferStepSize))
351
0
  {
352
    // LCOV_EXCL_START
353
0
    mutt_error(_("Out of memory"));
354
0
    mutt_exit(1);
355
    // LCOV_EXCL_STOP
356
0
  }
357
358
1.64M
  const bool was_empty = (buf->dptr == NULL);
359
1.64M
  const size_t offset = (buf->dptr && buf->data) ? (buf->dptr - buf->data) : 0;
360
361
1.64M
  buf->dsize = ROUND_UP(new_size + 1, BufferStepSize);
362
363
1.64M
  MUTT_MEM_REALLOC(&buf->data, buf->dsize, char);
364
1.64M
  buf->dptr = buf->data + offset;
365
366
  // Ensures that initially NULL buf->data is properly terminated
367
1.64M
  if (was_empty)
368
75.6k
  {
369
75.6k
    *buf->dptr = '\0';
370
75.6k
  }
371
1.64M
}
372
373
/**
374
 * buf_dealloc - Release the memory allocated by a buffer
375
 * @param buf Buffer to change
376
 */
377
void buf_dealloc(struct Buffer *buf)
378
228k
{
379
228k
  if (!buf || !buf->data)
380
153k
    return;
381
382
75.5k
  buf->dptr = NULL;
383
75.5k
  buf->dsize = 0;
384
75.5k
  FREE(&buf->data);
385
75.5k
}
386
387
/**
388
 * buf_strcpy - Copy a string into a Buffer
389
 * @param buf Buffer to overwrite
390
 * @param s   String to copy
391
 * @retval num Bytes written to Buffer
392
 *
393
 * Overwrites any existing content.
394
 */
395
size_t buf_strcpy(struct Buffer *buf, const char *s)
396
71.3k
{
397
71.3k
  if (!buf)
398
408
    return 0;
399
400
70.9k
  buf_reset(buf);
401
70.9k
  if (!s)
402
0
    return 0;
403
404
70.9k
  return buf_addstr(buf, s);
405
70.9k
}
406
407
/**
408
 * buf_strcpy_n - Copy a string into a Buffer
409
 * @param buf Buffer to overwrite
410
 * @param s   String to copy
411
 * @param len Length of string to copy
412
 * @retval num Bytes written to Buffer
413
 *
414
 * Overwrites any existing content.
415
 */
416
size_t buf_strcpy_n(struct Buffer *buf, const char *s, size_t len)
417
0
{
418
0
  if (!buf)
419
0
    return 0;
420
421
0
  buf_reset(buf);
422
0
  if (!s)
423
0
    return 0;
424
425
0
  return buf_addstr_n(buf, s, len);
426
0
}
427
428
/**
429
 * buf_dequote_comment - Un-escape characters in an email address comment
430
 * @param buf Buffer to be un-escaped
431
 *
432
 * @note the buffer is modified
433
 */
434
void buf_dequote_comment(struct Buffer *buf)
435
0
{
436
0
  if (!buf || !buf->data || (buf->dsize == 0))
437
0
    return;
438
439
0
  buf_seek(buf, 0);
440
441
0
  char *s = buf->data;
442
0
  for (; *buf->dptr; buf->dptr++)
443
0
  {
444
0
    if (*buf->dptr == '\\')
445
0
    {
446
0
      if (!*++buf->dptr)
447
0
        break; /* error? */
448
0
      *s++ = *buf->dptr;
449
0
    }
450
0
    else if (*buf->dptr != '\"')
451
0
    {
452
0
      if (s != buf->dptr)
453
0
        *s = *buf->dptr;
454
0
      s++;
455
0
    }
456
0
  }
457
0
  *s = '\0';
458
459
0
  buf_fix_dptr(buf);
460
0
}
461
462
/**
463
 * buf_substrcpy - Copy a partial string into a Buffer
464
 * @param buf Buffer to overwrite
465
 * @param beg Start of string to copy
466
 * @param end End of string to copy
467
 * @retval num Bytes written to Buffer
468
 *
469
 * Overwrites any existing content.
470
 */
471
size_t buf_substrcpy(struct Buffer *buf, const char *beg, const char *end)
472
0
{
473
0
  if (!buf)
474
0
    return 0;
475
476
0
  buf_reset(buf);
477
0
  if (!beg || !end)
478
0
    return 0;
479
480
0
  if (end <= beg)
481
0
    return 0;
482
483
0
  return buf_strcpy_n(buf, beg, end - beg);
484
0
}
485
486
/**
487
 * buf_len - Calculate the length of a Buffer
488
 * @param buf Buffer
489
 * @retval num Size of buffer
490
 */
491
size_t buf_len(const struct Buffer *buf)
492
182k
{
493
182k
  if (!buf || !buf->data || !buf->dptr)
494
0
    return 0;
495
496
182k
  return buf->dptr - buf->data;
497
182k
}
498
499
/**
500
 * buf_concat_path - Join a directory name and a filename
501
 * @param buf   Buffer to add to
502
 * @param dir   Directory name
503
 * @param fname File name
504
 * @retval num Bytes written to Buffer
505
 *
506
 * If both dir and fname are supplied, they are separated with '/'.
507
 * If either is missing, then the other will be copied exactly.
508
 */
509
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
510
0
{
511
0
  if (!buf)
512
0
    return 0;
513
514
0
  if (!dir)
515
0
    dir = "";
516
0
  if (!fname)
517
0
    fname = "";
518
519
0
  const bool d_set = (dir[0] != '\0');
520
0
  const bool f_set = (fname[0] != '\0');
521
0
  if (!d_set && !f_set)
522
0
    return 0;
523
524
0
  const int d_len = strlen(dir);
525
0
  const bool slash = d_set && (dir[d_len - 1] == '/');
526
527
0
  const char *fmt = "%s/%s";
528
0
  if (!f_set || !d_set || slash)
529
0
    fmt = "%s%s";
530
531
0
  return buf_printf(buf, fmt, dir, fname);
532
0
}
533
534
/**
535
 * buf_concatn_path - Join a directory name and a filename
536
 * @param buf      Buffer for the result
537
 * @param dir      Directory name
538
 * @param dirlen   Directory name
539
 * @param fname    File name
540
 * @param fnamelen File name
541
 * @retval num Size of buffer
542
 *
543
 * If both dir and fname are supplied, they are separated with '/'.
544
 * If either is missing, then the other will be copied exactly.
545
 */
546
size_t buf_concatn_path(struct Buffer *buf, const char *dir, size_t dirlen,
547
                        const char *fname, size_t fnamelen)
548
0
{
549
0
  if (!buf)
550
0
    return 0;
551
552
0
  buf_reset(buf);
553
554
0
  size_t len = 0;
555
0
  if (dirlen != 0)
556
0
    len += buf_addstr_n(buf, dir, dirlen);
557
0
  if ((dirlen != 0) && (fnamelen != 0))
558
0
    len += buf_addch(buf, '/');
559
0
  if (fnamelen != 0)
560
0
    len += buf_addstr_n(buf, fname, fnamelen);
561
0
  return len;
562
0
}
563
564
/**
565
 * buf_strdup - Copy a Buffer's string
566
 * @param buf Buffer to copy
567
 * @retval ptr Copy of string
568
 *
569
 * @note Caller must free the returned string
570
 */
571
char *buf_strdup(const struct Buffer *buf)
572
1.60M
{
573
1.60M
  if (!buf)
574
0
    return NULL;
575
576
1.60M
  return mutt_str_dup(buf->data);
577
1.60M
}
578
579
/**
580
 * buf_dup - Copy a Buffer into a new allocated buffer
581
 * @param buf Buffer to copy
582
 * @retval buf New allocated copy of buffer
583
 *
584
 * @note Caller must free the returned buffer
585
 */
586
struct Buffer *buf_dup(const struct Buffer *buf)
587
0
{
588
0
  if (!buf)
589
0
    return NULL;
590
591
0
  return buf_new(buf_string(buf));
592
0
}
593
594
/**
595
 * buf_copy - Copy a Buffer's contents to another Buffer
596
 * @param dst Buffer for result
597
 * @param src Buffer to copy
598
 * @retval num Bytes written to Buffer
599
 * @retval 0   Error
600
 */
601
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
602
0
{
603
0
  if (!dst)
604
0
    return 0;
605
606
0
  buf_reset(dst);
607
0
  if (!src || !src->data)
608
0
    return 0;
609
610
0
  return buf_addstr_n(dst, src->data, buf_len(src));
611
0
}
612
613
/**
614
 * buf_seek - Set current read/write position to offset from beginning
615
 * @param buf    Buffer to use
616
 * @param offset Distance from the beginning
617
 *
618
 * This is used for cases where the buffer is read from
619
 * A value is placed in the buffer, and then b->dptr is set back to the
620
 * beginning as a read marker instead of write marker.
621
 */
622
void buf_seek(struct Buffer *buf, size_t offset)
623
6.40M
{
624
6.40M
  if (!buf || !buf->data)
625
0
    return;
626
627
6.40M
  if (offset < buf->dsize)
628
6.40M
    buf->dptr = buf->data + offset;
629
6.40M
}
630
631
/**
632
 * buf_find_string - Return a pointer to a substring found in the buffer
633
 * @param buf    Buffer to search
634
 * @param s      Substring to find
635
 * @retval NULL substring not found
636
 * @retval n    Pointer to the beginning of the found substring
637
 */
638
const char *buf_find_string(const struct Buffer *buf, const char *s)
639
15.6k
{
640
15.6k
  if (!buf || !s)
641
0
    return NULL;
642
643
15.6k
  return strstr(buf->data, s);
644
15.6k
}
645
646
/**
647
 * buf_find_char - Return a pointer to a char found in the buffer
648
 * @param buf    Buffer to search
649
 * @param c      Char to find
650
 * @retval NULL char not found
651
 * @retval ptr  Pointer to the found char
652
 */
653
const char *buf_find_char(const struct Buffer *buf, const char c)
654
0
{
655
0
  if (!buf)
656
0
    return NULL;
657
658
0
  return strchr(buf->data, c);
659
0
}
660
661
/**
662
 * buf_at - Return the character at the given offset
663
 * @param buf    Buffer to search
664
 * @param offset Offset to get
665
 * @retval NUL Offset out of bounds
666
 * @return n   The char at the offset
667
 */
668
char buf_at(const struct Buffer *buf, size_t offset)
669
182k
{
670
182k
  if (!buf || (offset > buf_len(buf)))
671
0
    return '\0';
672
673
182k
  return buf->data[offset];
674
182k
}
675
676
/**
677
 * buf_str_equal - Return if two buffers are equal
678
 * @param a - Buffer to compare
679
 * @param b - Buffer to compare
680
 * @retval true  Strings are equal
681
 * @retval false String are not equal
682
 */
683
bool buf_str_equal(const struct Buffer *a, const struct Buffer *b)
684
0
{
685
0
  return mutt_str_equal(buf_string(a), buf_string(b));
686
0
}
687
688
/**
689
 * buf_istr_equal - Return if two buffers are equal, case insensitive
690
 * @param a - First buffer to compare
691
 * @param b - Second buffer to compare
692
 * @retval true  Strings are equal
693
 * @retval false String are not equal
694
 */
695
bool buf_istr_equal(const struct Buffer *a, const struct Buffer *b)
696
0
{
697
0
  return mutt_istr_equal(buf_string(a), buf_string(b));
698
0
}
699
700
/**
701
 * buf_startswith - Check whether a buffer starts with a prefix
702
 * @param buf Buffer to check
703
 * @param prefix Prefix to match
704
 * @retval num Length of prefix if str starts with prefix
705
 * @retval 0   str does not start with prefix
706
 */
707
size_t buf_startswith(const struct Buffer *buf, const char *prefix)
708
0
{
709
0
  if (!buf || !prefix)
710
0
    return 0;
711
712
0
  return mutt_str_startswith(buf_string(buf), prefix);
713
0
}
714
715
/**
716
 * buf_coll - Collate two strings (compare using locale)
717
 * @param a First buffer to compare
718
 * @param b Second buffer to compare
719
 * @retval <0 a precedes b
720
 * @retval  0 a and b are identical
721
 * @retval >0 b precedes a
722
 */
723
int buf_coll(const struct Buffer *a, const struct Buffer *b)
724
0
{
725
0
  return mutt_str_coll(buf_string(a), buf_string(b));
726
0
}
727
728
/**
729
 * buf_lower - Sets a buffer to lowercase
730
 * @param[out] buf Buffer to transform to lowercase
731
 *
732
 * @note Modifies the buffer
733
 */
734
void buf_lower(struct Buffer *buf)
735
0
{
736
0
  if (!buf)
737
0
    return;
738
739
0
  mutt_str_lower(buf->data);
740
0
}
741
742
/**
743
 * buf_join_str - Join a buffer with a string separated by sep
744
 * @param buf Buffer to append to
745
 * @param str String to append
746
 * @param sep separator between string item
747
 */
748
void buf_join_str(struct Buffer *buf, const char *str, char sep)
749
0
{
750
0
  if (!buf || !str)
751
0
    return;
752
753
0
  if (!buf_is_empty(buf) && mutt_str_len(str))
754
0
    buf_addch(buf, sep);
755
756
0
  buf_addstr(buf, str);
757
0
}
758
759
/*
760
 * buf_inline_replace - Replace part of a string
761
 * @param buf   Buffer to modify
762
 * @param pos   Starting position of string to overwrite
763
 * @param len   Length of string to overwrite
764
 * @param str   Replacement string
765
 *
766
 * String (`11XXXOOOOOO`, 2, 3, `YYYY`) becomes `11YYYY000000`
767
 */
768
void buf_inline_replace(struct Buffer *buf, size_t pos, size_t len, const char *str)
769
0
{
770
0
  if (!buf || !str)
771
0
    return;
772
773
0
  size_t olen = buf->dsize;
774
0
  size_t rlen = mutt_str_len(str);
775
776
0
  size_t new_size = buf->dsize - len + rlen + 1;
777
0
  if (new_size > buf->dsize)
778
0
    buf_alloc(buf, new_size);
779
780
0
  memmove(buf->data + pos + rlen, buf->data + pos + len, olen - pos - len);
781
0
  memmove(buf->data + pos, str, rlen);
782
783
0
  buf_fix_dptr(buf);
784
0
}
785
786
/**
787
 * buf_rfind - Find last instance of a substring
788
 * @param buf   Buffer to search through
789
 * @param str   String to find
790
 * @retval NULL String not found
791
 * @retval ptr  Location of string
792
 *
793
 * Return the last instance of str in the buffer, or NULL.
794
 */
795
const char *buf_rfind(const struct Buffer *buf, const char *str)
796
0
{
797
0
  if (buf_is_empty(buf) || !str)
798
0
    return NULL;
799
800
0
  int len = strlen(str);
801
0
  const char *end = buf->data + buf->dsize - len;
802
803
0
  for (const char *p = end; p >= buf->data; p--)
804
0
  {
805
0
    for (size_t i = 0; i < len; i++)
806
0
    {
807
0
      if (p[i] != str[i])
808
0
        goto next;
809
0
    }
810
0
    return p;
811
812
0
  next:;
813
0
  }
814
0
  return NULL;
815
0
}