Line data Source code
1 : /* Copyright 1989-2025 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include <stdio.h> // FILE, putc(), sprintf()
24 : #include <stdlib.h> // calloc()
25 : #include <string.h> // memchr(), memcmp(), memcpy(), memmem(), memset(),
26 : // strlen(), size_t
27 :
28 : #include "cset.h" // csprint()
29 : #include "lib.h"
30 : #include "json-encode.h" // json_char, json_encode_char()
31 :
32 : #include "stringclass.h"
33 :
34 : static char *salloc(int len, int *sizep);
35 : static void sfree(char *ptr, int size);
36 : static char *sfree_alloc(char *ptr, int size, int len, int *sizep);
37 : static char *srealloc(char *ptr, int size, int oldlen, int newlen,
38 : int *sizep);
39 :
40 3131688 : static char *salloc(int len, int *sizep)
41 : {
42 3131688 : if (len == 0) {
43 549588 : *sizep = 0;
44 549588 : return 0;
45 : }
46 : else
47 2582100 : return new char[*sizep = (len * 2)];
48 : }
49 :
50 3513468 : static void sfree(char *ptr, int)
51 : {
52 3513468 : delete[] ptr;
53 3513468 : }
54 :
55 448693 : static char *sfree_alloc(char *ptr, int oldsz, int len, int *sizep)
56 : {
57 448693 : if (oldsz >= len) {
58 151714 : *sizep = oldsz;
59 151714 : return ptr;
60 : }
61 296979 : delete[] ptr;
62 296979 : if (len == 0) {
63 0 : *sizep = 0;
64 0 : return 0;
65 : }
66 : else
67 296979 : return new char[*sizep = (len * 2)];
68 : }
69 :
70 1258573 : static char *srealloc(char *ptr, int oldsz, int oldlen, int newlen,
71 : int *sizep)
72 : {
73 1258573 : if (oldsz >= newlen) {
74 0 : *sizep = oldsz;
75 0 : return ptr;
76 : }
77 1258573 : if (newlen == 0) {
78 0 : delete[] ptr;
79 0 : *sizep = 0;
80 0 : return 0;
81 : }
82 : else {
83 1258573 : char *p = new char[*sizep = (newlen * 2)];
84 1258573 : if (oldlen < newlen && oldlen != 0)
85 956331 : memcpy(p, ptr, oldlen);
86 1258573 : delete[] ptr;
87 1258573 : return p;
88 : }
89 : }
90 :
91 288074 : string::string() : ptr(0), len(0), sz(0)
92 : {
93 288074 : }
94 :
95 11929 : string::string(const char *p, int n) : len(n)
96 : {
97 11929 : assert(n >= 0);
98 11929 : ptr = salloc(n, &sz);
99 11929 : if (n != 0)
100 11929 : memcpy(ptr, p, n);
101 11929 : }
102 :
103 2845276 : string::string(const char *p)
104 : {
105 2845276 : if (p == 0 /* nullptr */) {
106 97652 : len = 0;
107 97652 : ptr = 0 /* nullptr */;
108 97652 : sz = 0;
109 : }
110 : else {
111 2747624 : len = strlen(p);
112 2747624 : ptr = salloc(len, &sz);
113 2747624 : if (len < sz)
114 2277876 : memset(ptr, 0, sz);
115 2747624 : if (len != 0)
116 2277876 : memcpy(ptr, p, len);
117 : }
118 2845276 : }
119 :
120 0 : string::string(char c) : len(1)
121 : {
122 0 : ptr = salloc(1, &sz);
123 0 : *ptr = c;
124 0 : }
125 :
126 364302 : string::string(const string &s) : len(s.len)
127 : {
128 364302 : ptr = salloc(len, &sz);
129 364302 : if (len != 0)
130 284462 : memcpy(ptr, s.ptr, len);
131 364302 : }
132 :
133 7025706 : string::~string()
134 : {
135 3512853 : sfree(ptr, sz);
136 3512853 : }
137 :
138 382963 : string &string::operator=(const string &s)
139 : {
140 382963 : ptr = sfree_alloc(ptr, sz, s.len, &sz);
141 382963 : len = s.len;
142 382963 : if (len != 0)
143 299401 : memcpy(ptr, s.ptr, len);
144 382963 : return *this;
145 : }
146 :
147 49881 : string &string::operator=(const char *p)
148 : {
149 49881 : if (p == 0) {
150 0 : sfree(ptr, len);
151 0 : len = 0;
152 0 : ptr = 0;
153 0 : sz = 0;
154 : }
155 : else {
156 49881 : size_t slen = strlen(p);
157 49881 : ptr = sfree_alloc(ptr, sz, slen, &sz);
158 49881 : len = slen;
159 49881 : if (len != 0)
160 17975 : memcpy(ptr, p, len);
161 : }
162 49881 : return *this;
163 : }
164 :
165 15849 : string &string::operator=(char c)
166 : {
167 15849 : ptr = sfree_alloc(ptr, sz, 1, &sz);
168 15849 : len = 1;
169 15849 : *ptr = c;
170 15849 : return *this;
171 : }
172 :
173 615 : void string::move(string &s)
174 : {
175 615 : sfree(ptr, sz);
176 615 : ptr = s.ptr;
177 615 : len = s.len;
178 615 : sz = s.sz;
179 615 : s.ptr = 0;
180 615 : s.len = 0;
181 615 : s.sz = 0;
182 615 : }
183 :
184 618730 : void string::grow1()
185 : {
186 618730 : ptr = srealloc(ptr, sz, len, len + 1, &sz);
187 618730 : }
188 :
189 2081141 : string &string::operator+=(const char *p)
190 : {
191 2081141 : if (p != 0) {
192 2081141 : size_t n = strlen(p);
193 2081141 : int newlen = len + n;
194 2081141 : if (newlen > sz)
195 569628 : ptr = srealloc(ptr, sz, len, newlen, &sz);
196 2081141 : memcpy(ptr + len, p, n);
197 2081141 : len = newlen;
198 : }
199 2081141 : return *this;
200 : }
201 :
202 2037616 : string &string::operator+=(const string &s)
203 : {
204 2037616 : if (s.len != 0) {
205 2037602 : int newlen = len + s.len;
206 2037602 : if (newlen > sz)
207 70215 : ptr = srealloc(ptr, sz, len, newlen, &sz);
208 2037602 : memcpy(ptr + len, s.ptr, s.len);
209 2037602 : len = newlen;
210 : }
211 2037616 : return *this;
212 : }
213 :
214 0 : void string::append(const char *p, int n)
215 : {
216 0 : if (n > 0) {
217 0 : int newlen = len + n;
218 0 : if (newlen > sz)
219 0 : ptr = srealloc(ptr, sz, len, newlen, &sz);
220 0 : memcpy(ptr + len, p, n);
221 0 : len = newlen;
222 : }
223 0 : }
224 :
225 7833 : string::string(const char *s1, int n1, const char *s2, int n2)
226 : {
227 7833 : assert(n1 >= 0 && n2 >= 0);
228 7833 : len = n1 + n2;
229 7833 : if (len == 0) {
230 0 : sz = 0;
231 0 : ptr = 0;
232 : }
233 : else {
234 7833 : ptr = salloc(len, &sz);
235 7833 : if (n1 == 0)
236 3 : memcpy(ptr, s2, n2);
237 : else {
238 7830 : memcpy(ptr, s1, n1);
239 7830 : if (n2 != 0)
240 7828 : memcpy(ptr + n1, s2, n2);
241 : }
242 : }
243 7833 : }
244 :
245 0 : int operator<=(const string &s1, const string &s2)
246 : {
247 0 : return (s1.len <= s2.len
248 0 : ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
249 0 : : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
250 : }
251 :
252 0 : int operator<(const string &s1, const string &s2)
253 : {
254 0 : return (s1.len < s2.len
255 0 : ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
256 0 : : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
257 : }
258 :
259 0 : int operator>=(const string &s1, const string &s2)
260 : {
261 0 : return (s1.len >= s2.len
262 0 : ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
263 0 : : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
264 : }
265 :
266 0 : int operator>(const string &s1, const string &s2)
267 : {
268 0 : return (s1.len > s2.len
269 0 : ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
270 0 : : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
271 : }
272 :
273 10397 : void string::set_length(int i)
274 : {
275 10397 : assert(i >= 0);
276 10397 : if (i > sz)
277 0 : ptr = srealloc(ptr, sz, len, i, &sz);
278 10397 : len = i;
279 10397 : }
280 :
281 726766 : void string::clear()
282 : {
283 726766 : len = 0;
284 726766 : }
285 :
286 42496 : int string::search(const char c) const
287 : {
288 84992 : const char *p = ptr
289 42496 : ? static_cast<const char *>(memchr(ptr, c, len))
290 : : 0 /* nullptr */;
291 42496 : return (p != 0 /* nullptr */) ? (p - ptr) : -1;
292 : }
293 :
294 : // Return index of substring `c` in string, -1 if not found.
295 53409 : int string::find(const char *c) const
296 : {
297 106818 : const char *p = ptr
298 53409 : ? static_cast<const char *>(memmem(ptr, len, c,
299 : strlen(c)))
300 : : 0 /* nullptr */;
301 53409 : return (p != 0 /* nullptr */) ? (p - ptr) : -1;
302 : }
303 :
304 : // Return pointer to null-terminated C string; any nulls internal to the
305 : // string are omitted. The caller is responsible for `free()`ing the
306 : // returned storage.
307 14859 : char *string::extract() const
308 : {
309 14859 : char *p = ptr;
310 14859 : int n = len;
311 14859 : int nnuls = 0;
312 : int i;
313 205823 : for (i = 0; i < n; i++)
314 190964 : if (p[i] == '\0')
315 0 : nnuls++;
316 14859 : char *q = static_cast<char *>(calloc(n + 1 - nnuls, sizeof(char)));
317 14859 : if (q != 0 /* nullptr */) {
318 14859 : char *r = q;
319 205823 : for (i = 0; i < n; i++)
320 190964 : if (p[i] != '\0')
321 190964 : *r++ = p[i];
322 14859 : *r = '\0';
323 : }
324 14859 : return q;
325 : }
326 :
327 : // Compute length of JSON representation of object.
328 0 : size_t string::json_length() const
329 : {
330 0 : size_t n = len;
331 0 : const char *p = ptr;
332 : char ch;
333 0 : int nextrachars = 2; // leading and trailing double quotes
334 0 : for (size_t i = 0; i < n; i++) {
335 0 : ch = p[i];
336 0 : assert ((ch >= 0) && (ch <= 127));
337 : // These printable characters require escaping.
338 0 : if (('"' == ch) || ('\\' == ch) || ('/' == ch))
339 0 : nextrachars++;
340 0 : else if (csprint(ch))
341 : ;
342 : else
343 0 : switch (ch) {
344 0 : case '\b':
345 : case '\f':
346 : case '\n':
347 : case '\r':
348 : case '\t':
349 0 : nextrachars++;
350 0 : break;
351 0 : default:
352 0 : nextrachars += 5;
353 : }
354 : }
355 0 : return (n + nextrachars);
356 : }
357 :
358 : // Like `extract()`, but double-quote the string and escape characters
359 : // per JSON and emit nulls.
360 0 : const char *string::json_extract() const
361 : {
362 0 : const char *p = ptr;
363 : char *r;
364 0 : size_t n = len;
365 : size_t i;
366 0 : char *q = static_cast<char *>(calloc(this->json_length() + 1,
367 : sizeof (char)));
368 0 : if (q != 0 /* nullptr */) {
369 0 : r = q;
370 0 : *r++ = '"';
371 : json_char ch;
372 0 : for (i = 0; i < n; i++, p++) {
373 0 : ch = json_encode_char(*p);
374 0 : for (size_t j = 0; j < ch.len; j++)
375 0 : *r++ = ch.buf[j];
376 : }
377 0 : *r++ = '"';
378 : }
379 : else
380 0 : return strdup("\"\"");
381 0 : *r++ = '\0';
382 0 : return q;
383 : }
384 :
385 : // Dump string in JSON representation to standard error stream.
386 0 : void string::json_dump() const
387 : {
388 0 : const char *repr = this->json_extract();
389 0 : size_t jsonlen = this->json_length();
390 : // Write it out by character to keep libc string functions from
391 : // interpreting escape sequences.
392 0 : for (size_t i = 0; i < jsonlen; i++)
393 0 : fputc(repr[i], stderr);
394 0 : free(const_cast<char *>(repr));
395 0 : }
396 :
397 66 : void string::remove_spaces()
398 : {
399 66 : int l = len - 1;
400 168 : while ((l >= 0) && (ptr[l] == ' '))
401 102 : l--;
402 66 : char *p = ptr;
403 66 : if (l > 0)
404 163 : while (*p == ' ') {
405 107 : p++;
406 107 : l--;
407 : }
408 66 : if (len - 1 != l) {
409 66 : if (l >= 0) {
410 66 : len = l + 1;
411 66 : char *tmp = new char[sz];
412 66 : memcpy(tmp, p, len);
413 66 : delete[] ptr;
414 66 : ptr = tmp;
415 : }
416 : else {
417 0 : len = 0;
418 0 : if (ptr) {
419 0 : delete[] ptr;
420 0 : ptr = 0;
421 0 : sz = 0;
422 : }
423 : }
424 : }
425 66 : }
426 :
427 514532 : void put_string(const string &s, FILE *fp)
428 : {
429 514532 : int len = s.length();
430 514532 : const char *ptr = s.contents();
431 10748854 : for (int i = 0; i < len; i++)
432 10234322 : putc(ptr[i], fp);
433 514532 : }
434 :
435 1995439 : string as_string(int i)
436 : {
437 : static char buf[INT_DIGITS + 2];
438 1995439 : sprintf(buf, "%d", i);
439 1995439 : return string(buf);
440 : }
441 :
442 : // Local Variables:
443 : // fill-column: 72
444 : // mode: C++
445 : // End:
446 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|