Coverage Report

Created: 2025-11-15 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/str.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "buffer.h"
5
#include "printf-format-fix.h"
6
#include "unichar.h"
7
#include "str.h"
8
9
#include <stdio.h>
10
11
string_t *str_new(pool_t pool, size_t initial_size)
12
1.11k
{
13
  /* never allocate a 0 byte size buffer. this is especially important
14
     when str_c() is called on an empty string from a different stack
15
     frame (see the comment in buffer.c about this). */
16
1.11k
  return buffer_create_dynamic(pool, I_MAX(initial_size, 1));
17
1.11k
}
18
19
string_t *str_new_const(pool_t pool, const char *str, size_t len)
20
0
{
21
0
  string_t *ret;
22
23
0
  i_assert(str[len] == '\0');
24
25
0
  ret = p_new(pool, buffer_t, 1);
26
0
  buffer_create_from_const_data(ret, str, len + 1);
27
0
  str_truncate(ret, len);
28
0
  return ret;
29
0
}
30
31
string_t *t_str_new(size_t initial_size)
32
1.11k
{
33
1.11k
  return str_new(pool_datastack_create(), initial_size);
34
1.11k
}
35
36
string_t *t_str_new_const(const char *str, size_t len)
37
0
{
38
0
  return str_new_const(pool_datastack_create(), str, len);
39
0
}
40
41
void str_free(string_t **str)
42
0
{
43
0
  if (str == NULL || *str == NULL)
44
0
    return;
45
46
0
  buffer_free(str);
47
0
}
48
49
static void str_add_nul(string_t *str)
50
1
{
51
1
  const unsigned char *data = str_data(str);
52
1
  size_t len = str_len(str);
53
1
  size_t alloc = buffer_get_size(str);
54
55
1
  if (len == alloc || data[len] != '\0') {
56
0
    buffer_write(str, len, "", 1);
57
    /* remove the \0 - we don't want to keep it */
58
0
    buffer_set_used_size(str, len);
59
0
  }
60
1
}
61
62
char *str_free_without_data(string_t **str)
63
0
{
64
0
  str_add_nul(*str);
65
0
  return buffer_free_without_data(str);
66
0
}
67
68
const char *str_c(string_t *str)
69
1
{
70
1
  str_add_nul(str);
71
1
  return str->data;
72
1
}
73
74
char *str_c_modifiable(string_t *str)
75
0
{
76
0
  str_add_nul(str);
77
0
  return buffer_get_modifiable_data(str, NULL);
78
0
}
79
80
bool str_equals(const string_t *str1, const string_t *str2)
81
0
{
82
0
  if (str1->used != str2->used)
83
0
    return FALSE;
84
85
0
  return memcmp(str1->data, str2->data, str1->used) == 0;
86
0
}
87
88
void str_append_max(string_t *str, const char *cstr, size_t max_len)
89
0
{
90
0
  const char *p;
91
0
  size_t len;
92
93
0
  p = memchr(cstr, '\0', max_len);
94
0
  if (p == NULL)
95
0
    len = max_len;
96
0
  else
97
0
    len = p - (const char *)cstr;
98
0
  buffer_append(str, cstr, len);
99
0
}
100
101
void str_printfa(string_t *str, const char *fmt, ...)
102
19.8M
{
103
19.8M
  va_list args;
104
105
19.8M
  va_start(args, fmt);
106
19.8M
  str_vprintfa(str, fmt, args);
107
19.8M
  va_end(args);
108
19.8M
}
109
110
void str_vprintfa(string_t *str, const char *fmt, va_list args)
111
19.8M
{
112
19.8M
#define SNPRINTF_INITIAL_EXTRA_SIZE 128
113
19.8M
  va_list args2;
114
19.8M
  char *tmp;
115
19.8M
  size_t init_size;
116
19.8M
  size_t pos = str->used;
117
19.8M
  int ret, ret2;
118
119
19.8M
  VA_COPY(args2, args);
120
121
  /* the format string is modified only if %m exists in it. it happens
122
     only in error conditions, so don't try to t_push() here since it'll
123
     just slow down the normal code path. */
124
19.8M
  fmt = printf_format_fix_get_len(fmt, &init_size);
125
19.8M
  init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
126
127
  /* @UNSAFE */
128
19.8M
  if (pos+init_size > buffer_get_writable_size(str) &&
129
17.8k
      pos < buffer_get_writable_size(str)) {
130
    /* avoid growing buffer larger if possible. this is also
131
       required if buffer isn't dynamically growing. */
132
17.7k
    init_size = buffer_get_writable_size(str)-pos;
133
17.7k
  }
134
19.8M
  tmp = buffer_get_space_unsafe(str, pos, init_size);
135
19.8M
  ret = vsnprintf(tmp, init_size, fmt, args);
136
19.8M
  i_assert(ret >= 0);
137
138
19.8M
  if ((unsigned int)ret >= init_size) {
139
    /* didn't fit with the first guess. now we know the size,
140
       so try again. */
141
458
    tmp = buffer_get_space_unsafe(str, pos, ret + 1);
142
458
    ret2 = vsnprintf(tmp, ret + 1, fmt, args2);
143
458
    i_assert(ret2 == ret);
144
458
  }
145
19.8M
  va_end(args2);
146
147
  /* drop the unused data, including terminating NUL */
148
19.8M
  buffer_set_used_size(str, pos + ret);
149
19.8M
}
150
151
void str_truncate_utf8(string_t *str, size_t len)
152
0
{
153
0
  size_t size = str_len(str);
154
155
0
  if (size <= len)
156
0
    return;
157
0
  str_truncate(str, uni_utf8_data_truncate(str_data(str), size, len));
158
0
}