LCOV - differential code coverage report
Current view: top level - src/bin/psql - stringutils.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 86.5 % 111 96 15 96
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 3 3 3
Baseline: 16@8cea358b128 Branches: 74.0 % 96 71 25 71
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 86.5 % 111 96 15 96
Function coverage date bins:
(240..) days: 100.0 % 3 3 3
Branch coverage date bins:
(240..) days: 74.0 % 96 71 25 71

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * psql - the PostgreSQL interactive terminal
                                  3                 :                :  *
                                  4                 :                :  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
                                  5                 :                :  *
                                  6                 :                :  * src/bin/psql/stringutils.c
                                  7                 :                :  */
                                  8                 :                : #include "postgres_fe.h"
                                  9                 :                : 
                                 10                 :                : #include <ctype.h>
                                 11                 :                : 
                                 12                 :                : #include "common.h"
                                 13                 :                : #include "stringutils.h"
                                 14                 :                : 
                                 15                 :                : 
                                 16                 :                : /*
                                 17                 :                :  * Replacement for strtok() (a.k.a. poor man's flex)
                                 18                 :                :  *
                                 19                 :                :  * Splits a string into tokens, returning one token per call, then NULL
                                 20                 :                :  * when no more tokens exist in the given string.
                                 21                 :                :  *
                                 22                 :                :  * The calling convention is similar to that of strtok, but with more
                                 23                 :                :  * frammishes.
                                 24                 :                :  *
                                 25                 :                :  * s -          string to parse, if NULL continue parsing the last string
                                 26                 :                :  * whitespace - set of whitespace characters that separate tokens
                                 27                 :                :  * delim -      set of non-whitespace separator characters (or NULL)
                                 28                 :                :  * quote -      set of characters that can quote a token (NULL if none)
                                 29                 :                :  * escape -     character that can quote quotes (0 if none)
                                 30                 :                :  * e_strings -  if true, treat E'...' syntax as a valid token
                                 31                 :                :  * del_quotes - if true, strip quotes from the returned token, else return
                                 32                 :                :  *              it exactly as found in the string
                                 33                 :                :  * encoding -   the active character-set encoding
                                 34                 :                :  *
                                 35                 :                :  * Characters in 'delim', if any, will be returned as single-character
                                 36                 :                :  * tokens unless part of a quoted token.
                                 37                 :                :  *
                                 38                 :                :  * Double occurrences of the quoting character are always taken to represent
                                 39                 :                :  * a single quote character in the data.  If escape isn't 0, then escape
                                 40                 :                :  * followed by anything (except \0) is a data character too.
                                 41                 :                :  *
                                 42                 :                :  * The combination of e_strings and del_quotes both true is not currently
                                 43                 :                :  * handled.  This could be fixed but it's not needed anywhere at the moment.
                                 44                 :                :  *
                                 45                 :                :  * Note that the string s is _not_ overwritten in this implementation.
                                 46                 :                :  *
                                 47                 :                :  * NB: it's okay to vary delim, quote, and escape from one call to the
                                 48                 :                :  * next on a single source string, but changing whitespace is a bad idea
                                 49                 :                :  * since you might lose data.
                                 50                 :                :  */
                                 51                 :                : char *
 8928 bruce@momjian.us           52                 :CBC         507 : strtokx(const char *s,
                                 53                 :                :         const char *whitespace,
                                 54                 :                :         const char *delim,
                                 55                 :                :         const char *quote,
                                 56                 :                :         char escape,
                                 57                 :                :         bool e_strings,
                                 58                 :                :         bool del_quotes,
                                 59                 :                :         int encoding)
                                 60                 :                : {
                                 61                 :                :     static char *storage = NULL;    /* store the local copy of the users
                                 62                 :                :                                      * string here */
                                 63                 :                :     static char *string = NULL; /* pointer into storage where to continue on
                                 64                 :                :                                  * next call */
                                 65                 :                : 
                                 66                 :                :     /* variously abused variables: */
                                 67                 :                :     unsigned int offset;
                                 68                 :                :     char       *start;
                                 69                 :                :     char       *p;
                                 70                 :                : 
                                 71         [ +  + ]:            507 :     if (s)
                                 72                 :                :     {
                                 73                 :             93 :         free(storage);
                                 74                 :                : 
                                 75                 :                :         /*
                                 76                 :                :          * We may need extra space to insert delimiter nulls for adjacent
                                 77                 :                :          * tokens.  2X the space is a gross overestimate, but it's unlikely
                                 78                 :                :          * that this code will be used on huge strings anyway.
                                 79                 :                :          */
 7385 neilc@samurai.com          80                 :             93 :         storage = pg_malloc(2 * strlen(s) + 1);
 7848 tgl@sss.pgh.pa.us          81                 :             93 :         strcpy(storage, s);
 8928 bruce@momjian.us           82                 :             93 :         string = storage;
                                 83                 :                :     }
                                 84                 :                : 
                                 85         [ -  + ]:            507 :     if (!storage)
 8928 bruce@momjian.us           86                 :UBC           0 :         return NULL;
                                 87                 :                : 
                                 88                 :                :     /* skip leading whitespace */
 7848 tgl@sss.pgh.pa.us          89                 :CBC         507 :     offset = strspn(string, whitespace);
                                 90                 :            507 :     start = &string[offset];
                                 91                 :                : 
                                 92                 :                :     /* end of string reached? */
                                 93         [ +  + ]:            507 :     if (*start == '\0')
                                 94                 :                :     {
                                 95                 :                :         /* technically we don't need to free here, but we're nice */
 8928 bruce@momjian.us           96                 :             56 :         free(storage);
                                 97                 :             56 :         storage = NULL;
                                 98                 :             56 :         string = NULL;
                                 99                 :             56 :         return NULL;
                                100                 :                :     }
                                101                 :                : 
                                102                 :                :     /* test if delimiter character */
 7848 tgl@sss.pgh.pa.us         103   [ +  +  +  + ]:            451 :     if (delim && strchr(delim, *start))
                                104                 :                :     {
                                105                 :                :         /*
                                106                 :                :          * If not at end of string, we need to insert a null to terminate the
                                107                 :                :          * returned token.  We can just overwrite the next character if it
                                108                 :                :          * happens to be in the whitespace set ... otherwise move over the
                                109                 :                :          * rest of the string to make room.  (This is why we allocated extra
                                110                 :                :          * space above).
                                111                 :                :          */
                                112                 :             42 :         p = start + 1;
                                113         [ +  - ]:             42 :         if (*p != '\0')
                                114                 :                :         {
                                115         [ +  + ]:             42 :             if (!strchr(whitespace, *p))
                                116                 :             24 :                 memmove(p + 1, p, strlen(p) + 1);
                                117                 :             42 :             *p = '\0';
                                118                 :             42 :             string = p + 1;
                                119                 :                :         }
                                120                 :                :         else
                                121                 :                :         {
                                122                 :                :             /* at end of string, so no extra work */
 7848 tgl@sss.pgh.pa.us         123                 :UBC           0 :             string = p;
                                124                 :                :         }
                                125                 :                : 
 7848 tgl@sss.pgh.pa.us         126                 :CBC          42 :         return start;
                                127                 :                :     }
                                128                 :                : 
                                129                 :                :     /* check for E string */
 6527                           130                 :            409 :     p = start;
                                131         [ +  + ]:            409 :     if (e_strings &&
                                132   [ +  -  -  + ]:            141 :         (*p == 'E' || *p == 'e') &&
 6527 tgl@sss.pgh.pa.us         133         [ #  # ]:UBC           0 :         p[1] == '\'')
                                134                 :                :     {
                                135                 :              0 :         quote = "'";
                                136                 :              0 :         escape = '\\';          /* if std strings before, not any more */
                                137                 :              0 :         p++;
                                138                 :                :     }
                                139                 :                : 
                                140                 :                :     /* test if quoting character */
 6527 tgl@sss.pgh.pa.us         141   [ +  +  +  + ]:CBC         409 :     if (quote && strchr(quote, *p))
                                142                 :                :     {
                                143                 :                :         /* okay, we have a quoted token, now scan for the closer */
                                144                 :             78 :         char        thisquote = *p++;
                                145                 :                : 
 1042                           146         [ +  + ]:           1331 :         for (; *p; p += PQmblenBounded(p, encoding))
                                147                 :                :         {
 7848                           148   [ -  +  -  - ]:           1325 :             if (*p == escape && p[1] != '\0')
 7848 tgl@sss.pgh.pa.us         149                 :UBC           0 :                 p++;            /* process escaped anything */
 7848 tgl@sss.pgh.pa.us         150   [ +  +  -  + ]:CBC        1325 :             else if (*p == thisquote && p[1] == thisquote)
 7848 tgl@sss.pgh.pa.us         151                 :UBC           0 :                 p++;            /* process doubled quote */
 7848 tgl@sss.pgh.pa.us         152         [ +  + ]:CBC        1325 :             else if (*p == thisquote)
                                153                 :                :             {
                                154                 :             72 :                 p++;            /* skip trailing quote */
                                155                 :             72 :                 break;
                                156                 :                :             }
                                157                 :                :         }
                                158                 :                : 
                                159                 :                :         /*
                                160                 :                :          * If not at end of string, we need to insert a null to terminate the
                                161                 :                :          * returned token.  See notes above.
                                162                 :                :          */
 8928 bruce@momjian.us          163         [ +  + ]:             78 :         if (*p != '\0')
                                164                 :                :         {
 7848 tgl@sss.pgh.pa.us         165         [ +  + ]:             30 :             if (!strchr(whitespace, *p))
                                166                 :             18 :                 memmove(p + 1, p, strlen(p) + 1);
 8928 bruce@momjian.us          167                 :             30 :             *p = '\0';
                                168                 :             30 :             string = p + 1;
                                169                 :                :         }
                                170                 :                :         else
                                171                 :                :         {
                                172                 :                :             /* at end of string, so no extra work */
                                173                 :             48 :             string = p;
                                174                 :                :         }
                                175                 :                : 
                                176                 :                :         /* Clean up the token if caller wants that */
 7848 tgl@sss.pgh.pa.us         177         [ +  + ]:             78 :         if (del_quotes)
                                178                 :              6 :             strip_quotes(start, thisquote, escape, encoding);
                                179                 :                : 
                                180                 :             78 :         return start;
                                181                 :                :     }
                                182                 :                : 
                                183                 :                :     /*
                                184                 :                :      * Otherwise no quoting character.  Scan till next whitespace, delimiter
                                185                 :                :      * or quote.  NB: at this point, *start is known not to be '\0',
                                186                 :                :      * whitespace, delim, or quote, so we will consume at least one character.
                                187                 :                :      */
                                188                 :            331 :     offset = strcspn(start, whitespace);
                                189                 :                : 
                                190         [ +  + ]:            331 :     if (delim)
                                191                 :                :     {
                                192                 :            300 :         unsigned int offset2 = strcspn(start, delim);
                                193                 :                : 
                                194         [ +  + ]:            300 :         if (offset > offset2)
                                195                 :             36 :             offset = offset2;
                                196                 :                :     }
                                197                 :                : 
                                198         [ +  + ]:            331 :     if (quote)
                                199                 :                :     {
                                200                 :            306 :         unsigned int offset2 = strcspn(start, quote);
                                201                 :                : 
                                202         [ +  + ]:            306 :         if (offset > offset2)
                                203                 :             12 :             offset = offset2;
                                204                 :                :     }
                                205                 :                : 
                                206                 :            331 :     p = start + offset;
                                207                 :                : 
                                208                 :                :     /*
                                209                 :                :      * If not at end of string, we need to insert a null to terminate the
                                210                 :                :      * returned token.  See notes above.
                                211                 :                :      */
                                212         [ +  + ]:            331 :     if (*p != '\0')
                                213                 :                :     {
                                214         [ +  + ]:            286 :         if (!strchr(whitespace, *p))
                                215                 :             42 :             memmove(p + 1, p, strlen(p) + 1);
                                216                 :            286 :         *p = '\0';
                                217                 :            286 :         string = p + 1;
                                218                 :                :     }
                                219                 :                :     else
                                220                 :                :     {
                                221                 :                :         /* at end of string, so no extra work */
                                222                 :             45 :         string = p;
                                223                 :                :     }
                                224                 :                : 
                                225                 :            331 :     return start;
                                226                 :                : }
                                227                 :                : 
                                228                 :                : 
                                229                 :                : /*
                                230                 :                :  * strip_quotes
                                231                 :                :  *
                                232                 :                :  * Remove quotes from the string at *source.  Leading and trailing occurrences
                                233                 :                :  * of 'quote' are removed; embedded double occurrences of 'quote' are reduced
                                234                 :                :  * to single occurrences; if 'escape' is not 0 then 'escape' removes special
                                235                 :                :  * significance of next character.
                                236                 :                :  *
                                237                 :                :  * Note that the source string is overwritten in-place.
                                238                 :                :  */
                                239                 :                : void
                                240                 :             52 : strip_quotes(char *source, char quote, char escape, int encoding)
                                241                 :                : {
                                242                 :                :     char       *src;
                                243                 :                :     char       *dst;
                                244                 :                : 
 4139 andrew@dunslane.net       245         [ -  + ]:             52 :     Assert(source != NULL);
                                246         [ -  + ]:             52 :     Assert(quote != '\0');
                                247                 :                : 
 7848 tgl@sss.pgh.pa.us         248                 :             52 :     src = dst = source;
                                249                 :                : 
                                250   [ +  -  +  + ]:             52 :     if (*src && *src == quote)
                                251                 :             51 :         src++;                  /* skip leading quote */
                                252                 :                : 
                                253         [ +  + ]:           1355 :     while (*src)
                                254                 :                :     {
                                255                 :           1348 :         char        c = *src;
                                256                 :                :         int         i;
                                257                 :                : 
                                258   [ +  +  -  + ]:           1348 :         if (c == quote && src[1] == '\0')
                                259                 :                :             break;              /* skip trailing quote */
                                260   [ -  +  -  - ]:           1303 :         else if (c == quote && src[1] == quote)
 7848 tgl@sss.pgh.pa.us         261                 :UBC           0 :             src++;              /* process doubled quote */
 7848 tgl@sss.pgh.pa.us         262   [ -  +  -  - ]:CBC        1303 :         else if (c == escape && src[1] != '\0')
 7848 tgl@sss.pgh.pa.us         263                 :UBC           0 :             src++;              /* process escaped character */
                                264                 :                : 
 1042 tgl@sss.pgh.pa.us         265                 :CBC        1303 :         i = PQmblenBounded(src, encoding);
 7848                           266         [ +  + ]:           2606 :         while (i--)
                                267                 :           1303 :             *dst++ = *src++;
                                268                 :                :     }
                                269                 :                : 
                                270                 :             52 :     *dst = '\0';
 8928 bruce@momjian.us          271                 :             52 : }
                                272                 :                : 
                                273                 :                : 
                                274                 :                : /*
                                275                 :                :  * quote_if_needed
                                276                 :                :  *
                                277                 :                :  * Opposite of strip_quotes().  If "source" denotes itself literally without
                                278                 :                :  * quoting or escaping, returns NULL.  Otherwise, returns a malloc'd copy with
                                279                 :                :  * quoting and escaping applied:
                                280                 :                :  *
                                281                 :                :  * source -         string to parse
                                282                 :                :  * entails_quote -  any of these present?  need outer quotes
                                283                 :                :  * quote -          doubled within string, affixed to both ends
                                284                 :                :  * escape -         doubled within string
                                285                 :                :  * force_quote -    if true, quote the output even if it doesn't "need" it
                                286                 :                :  * encoding -       the active character-set encoding
                                287                 :                :  *
                                288                 :                :  * Do not use this as a substitute for PQescapeStringConn().  Use it for
                                289                 :                :  * strings to be parsed by strtokx() or psql_scan_slash_option().
                                290                 :                :  */
                                291                 :                : char *
 4429 alvherre@alvh.no-ip.      292                 :              5 : quote_if_needed(const char *source, const char *entails_quote,
                                293                 :                :                 char quote, char escape, bool force_quote,
                                294                 :                :                 int encoding)
                                295                 :                : {
                                296                 :                :     const char *src;
                                297                 :                :     char       *ret;
                                298                 :                :     char       *dst;
 1543 tgl@sss.pgh.pa.us         299                 :              5 :     bool        need_quotes = force_quote;
                                300                 :                : 
 4139 andrew@dunslane.net       301         [ -  + ]:              5 :     Assert(source != NULL);
                                302         [ -  + ]:              5 :     Assert(quote != '\0');
                                303                 :                : 
 4429 alvherre@alvh.no-ip.      304                 :              5 :     src = source;
 4326 bruce@momjian.us          305                 :              5 :     dst = ret = pg_malloc(2 * strlen(src) + 3); /* excess */
                                306                 :                : 
 4429 alvherre@alvh.no-ip.      307                 :              5 :     *dst++ = quote;
                                308                 :                : 
                                309         [ +  + ]:            101 :     while (*src)
                                310                 :                :     {
                                311                 :             96 :         char        c = *src;
                                312                 :                :         int         i;
                                313                 :                : 
                                314         [ -  + ]:             96 :         if (c == quote)
                                315                 :                :         {
 4429 alvherre@alvh.no-ip.      316                 :UBC           0 :             need_quotes = true;
                                317                 :              0 :             *dst++ = quote;
                                318                 :                :         }
 4429 alvherre@alvh.no-ip.      319         [ -  + ]:CBC          96 :         else if (c == escape)
                                320                 :                :         {
 4429 alvherre@alvh.no-ip.      321                 :UBC           0 :             need_quotes = true;
                                322                 :              0 :             *dst++ = escape;
                                323                 :                :         }
 4429 alvherre@alvh.no-ip.      324         [ -  + ]:CBC          96 :         else if (strchr(entails_quote, c))
 4429 alvherre@alvh.no-ip.      325                 :UBC           0 :             need_quotes = true;
                                326                 :                : 
 1042 tgl@sss.pgh.pa.us         327                 :CBC          96 :         i = PQmblenBounded(src, encoding);
 4429 alvherre@alvh.no-ip.      328         [ +  + ]:            192 :         while (i--)
                                329                 :             96 :             *dst++ = *src++;
                                330                 :                :     }
                                331                 :                : 
                                332                 :              5 :     *dst++ = quote;
                                333                 :              5 :     *dst = '\0';
                                334                 :                : 
                                335         [ +  + ]:              5 :     if (!need_quotes)
                                336                 :                :     {
                                337                 :              2 :         free(ret);
                                338                 :              2 :         ret = NULL;
                                339                 :                :     }
                                340                 :                : 
                                341                 :              5 :     return ret;
                                342                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622