LCOV - code coverage report
Current view: top level - src/bin/psql - prompt.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 190 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 1 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 133 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * psql - the PostgreSQL interactive terminal
       3                 :             :  *
       4                 :             :  * Copyright (c) 2000-2026, PostgreSQL Global Development Group
       5                 :             :  *
       6                 :             :  * src/bin/psql/prompt.c
       7                 :             :  */
       8                 :             : #include "postgres_fe.h"
       9                 :             : 
      10                 :             : #ifdef WIN32
      11                 :             : #include <io.h>
      12                 :             : #include <win32.h>
      13                 :             : #endif
      14                 :             : 
      15                 :             : #include "common.h"
      16                 :             : #include "common/string.h"
      17                 :             : #include "input.h"
      18                 :             : #include "libpq/pqcomm.h"
      19                 :             : #include "prompt.h"
      20                 :             : #include "settings.h"
      21                 :             : 
      22                 :             : /*--------------------------
      23                 :             :  * get_prompt
      24                 :             :  *
      25                 :             :  * Returns a statically allocated prompt made by interpolating certain
      26                 :             :  * tcsh style escape sequences into pset.vars "PROMPT1|2|3".
      27                 :             :  * (might not be completely multibyte safe)
      28                 :             :  *
      29                 :             :  * Defined interpolations are:
      30                 :             :  * %M - database server "hostname.domainname", "[local]" for AF_UNIX
      31                 :             :  *              sockets, "[local:/dir/name]" if not default
      32                 :             :  * %m - like %M, but hostname only (before first dot), or always "[local]"
      33                 :             :  * %p - backend pid
      34                 :             :  * %P - pipeline status: on, off or abort
      35                 :             :  * %> - database server port number
      36                 :             :  * %n - database user name
      37                 :             :  * %S - search_path
      38                 :             :  * %s - service
      39                 :             :  * %/ - current database
      40                 :             :  * %~ - like %/ but "~" when database name equals user name
      41                 :             :  * %w - whitespace of the same width as the most recent output of PROMPT1
      42                 :             :  * %# - "#" if superuser, ">" otherwise
      43                 :             :  * %R - in prompt1 normally =, or ^ if single line mode,
      44                 :             :  *                      or a ! if session is not connected to a database;
      45                 :             :  *              in prompt2 -, *, ', or ";
      46                 :             :  *              in prompt3 nothing
      47                 :             :  * %x - transaction status: empty, *, !, ? (unknown or no connection)
      48                 :             :  * %l - The line number inside the current statement, starting from 1.
      49                 :             :  * %? - the error code of the last query (not yet implemented)
      50                 :             :  * %% - a percent sign
      51                 :             :  *
      52                 :             :  * %[0-9]                  - the character with the given decimal code
      53                 :             :  * %0[0-7]                 - the character with the given octal code
      54                 :             :  * %0x[0-9A-Fa-f]  - the character with the given hexadecimal code
      55                 :             :  *
      56                 :             :  * %`command`      - The result of executing command in /bin/sh with trailing
      57                 :             :  *                                       newline stripped.
      58                 :             :  * %:name:                 - The value of the psql variable 'name'
      59                 :             :  * (those will not be rescanned for more escape sequences!)
      60                 :             :  *
      61                 :             :  * %[ ... %]       - tell readline that the contained text is invisible
      62                 :             :  *
      63                 :             :  * If the application-wide prompts become NULL somehow, the returned string
      64                 :             :  * will be empty (not NULL!).
      65                 :             :  *--------------------------
      66                 :             :  */
      67                 :             : 
      68                 :             : char *
      69                 :           0 : get_prompt(promptStatus_t status, ConditionalStack cstack)
      70                 :             : {
      71                 :             : #define MAX_PROMPT_SIZE 256
      72                 :             :         static char destination[MAX_PROMPT_SIZE + 1];
      73                 :           0 :         char            buf[MAX_PROMPT_SIZE + 1];
      74                 :           0 :         bool            esc = false;
      75                 :           0 :         const char *p;
      76                 :           0 :         const char *prompt_string = "? ";
      77                 :             :         static size_t last_prompt1_width = 0;
      78                 :             : 
      79   [ #  #  #  # ]:           0 :         switch (status)
      80                 :             :         {
      81                 :             :                 case PROMPT_READY:
      82                 :           0 :                         prompt_string = pset.prompt1;
      83                 :           0 :                         break;
      84                 :             : 
      85                 :             :                 case PROMPT_CONTINUE:
      86                 :             :                 case PROMPT_SINGLEQUOTE:
      87                 :             :                 case PROMPT_DOUBLEQUOTE:
      88                 :             :                 case PROMPT_DOLLARQUOTE:
      89                 :             :                 case PROMPT_COMMENT:
      90                 :             :                 case PROMPT_PAREN:
      91                 :           0 :                         prompt_string = pset.prompt2;
      92                 :           0 :                         break;
      93                 :             : 
      94                 :             :                 case PROMPT_COPY:
      95                 :           0 :                         prompt_string = pset.prompt3;
      96                 :           0 :                         break;
      97                 :             :         }
      98                 :             : 
      99                 :           0 :         destination[0] = '\0';
     100                 :             : 
     101         [ #  # ]:           0 :         for (p = prompt_string;
     102         [ #  # ]:           0 :                  *p && strlen(destination) < sizeof(destination) - 1;
     103                 :           0 :                  p++)
     104                 :             :         {
     105                 :           0 :                 memset(buf, 0, sizeof(buf));
     106         [ #  # ]:           0 :                 if (esc)
     107                 :             :                 {
     108   [ #  #  #  #  :           0 :                         switch (*p)
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     109                 :             :                         {
     110                 :             :                                         /* Current database */
     111                 :             :                                 case '/':
     112         [ #  # ]:           0 :                                         if (pset.db)
     113                 :           0 :                                                 strlcpy(buf, PQdb(pset.db), sizeof(buf));
     114                 :           0 :                                         break;
     115                 :             :                                 case '~':
     116         [ #  # ]:           0 :                                         if (pset.db)
     117                 :             :                                         {
     118                 :           0 :                                                 const char *var;
     119                 :             : 
     120   [ #  #  #  # ]:           0 :                                                 if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
     121         [ #  # ]:           0 :                                                         ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
     122                 :           0 :                                                         strlcpy(buf, "~", sizeof(buf));
     123                 :             :                                                 else
     124                 :           0 :                                                         strlcpy(buf, PQdb(pset.db), sizeof(buf));
     125                 :           0 :                                         }
     126                 :           0 :                                         break;
     127                 :             : 
     128                 :             :                                         /* Whitespace of the same width as the last PROMPT1 */
     129                 :             :                                 case 'w':
     130         [ #  # ]:           0 :                                         if (pset.db)
     131         [ #  # ]:           0 :                                                 memset(buf, ' ',
     132                 :             :                                                            Min(last_prompt1_width, sizeof(buf) - 1));
     133                 :           0 :                                         break;
     134                 :             : 
     135                 :             :                                         /* DB server hostname (long/short) */
     136                 :             :                                 case 'M':
     137                 :             :                                 case 'm':
     138         [ #  # ]:           0 :                                         if (pset.db)
     139                 :             :                                         {
     140                 :           0 :                                                 const char *host = PQhost(pset.db);
     141                 :             : 
     142                 :             :                                                 /* INET socket */
     143   [ #  #  #  #  :           0 :                                                 if (host && host[0] && !is_unixsock_path(host))
                   #  # ]
     144                 :             :                                                 {
     145                 :           0 :                                                         strlcpy(buf, host, sizeof(buf));
     146         [ #  # ]:           0 :                                                         if (*p == 'm')
     147                 :           0 :                                                                 buf[strcspn(buf, ".")] = '\0';
     148                 :           0 :                                                 }
     149                 :             :                                                 /* UNIX socket */
     150                 :             :                                                 else
     151                 :             :                                                 {
     152                 :           0 :                                                         if (!host
     153         [ #  # ]:           0 :                                                                 || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
     154   [ #  #  #  # ]:           0 :                                                                 || *p == 'm')
     155                 :           0 :                                                                 strlcpy(buf, "[local]", sizeof(buf));
     156                 :             :                                                         else
     157                 :           0 :                                                                 snprintf(buf, sizeof(buf), "[local:%s]", host);
     158                 :             :                                                 }
     159                 :           0 :                                         }
     160                 :           0 :                                         break;
     161                 :             :                                         /* DB server port number */
     162                 :             :                                 case '>':
     163   [ #  #  #  # ]:           0 :                                         if (pset.db && PQport(pset.db))
     164                 :           0 :                                                 strlcpy(buf, PQport(pset.db), sizeof(buf));
     165                 :           0 :                                         break;
     166                 :             :                                         /* DB server user name */
     167                 :             :                                 case 'n':
     168         [ #  # ]:           0 :                                         if (pset.db)
     169                 :           0 :                                                 strlcpy(buf, session_username(), sizeof(buf));
     170                 :           0 :                                         break;
     171                 :             :                                         /* search_path */
     172                 :             :                                 case 'S':
     173         [ #  # ]:           0 :                                         if (pset.db)
     174                 :             :                                         {
     175                 :           0 :                                                 const char *sp = PQparameterStatus(pset.db, "search_path");
     176                 :             : 
     177                 :             :                                                 /* Use ? for versions that don't report search_path. */
     178         [ #  # ]:           0 :                                                 strlcpy(buf, sp ? sp : "?", sizeof(buf));
     179                 :           0 :                                         }
     180                 :           0 :                                         break;
     181                 :             :                                         /* service name */
     182                 :             :                                 case 's':
     183                 :             :                                         {
     184                 :           0 :                                                 const char *service_name = GetVariable(pset.vars, "SERVICE");
     185                 :             : 
     186         [ #  # ]:           0 :                                                 if (service_name)
     187                 :           0 :                                                         strlcpy(buf, service_name, sizeof(buf));
     188                 :           0 :                                         }
     189                 :           0 :                                         break;
     190                 :             :                                         /* backend pid */
     191                 :             :                                 case 'p':
     192         [ #  # ]:           0 :                                         if (pset.db)
     193                 :             :                                         {
     194                 :           0 :                                                 int                     pid = PQbackendPID(pset.db);
     195                 :             : 
     196         [ #  # ]:           0 :                                                 if (pid)
     197                 :           0 :                                                         snprintf(buf, sizeof(buf), "%d", pid);
     198                 :           0 :                                         }
     199                 :           0 :                                         break;
     200                 :             :                                         /* pipeline status */
     201                 :             :                                 case 'P':
     202                 :             :                                         {
     203                 :           0 :                                                 PGpipelineStatus status = PQpipelineStatus(pset.db);
     204                 :             : 
     205         [ #  # ]:           0 :                                                 if (status == PQ_PIPELINE_ON)
     206                 :           0 :                                                         strlcpy(buf, "on", sizeof(buf));
     207         [ #  # ]:           0 :                                                 else if (status == PQ_PIPELINE_ABORTED)
     208                 :           0 :                                                         strlcpy(buf, "abort", sizeof(buf));
     209                 :             :                                                 else
     210                 :           0 :                                                         strlcpy(buf, "off", sizeof(buf));
     211                 :             :                                                 break;
     212                 :           0 :                                         }
     213                 :             : 
     214                 :             :                                 case '0':
     215                 :             :                                 case '1':
     216                 :             :                                 case '2':
     217                 :             :                                 case '3':
     218                 :             :                                 case '4':
     219                 :             :                                 case '5':
     220                 :             :                                 case '6':
     221                 :             :                                 case '7':
     222                 :           0 :                                         *buf = (char) strtol(p, unconstify(char **, &p), 8);
     223                 :           0 :                                         --p;
     224                 :           0 :                                         break;
     225                 :             :                                 case 'R':
     226   [ #  #  #  #  :           0 :                                         switch (status)
             #  #  #  # ]
     227                 :             :                                         {
     228                 :             :                                                 case PROMPT_READY:
     229   [ #  #  #  # ]:           0 :                                                         if (cstack != NULL && !conditional_active(cstack))
     230                 :           0 :                                                                 buf[0] = '@';
     231         [ #  # ]:           0 :                                                         else if (!pset.db)
     232                 :           0 :                                                                 buf[0] = '!';
     233         [ #  # ]:           0 :                                                         else if (!pset.singleline)
     234                 :           0 :                                                                 buf[0] = '=';
     235                 :             :                                                         else
     236                 :           0 :                                                                 buf[0] = '^';
     237                 :           0 :                                                         break;
     238                 :             :                                                 case PROMPT_CONTINUE:
     239                 :           0 :                                                         buf[0] = '-';
     240                 :           0 :                                                         break;
     241                 :             :                                                 case PROMPT_SINGLEQUOTE:
     242                 :           0 :                                                         buf[0] = '\'';
     243                 :           0 :                                                         break;
     244                 :             :                                                 case PROMPT_DOUBLEQUOTE:
     245                 :           0 :                                                         buf[0] = '"';
     246                 :           0 :                                                         break;
     247                 :             :                                                 case PROMPT_DOLLARQUOTE:
     248                 :           0 :                                                         buf[0] = '$';
     249                 :           0 :                                                         break;
     250                 :             :                                                 case PROMPT_COMMENT:
     251                 :           0 :                                                         buf[0] = '*';
     252                 :           0 :                                                         break;
     253                 :             :                                                 case PROMPT_PAREN:
     254                 :           0 :                                                         buf[0] = '(';
     255                 :           0 :                                                         break;
     256                 :             :                                                 default:
     257                 :           0 :                                                         buf[0] = '\0';
     258                 :           0 :                                                         break;
     259                 :             :                                         }
     260                 :           0 :                                         break;
     261                 :             : 
     262                 :             :                                 case 'x':
     263         [ #  # ]:           0 :                                         if (!pset.db)
     264                 :           0 :                                                 buf[0] = '?';
     265                 :             :                                         else
     266   [ #  #  #  # ]:           0 :                                                 switch (PQtransactionStatus(pset.db))
     267                 :             :                                                 {
     268                 :             :                                                         case PQTRANS_IDLE:
     269                 :           0 :                                                                 buf[0] = '\0';
     270                 :           0 :                                                                 break;
     271                 :             :                                                         case PQTRANS_ACTIVE:
     272                 :             :                                                         case PQTRANS_INTRANS:
     273                 :           0 :                                                                 buf[0] = '*';
     274                 :           0 :                                                                 break;
     275                 :             :                                                         case PQTRANS_INERROR:
     276                 :           0 :                                                                 buf[0] = '!';
     277                 :           0 :                                                                 break;
     278                 :             :                                                         default:
     279                 :           0 :                                                                 buf[0] = '?';
     280                 :           0 :                                                                 break;
     281                 :             :                                                 }
     282                 :           0 :                                         break;
     283                 :             : 
     284                 :             :                                 case 'l':
     285                 :           0 :                                         snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno);
     286                 :           0 :                                         break;
     287                 :             : 
     288                 :             :                                 case '?':
     289                 :             :                                         /* not here yet */
     290                 :             :                                         break;
     291                 :             : 
     292                 :             :                                 case '#':
     293         [ #  # ]:           0 :                                         if (is_superuser())
     294                 :           0 :                                                 buf[0] = '#';
     295                 :             :                                         else
     296                 :           0 :                                                 buf[0] = '>';
     297                 :           0 :                                         break;
     298                 :             : 
     299                 :             :                                         /* execute command */
     300                 :             :                                 case '`':
     301                 :             :                                         {
     302                 :           0 :                                                 int                     cmdend = strcspn(p + 1, "`");
     303                 :           0 :                                                 char       *file = pnstrdup(p + 1, cmdend);
     304                 :           0 :                                                 FILE       *fd;
     305                 :             : 
     306                 :           0 :                                                 fflush(NULL);
     307                 :           0 :                                                 fd = popen(file, "r");
     308         [ #  # ]:           0 :                                                 if (fd)
     309                 :             :                                                 {
     310         [ #  # ]:           0 :                                                         if (fgets(buf, sizeof(buf), fd) == NULL)
     311                 :           0 :                                                                 buf[0] = '\0';
     312                 :           0 :                                                         pclose(fd);
     313                 :           0 :                                                 }
     314                 :             : 
     315                 :             :                                                 /* strip trailing newline and carriage return */
     316                 :           0 :                                                 (void) pg_strip_crlf(buf);
     317                 :             : 
     318                 :           0 :                                                 free(file);
     319                 :           0 :                                                 p += cmdend + 1;
     320                 :             :                                                 break;
     321                 :           0 :                                         }
     322                 :             : 
     323                 :             :                                         /* interpolate variable */
     324                 :             :                                 case ':':
     325                 :             :                                         {
     326                 :           0 :                                                 int                     nameend = strcspn(p + 1, ":");
     327                 :           0 :                                                 char       *name = pnstrdup(p + 1, nameend);
     328                 :           0 :                                                 const char *val;
     329                 :             : 
     330                 :           0 :                                                 val = GetVariable(pset.vars, name);
     331         [ #  # ]:           0 :                                                 if (val)
     332                 :           0 :                                                         strlcpy(buf, val, sizeof(buf));
     333                 :           0 :                                                 free(name);
     334                 :           0 :                                                 p += nameend + 1;
     335                 :             :                                                 break;
     336                 :           0 :                                         }
     337                 :             : 
     338                 :             :                                 case '[':
     339                 :             :                                 case ']':
     340                 :             : #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
     341                 :             : 
     342                 :             :                                         /*
     343                 :             :                                          * readline >=4.0 undocumented feature: non-printing
     344                 :             :                                          * characters in prompt strings must be marked as such, in
     345                 :             :                                          * order to properly display the line during editing.
     346                 :             :                                          */
     347                 :           0 :                                         buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
     348                 :           0 :                                         buf[1] = '\0';
     349                 :             : #endif                                                  /* USE_READLINE */
     350                 :           0 :                                         break;
     351                 :             : 
     352                 :             :                                 default:
     353                 :           0 :                                         buf[0] = *p;
     354                 :           0 :                                         buf[1] = '\0';
     355                 :           0 :                                         break;
     356                 :             :                         }
     357                 :           0 :                         esc = false;
     358                 :           0 :                 }
     359         [ #  # ]:           0 :                 else if (*p == '%')
     360                 :           0 :                         esc = true;
     361                 :             :                 else
     362                 :             :                 {
     363                 :           0 :                         buf[0] = *p;
     364                 :           0 :                         buf[1] = '\0';
     365                 :           0 :                         esc = false;
     366                 :             :                 }
     367                 :             : 
     368         [ #  # ]:           0 :                 if (!esc)
     369                 :           0 :                         strlcat(destination, buf, sizeof(destination));
     370                 :           0 :         }
     371                 :             : 
     372                 :             :         /* Compute the visible width of PROMPT1, for PROMPT2's %w */
     373         [ #  # ]:           0 :         if (prompt_string == pset.prompt1)
     374                 :             :         {
     375                 :           0 :                 char       *p = destination;
     376                 :           0 :                 char       *end = p + strlen(p);
     377                 :           0 :                 bool            visible = true;
     378                 :             : 
     379                 :           0 :                 last_prompt1_width = 0;
     380         [ #  # ]:           0 :                 while (*p)
     381                 :             :                 {
     382                 :             : #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
     383         [ #  # ]:           0 :                         if (*p == RL_PROMPT_START_IGNORE)
     384                 :             :                         {
     385                 :           0 :                                 visible = false;
     386                 :           0 :                                 ++p;
     387                 :           0 :                         }
     388         [ #  # ]:           0 :                         else if (*p == RL_PROMPT_END_IGNORE)
     389                 :             :                         {
     390                 :           0 :                                 visible = true;
     391                 :           0 :                                 ++p;
     392                 :           0 :                         }
     393                 :             :                         else
     394                 :             : #endif
     395                 :             :                         {
     396                 :           0 :                                 int                     chlen,
     397                 :             :                                                         chwidth;
     398                 :             : 
     399                 :           0 :                                 chlen = PQmblen(p, pset.encoding);
     400         [ #  # ]:           0 :                                 if (p + chlen > end)
     401                 :           0 :                                         break;          /* Invalid string */
     402                 :             : 
     403         [ #  # ]:           0 :                                 if (visible)
     404                 :             :                                 {
     405                 :           0 :                                         chwidth = PQdsplen(p, pset.encoding);
     406                 :             : 
     407         [ #  # ]:           0 :                                         if (*p == '\n')
     408                 :           0 :                                                 last_prompt1_width = 0;
     409         [ #  # ]:           0 :                                         else if (chwidth > 0)
     410                 :           0 :                                                 last_prompt1_width += chwidth;
     411                 :           0 :                                 }
     412                 :             : 
     413                 :           0 :                                 p += chlen;
     414      [ #  #  # ]:           0 :                         }
     415                 :             :                 }
     416                 :           0 :         }
     417                 :             : 
     418                 :           0 :         return destination;
     419                 :           0 : }
        

Generated by: LCOV version 2.3.2-1