Coverage Report

Created: 2026-10-28 07:22

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