]> granicus.if.org Git - postgresql/blob - src/bin/psql/variables.c
Support non-ASCII letters in psql variable names.
[postgresql] / src / bin / psql / variables.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2011, PostgreSQL Global Development Group
5  *
6  * src/bin/psql/variables.c
7  */
8 #include "postgres_fe.h"
9
10 #include "common.h"
11 #include "variables.h"
12
13
14 /*
15  * Check whether a variable's name is allowed.
16  *
17  * We allow any non-ASCII character, as well as ASCII letters, digits, and
18  * underscore.  Keep this in sync with the definition of variable_char in
19  * psqlscan.l.
20  */
21 static bool
22 valid_variable_name(const char *name)
23 {
24         const unsigned char *ptr = (const unsigned char *) name;
25
26         /* Mustn't be zero-length */
27         if (*ptr == '\0')
28                 return false;
29
30         while (*ptr)
31         {
32                 if (IS_HIGHBIT_SET(*ptr) ||
33                         strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
34                                    "_0123456789", *ptr) != NULL)
35                         ptr++;
36                 else
37                         return false;
38         }
39
40         return true;
41 }
42
43 /*
44  * A "variable space" is represented by an otherwise-unused struct _variable
45  * that serves as list header.
46  */
47 VariableSpace
48 CreateVariableSpace(void)
49 {
50         struct _variable *ptr;
51
52         ptr = pg_malloc(sizeof *ptr);
53         ptr->name = NULL;
54         ptr->value = NULL;
55         ptr->assign_hook = NULL;
56         ptr->next = NULL;
57
58         return ptr;
59 }
60
61 const char *
62 GetVariable(VariableSpace space, const char *name)
63 {
64         struct _variable *current;
65
66         if (!space)
67                 return NULL;
68
69         for (current = space->next; current; current = current->next)
70         {
71                 if (strcmp(current->name, name) == 0)
72                 {
73                         /* this is correct answer when value is NULL, too */
74                         return current->value;
75                 }
76         }
77
78         return NULL;
79 }
80
81 /*
82  * Try to interpret value as boolean value.  Valid values are: true,
83  * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
84  */
85 bool
86 ParseVariableBool(const char *value)
87 {
88         size_t          len;
89
90         if (value == NULL)
91                 return false;                   /* not set -> assume "off" */
92
93         len = strlen(value);
94
95         if (pg_strncasecmp(value, "true", len) == 0)
96                 return true;
97         else if (pg_strncasecmp(value, "false", len) == 0)
98                 return false;
99         else if (pg_strncasecmp(value, "yes", len) == 0)
100                 return true;
101         else if (pg_strncasecmp(value, "no", len) == 0)
102                 return false;
103         /* 'o' is not unique enough */
104         else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
105                 return true;
106         else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
107                 return false;
108         else if (pg_strcasecmp(value, "1") == 0)
109                 return true;
110         else if (pg_strcasecmp(value, "0") == 0)
111                 return false;
112         else
113         {
114                 /* NULL is treated as false, so a non-matching value is 'true' */
115                 psql_error("unrecognized boolean value; assuming \"on\".\n");
116                 return true;
117         }
118         /* suppress compiler warning */
119         return true;
120 }
121
122
123 /*
124  * Read numeric variable, or defaultval if it is not set, or faultval if its
125  * value is not a valid numeric string.  If allowtrail is false, this will
126  * include the case where there are trailing characters after the number.
127  */
128 int
129 ParseVariableNum(const char *val,
130                                  int defaultval,
131                                  int faultval,
132                                  bool allowtrail)
133 {
134         int                     result;
135
136         if (!val)
137                 result = defaultval;
138         else if (!val[0])
139                 result = faultval;
140         else
141         {
142                 char       *end;
143
144                 result = strtol(val, &end, 0);
145                 if (!allowtrail && *end)
146                         result = faultval;
147         }
148
149         return result;
150 }
151
152 int
153 GetVariableNum(VariableSpace space,
154                            const char *name,
155                            int defaultval,
156                            int faultval,
157                            bool allowtrail)
158 {
159         const char *val;
160
161         val = GetVariable(space, name);
162         return ParseVariableNum(val, defaultval, faultval, allowtrail);
163 }
164
165 void
166 PrintVariables(VariableSpace space)
167 {
168         struct _variable *ptr;
169
170         if (!space)
171                 return;
172
173         for (ptr = space->next; ptr; ptr = ptr->next)
174         {
175                 if (ptr->value)
176                         printf("%s = '%s'\n", ptr->name, ptr->value);
177                 if (cancel_pressed)
178                         break;
179         }
180 }
181
182 bool
183 SetVariable(VariableSpace space, const char *name, const char *value)
184 {
185         struct _variable *current,
186                            *previous;
187
188         if (!space)
189                 return false;
190
191         if (!valid_variable_name(name))
192                 return false;
193
194         if (!value)
195                 return DeleteVariable(space, name);
196
197         for (previous = space, current = space->next;
198                  current;
199                  previous = current, current = current->next)
200         {
201                 if (strcmp(current->name, name) == 0)
202                 {
203                         /* found entry, so update */
204                         if (current->value)
205                                 free(current->value);
206                         current->value = pg_strdup(value);
207                         if (current->assign_hook)
208                                 (*current->assign_hook) (current->value);
209                         return true;
210                 }
211         }
212
213         /* not present, make new entry */
214         current = pg_malloc(sizeof *current);
215         current->name = pg_strdup(name);
216         current->value = pg_strdup(value);
217         current->assign_hook = NULL;
218         current->next = NULL;
219         previous->next = current;
220         return true;
221 }
222
223 /*
224  * This both sets a hook function, and calls it on the current value (if any)
225  */
226 bool
227 SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook)
228 {
229         struct _variable *current,
230                            *previous;
231
232         if (!space)
233                 return false;
234
235         if (!valid_variable_name(name))
236                 return false;
237
238         for (previous = space, current = space->next;
239                  current;
240                  previous = current, current = current->next)
241         {
242                 if (strcmp(current->name, name) == 0)
243                 {
244                         /* found entry, so update */
245                         current->assign_hook = hook;
246                         (*hook) (current->value);
247                         return true;
248                 }
249         }
250
251         /* not present, make new entry */
252         current = pg_malloc(sizeof *current);
253         current->name = pg_strdup(name);
254         current->value = NULL;
255         current->assign_hook = hook;
256         current->next = NULL;
257         previous->next = current;
258         (*hook) (NULL);
259         return true;
260 }
261
262 bool
263 SetVariableBool(VariableSpace space, const char *name)
264 {
265         return SetVariable(space, name, "on");
266 }
267
268 bool
269 DeleteVariable(VariableSpace space, const char *name)
270 {
271         struct _variable *current,
272                            *previous;
273
274         if (!space)
275                 return false;
276
277         for (previous = space, current = space->next;
278                  current;
279                  previous = current, current = current->next)
280         {
281                 if (strcmp(current->name, name) == 0)
282                 {
283                         if (current->value)
284                                 free(current->value);
285                         current->value = NULL;
286                         /* Physically delete only if no hook function to remember */
287                         if (current->assign_hook)
288                                 (*current->assign_hook) (NULL);
289                         else
290                         {
291                                 previous->next = current->next;
292                                 free(current->name);
293                                 free(current);
294                         }
295                         return true;
296                 }
297         }
298
299         return true;
300 }