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 *
59 470 : replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
60 : {
61 : StringInfoData result;
62 :
63 470 : initStringInfo(&result);
64 :
65 50824 : for (const char *sp = instr; *sp; sp++)
66 : {
67 50354 : if (*sp == '%')
68 : {
69 904 : if (sp[1] == '%')
70 : {
71 : /* Convert %% to a single % */
72 0 : sp++;
73 0 : appendStringInfoChar(&result, *sp);
74 : }
75 904 : else if (sp[1] == '\0')
76 : {
77 : /* Incomplete escape sequence, expected a character afterward */
78 : #ifdef FRONTEND
79 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 */
92 904 : bool found = false;
93 : va_list ap;
94 :
95 904 : sp++;
96 :
97 904 : va_start(ap, letters);
98 1728 : for (const char *lp = letters; *lp; lp++)
99 : {
100 1728 : char *val = va_arg(ap, char *);
101 :
102 1728 : if (*sp == *lp)
103 : {
104 904 : if (val)
105 : {
106 904 : appendStringInfoString(&result, val);
107 904 : found = true;
108 : }
109 : /* If val is NULL, we will report an error. */
110 904 : break;
111 : }
112 : }
113 904 : va_end(ap);
114 904 : if (!found)
115 : {
116 : /* Unknown placeholder */
117 : #ifdef FRONTEND
118 0 : pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
119 0 : pg_log_error_detail("String contains unexpected placeholder \"%%%c\".", *sp);
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 : {
132 49450 : appendStringInfoChar(&result, *sp);
133 : }
134 : }
135 :
136 470 : return result.data;
137 : }
|