Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * percentrepl.c
4 : : * Common routines to replace percent placeholders in strings
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/common/percentrepl.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #ifndef FRONTEND
17 : : #include "postgres.h"
18 : : #else
19 : : #include "postgres_fe.h"
20 : : #include "common/logging.h"
21 : : #endif
22 : :
23 : : #include "common/percentrepl.h"
24 : : #include "lib/stringinfo.h"
25 : :
26 : : /*
27 : : * replace_percent_placeholders
28 : : *
29 : : * Replace percent-letter placeholders in input string with the supplied
30 : : * values. For example, to replace %f with foo and %b with bar, call
31 : : *
32 : : * replace_percent_placeholders(instr, "param_name", "bf", bar, foo);
33 : : *
34 : : * The return value is palloc'd.
35 : : *
36 : : * "%%" is replaced by a single "%".
37 : : *
38 : : * This throws an error for an unsupported placeholder or a "%" at the end of
39 : : * the input string.
40 : : *
41 : : * A value may be NULL. If the corresponding placeholder is found in the
42 : : * input string, it will be treated as if an unsupported placeholder was used.
43 : : * This allows callers to share a "letters" specification but vary the
44 : : * actually supported placeholders at run time.
45 : : *
46 : : * This functions is meant for cases where all the values are readily
47 : : * available or cheap to compute and most invocations will use most values
48 : : * (for example for archive_command). Also, it requires that all values are
49 : : * strings. It won't be a good match for things like log prefixes or prompts
50 : : * that use a mix of data types and any invocation will only use a few of the
51 : : * possible values.
52 : : *
53 : : * param_name is the name of the underlying GUC parameter, for error
54 : : * reporting. At the moment, this function is only used for GUC parameters.
55 : : * If other kinds of uses were added, the error reporting would need to be
56 : : * revised.
57 : : */
58 : : char *
459 peter@eisentraut.org 59 :CBC 429 : replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
60 : : {
61 : : StringInfoData result;
62 : :
63 : 429 : initStringInfo(&result);
64 : :
65 [ + + ]: 62548 : for (const char *sp = instr; *sp; sp++)
66 : : {
67 [ + + ]: 62119 : if (*sp == '%')
68 : : {
69 [ - + ]: 842 : if (sp[1] == '%')
70 : : {
71 : : /* Convert %% to a single % */
459 peter@eisentraut.org 72 :UBC 0 : sp++;
73 : 0 : appendStringInfoChar(&result, *sp);
74 : : }
459 peter@eisentraut.org 75 [ - + ]:CBC 842 : else if (sp[1] == '\0')
76 : : {
77 : : /* Incomplete escape sequence, expected a character afterward */
78 : : #ifdef FRONTEND
459 peter@eisentraut.org 79 :UBC 0 : pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
80 : 0 : pg_log_error_detail("String ends unexpectedly after escape character \"%%\".");
81 : 0 : exit(1);
82 : : #else
83 [ # # ]: 0 : ereport(ERROR,
84 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
85 : : errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
86 : : errdetail("String ends unexpectedly after escape character \"%%\"."));
87 : : #endif
88 : : }
89 : : else
90 : : {
91 : : /* Look up placeholder character */
459 peter@eisentraut.org 92 :CBC 842 : bool found = false;
93 : : va_list ap;
94 : :
95 : 842 : sp++;
96 : :
97 : 842 : va_start(ap, letters);
98 [ + - ]: 1572 : for (const char *lp = letters; *lp; lp++)
99 : : {
100 : 1572 : char *val = va_arg(ap, char *);
101 : :
102 [ + + ]: 1572 : if (*sp == *lp)
103 : : {
104 [ + - ]: 842 : if (val)
105 : : {
106 : 842 : appendStringInfoString(&result, val);
107 : 842 : found = true;
108 : : }
109 : : /* If val is NULL, we will report an error. */
110 : 842 : break;
111 : : }
112 : : }
113 : 842 : va_end(ap);
114 [ - + ]: 842 : if (!found)
115 : : {
116 : : /* Unknown placeholder */
117 : : #ifdef FRONTEND
459 peter@eisentraut.org 118 :UBC 0 : pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
333 119 : 0 : pg_log_error_detail("String contains unexpected placeholder \"%%%c\".", *sp);
459 120 : 0 : exit(1);
121 : : #else
122 [ # # ]: 0 : ereport(ERROR,
123 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124 : : errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
125 : : errdetail("String contains unexpected placeholder \"%%%c\".", *sp));
126 : : #endif
127 : : }
128 : : }
129 : : }
130 : : else
131 : : {
459 peter@eisentraut.org 132 :CBC 61277 : appendStringInfoChar(&result, *sp);
133 : : }
134 : : }
135 : :
136 : 429 : return result.data;
137 : : }
|