LCOV - code coverage report
Current view: top level - libs/libgroff - string.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 166 253 65.6 %
Date: 2026-01-16 17:51:41 Functions: 25 34 73.5 %
Legend: Lines: hit not hit

          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:

Generated by: LCOV version 1.14