LCOV - code coverage report
Current view: top level - src/fe_utils - mbprint.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.0 % 176 139
Test Date: 2026-03-12 01:15:13 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * Multibyte character printing support for frontend code
       4              :  *
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  * src/fe_utils/mbprint.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres_fe.h"
      14              : 
      15              : #include "fe_utils/mbprint.h"
      16              : 
      17              : #include "libpq-fe.h"
      18              : 
      19              : 
      20              : /*
      21              :  * To avoid version-skew problems, this file must not use declarations
      22              :  * from pg_wchar.h: the encoding IDs we are dealing with are determined
      23              :  * by the libpq.so we are linked with, and that might not match the
      24              :  * numbers we see at compile time.  (If this file were inside libpq,
      25              :  * the problem would go away...)
      26              :  *
      27              :  * Hence, we have our own definition of pg_wchar, and we get the values
      28              :  * of any needed encoding IDs on-the-fly.
      29              :  */
      30              : 
      31              : typedef unsigned int pg_wchar;
      32              : 
      33              : static int
      34      2533504 : pg_get_utf8_id(void)
      35              : {
      36              :     static int  utf8_id = -1;
      37              : 
      38      2533504 :     if (utf8_id < 0)
      39         6455 :         utf8_id = pg_char_to_encoding("utf8");
      40      2533504 :     return utf8_id;
      41              : }
      42              : 
      43              : #define PG_UTF8     pg_get_utf8_id()
      44              : 
      45              : 
      46              : /*
      47              :  * Convert a UTF-8 character to a Unicode code point.
      48              :  * This is a one-character version of pg_utf2wchar_with_len.
      49              :  *
      50              :  * No error checks here, c must point to a long-enough string.
      51              :  */
      52              : static char32_t
      53            0 : utf8_to_unicode(const unsigned char *c)
      54              : {
      55            0 :     if ((*c & 0x80) == 0)
      56            0 :         return (char32_t) c[0];
      57            0 :     else if ((*c & 0xe0) == 0xc0)
      58            0 :         return (char32_t) (((c[0] & 0x1f) << 6) |
      59            0 :                            (c[1] & 0x3f));
      60            0 :     else if ((*c & 0xf0) == 0xe0)
      61            0 :         return (char32_t) (((c[0] & 0x0f) << 12) |
      62            0 :                            ((c[1] & 0x3f) << 6) |
      63            0 :                            (c[2] & 0x3f));
      64            0 :     else if ((*c & 0xf8) == 0xf0)
      65            0 :         return (char32_t) (((c[0] & 0x07) << 18) |
      66            0 :                            ((c[1] & 0x3f) << 12) |
      67            0 :                            ((c[2] & 0x3f) << 6) |
      68            0 :                            (c[3] & 0x3f));
      69              :     else
      70              :         /* that is an invalid code on purpose */
      71            0 :         return 0xffffffff;
      72              : }
      73              : 
      74              : 
      75              : /*
      76              :  * Unicode 3.1 compliant validation : for each category, it checks the
      77              :  * combination of each byte to make sure it maps to a valid range. It also
      78              :  * returns -1 for the following UCS values: ucs > 0x10ffff ucs & 0xfffe =
      79              :  * 0xfffe 0xfdd0 < ucs < 0xfdef ucs & 0xdb00 = 0xd800 (surrogates)
      80              :  */
      81              : static int
      82     11493947 : utf_charcheck(const unsigned char *c)
      83              : {
      84     11493947 :     if ((*c & 0x80) == 0)
      85     11492659 :         return 1;
      86         1288 :     else if ((*c & 0xe0) == 0xc0)
      87              :     {
      88              :         /* two-byte char */
      89         1114 :         if (((c[1] & 0xc0) == 0x80) && ((c[0] & 0x1f) > 0x01))
      90         1102 :             return 2;
      91           12 :         return -1;
      92              :     }
      93          174 :     else if ((*c & 0xf0) == 0xe0)
      94              :     {
      95              :         /* three-byte char */
      96          159 :         if (((c[1] & 0xc0) == 0x80) &&
      97          159 :             (((c[0] & 0x0f) != 0x00) || ((c[1] & 0x20) == 0x20)) &&
      98          159 :             ((c[2] & 0xc0) == 0x80))
      99              :         {
     100          159 :             int         z = c[0] & 0x0f;
     101          159 :             int         yx = ((c[1] & 0x3f) << 6) | (c[0] & 0x3f);
     102          159 :             int         lx = yx & 0x7f;
     103              : 
     104              :             /* check 0xfffe/0xffff, 0xfdd0..0xfedf range, surrogates */
     105          159 :             if (((z == 0x0f) &&
     106           24 :                  (((yx & 0xffe) == 0xffe) ||
     107          159 :                   (((yx & 0xf80) == 0xd80) && (lx >= 0x30) && (lx <= 0x4f)))) ||
     108            0 :                 ((z == 0x0d) && ((yx & 0xb00) == 0x800)))
     109            0 :                 return -1;
     110          159 :             return 3;
     111              :         }
     112            0 :         return -1;
     113              :     }
     114           15 :     else if ((*c & 0xf8) == 0xf0)
     115              :     {
     116           15 :         int         u = ((c[0] & 0x07) << 2) | ((c[1] & 0x30) >> 4);
     117              : 
     118              :         /* four-byte char */
     119           15 :         if (((c[1] & 0xc0) == 0x80) &&
     120           15 :             (u > 0x00) && (u <= 0x10) &&
     121           15 :             ((c[2] & 0xc0) == 0x80) && ((c[3] & 0xc0) == 0x80))
     122              :         {
     123              :             /* test for 0xzzzzfffe/0xzzzzfffff */
     124           15 :             if (((c[1] & 0x0f) == 0x0f) && ((c[2] & 0x3f) == 0x3f) &&
     125            0 :                 ((c[3] & 0x3e) == 0x3e))
     126            0 :                 return -1;
     127           15 :             return 4;
     128              :         }
     129            0 :         return -1;
     130              :     }
     131            0 :     return -1;
     132              : }
     133              : 
     134              : 
     135              : static void
     136      2531853 : mb_utf_validate(unsigned char *pwcs)
     137              : {
     138      2531853 :     unsigned char *p = pwcs;
     139              : 
     140     14025800 :     while (*pwcs)
     141              :     {
     142              :         int         len;
     143              : 
     144     11493947 :         if ((len = utf_charcheck(pwcs)) > 0)
     145              :         {
     146     11493935 :             if (p != pwcs)
     147              :             {
     148              :                 int         i;
     149              : 
     150            0 :                 for (i = 0; i < len; i++)
     151            0 :                     *p++ = *pwcs++;
     152              :             }
     153              :             else
     154              :             {
     155     11493935 :                 pwcs += len;
     156     11493935 :                 p += len;
     157              :             }
     158              :         }
     159              :         else
     160              :             /* we skip the char */
     161           12 :             pwcs++;
     162              :     }
     163      2531853 :     if (p != pwcs)
     164           12 :         *p = '\0';
     165      2531853 : }
     166              : 
     167              : /*
     168              :  * public functions : wcswidth and mbvalidate
     169              :  */
     170              : 
     171              : /*
     172              :  * pg_wcswidth is the dumb display-width function.
     173              :  * It assumes that everything will appear on one line.
     174              :  * OTOH it is easier to use than pg_wcssize if this applies to you.
     175              :  */
     176              : int
     177         2309 : pg_wcswidth(const char *pwcs, size_t len, int encoding)
     178              : {
     179         2309 :     int         width = 0;
     180              : 
     181        23407 :     while (len > 0)
     182              :     {
     183              :         int         chlen,
     184              :                     chwidth;
     185              : 
     186        21098 :         chlen = PQmblen(pwcs, encoding);
     187        21098 :         if (len < (size_t) chlen)
     188            0 :             break;              /* Invalid string */
     189              : 
     190        21098 :         chwidth = PQdsplen(pwcs, encoding);
     191        21098 :         if (chwidth > 0)
     192        21098 :             width += chwidth;
     193              : 
     194        21098 :         pwcs += chlen;
     195        21098 :         len -= chlen;
     196              :     }
     197         2309 :     return width;
     198              : }
     199              : 
     200              : /*
     201              :  * pg_wcssize takes the given string in the given encoding and returns three
     202              :  * values:
     203              :  *    result_width: Width in display characters of the longest line in string
     204              :  *    result_height: Number of lines in display output
     205              :  *    result_format_size: Number of bytes required to store formatted
     206              :  *      representation of string
     207              :  *
     208              :  * This MUST be kept in sync with pg_wcsformat!
     209              :  */
     210              : void
     211       699739 : pg_wcssize(const unsigned char *pwcs, size_t len, int encoding,
     212              :            int *result_width, int *result_height, int *result_format_size)
     213              : {
     214              :     int         w,
     215       699739 :                 chlen = 0,
     216       699739 :                 linewidth = 0;
     217       699739 :     int         width = 0;
     218       699739 :     int         height = 1;
     219       699739 :     int         format_size = 0;
     220              : 
     221      8409361 :     for (; *pwcs && len > 0; pwcs += chlen)
     222              :     {
     223      7709622 :         chlen = PQmblen((const char *) pwcs, encoding);
     224      7709622 :         if (len < (size_t) chlen)
     225            0 :             break;
     226      7709622 :         w = PQdsplen((const char *) pwcs, encoding);
     227              : 
     228      7709622 :         if (chlen == 1)         /* single-byte char */
     229              :         {
     230      7708346 :             if (*pwcs == '\n')  /* Newline */
     231              :             {
     232        10278 :                 if (linewidth > width)
     233         2661 :                     width = linewidth;
     234        10278 :                 linewidth = 0;
     235        10278 :                 height += 1;
     236        10278 :                 format_size += 1;   /* For NUL char */
     237              :             }
     238      7698068 :             else if (*pwcs == '\r') /* Linefeed */
     239              :             {
     240            4 :                 linewidth += 2;
     241            4 :                 format_size += 2;
     242              :             }
     243      7698064 :             else if (*pwcs == '\t') /* Tab */
     244              :             {
     245              :                 do
     246              :                 {
     247          855 :                     linewidth++;
     248          855 :                     format_size++;
     249          855 :                 } while (linewidth % 8 != 0);
     250              :             }
     251      7697955 :             else if (w < 0)      /* Other control char */
     252              :             {
     253           39 :                 linewidth += 4;
     254           39 :                 format_size += 4;
     255              :             }
     256              :             else                /* Output it as-is */
     257              :             {
     258      7697916 :                 linewidth += w;
     259      7697916 :                 format_size += 1;
     260              :             }
     261              :         }
     262         1276 :         else if (w < 0)          /* Non-ascii control char */
     263              :         {
     264            0 :             linewidth += 6;     /* \u0000 */
     265            0 :             format_size += 6;
     266              :         }
     267              :         else                    /* All other chars */
     268              :         {
     269         1276 :             linewidth += w;
     270         1276 :             format_size += chlen;
     271              :         }
     272      7709622 :         len -= chlen;
     273              :     }
     274       699739 :     if (linewidth > width)
     275       651369 :         width = linewidth;
     276       699739 :     format_size += 1;           /* For NUL char */
     277              : 
     278              :     /* Set results */
     279       699739 :     if (result_width)
     280       699729 :         *result_width = width;
     281       699739 :     if (result_height)
     282       699739 :         *result_height = height;
     283       699739 :     if (result_format_size)
     284       696504 :         *result_format_size = format_size;
     285       699739 : }
     286              : 
     287              : /*
     288              :  *  Format a string into one or more "struct lineptr" lines.
     289              :  *  lines[i].ptr == NULL indicates the end of the array.
     290              :  *
     291              :  * This MUST be kept in sync with pg_wcssize!
     292              :  */
     293              : void
     294       697476 : pg_wcsformat(const unsigned char *pwcs, size_t len, int encoding,
     295              :              struct lineptr *lines, int count)
     296              : {
     297              :     int         w,
     298       697476 :                 chlen = 0;
     299       697476 :     int         linewidth = 0;
     300       697476 :     unsigned char *ptr = lines->ptr; /* Pointer to data area */
     301              : 
     302      8335793 :     for (; *pwcs && len > 0; pwcs += chlen)
     303              :     {
     304      7638317 :         chlen = PQmblen((const char *) pwcs, encoding);
     305      7638317 :         if (len < (size_t) chlen)
     306            0 :             break;
     307      7638317 :         w = PQdsplen((const char *) pwcs, encoding);
     308              : 
     309      7638317 :         if (chlen == 1)         /* single-byte char */
     310              :         {
     311      7637041 :             if (*pwcs == '\n')  /* Newline */
     312              :             {
     313        10386 :                 *ptr++ = '\0';
     314        10386 :                 lines->width = linewidth;
     315        10386 :                 linewidth = 0;
     316        10386 :                 lines++;
     317        10386 :                 count--;
     318        10386 :                 if (count <= 0)
     319            0 :                     exit(1);    /* Screwup */
     320              : 
     321              :                 /* make next line point to remaining memory */
     322        10386 :                 lines->ptr = ptr;
     323              :             }
     324      7626655 :             else if (*pwcs == '\r') /* Linefeed */
     325              :             {
     326            4 :                 strcpy((char *) ptr, "\\r");
     327            4 :                 linewidth += 2;
     328            4 :                 ptr += 2;
     329              :             }
     330      7626651 :             else if (*pwcs == '\t') /* Tab */
     331              :             {
     332              :                 do
     333              :                 {
     334          855 :                     *ptr++ = ' ';
     335          855 :                     linewidth++;
     336          855 :                 } while (linewidth % 8 != 0);
     337              :             }
     338      7626542 :             else if (w < 0)      /* Other control char */
     339              :             {
     340           39 :                 sprintf((char *) ptr, "\\x%02X", *pwcs);
     341           39 :                 linewidth += 4;
     342           39 :                 ptr += 4;
     343              :             }
     344              :             else                /* Output it as-is */
     345              :             {
     346      7626503 :                 linewidth += w;
     347      7626503 :                 *ptr++ = *pwcs;
     348              :             }
     349              :         }
     350         1276 :         else if (w < 0)          /* Non-ascii control char */
     351              :         {
     352            0 :             if (encoding == PG_UTF8)
     353            0 :                 sprintf((char *) ptr, "\\u%04X", utf8_to_unicode(pwcs));
     354              :             else
     355              :             {
     356              :                 /*
     357              :                  * This case cannot happen in the current code because only
     358              :                  * UTF-8 signals multibyte control characters. But we may need
     359              :                  * to support it at some stage
     360              :                  */
     361            0 :                 sprintf((char *) ptr, "\\u????");
     362              :             }
     363            0 :             ptr += 6;
     364            0 :             linewidth += 6;
     365              :         }
     366              :         else                    /* All other chars */
     367              :         {
     368              :             int         i;
     369              : 
     370         4017 :             for (i = 0; i < chlen; i++)
     371         2741 :                 *ptr++ = pwcs[i];
     372         1276 :             linewidth += w;
     373              :         }
     374      7638317 :         len -= chlen;
     375              :     }
     376       697476 :     lines->width = linewidth;
     377       697476 :     *ptr++ = '\0';              /* Terminate formatted string */
     378              : 
     379       697476 :     if (count <= 0)
     380            0 :         exit(1);                /* Screwup */
     381              : 
     382       697476 :     (lines + 1)->ptr = NULL; /* terminate line array */
     383       697476 : }
     384              : 
     385              : 
     386              : /*
     387              :  * Encoding validation: delete any unvalidatable characters from the string
     388              :  *
     389              :  * This seems redundant with existing functionality elsewhere?
     390              :  */
     391              : unsigned char *
     392      2533504 : mbvalidate(unsigned char *pwcs, int encoding)
     393              : {
     394      2533504 :     if (encoding == PG_UTF8)
     395      2531853 :         mb_utf_validate(pwcs);
     396              :     else
     397              :     {
     398              :         /*
     399              :          * other encodings needing validation should add their own routines
     400              :          * here
     401              :          */
     402              :     }
     403              : 
     404      2533504 :     return pwcs;
     405              : }
        

Generated by: LCOV version 2.0-1