Line data Source code
1 : %top{
2 : /*-------------------------------------------------------------------------
3 : *
4 : * psqlscanslash.l
5 : * lexical scanner for psql backslash commands
6 : *
7 : * XXX Avoid creating backtracking cases --- see the backend lexer for info.
8 : *
9 : * See fe_utils/psqlscan_int.h for additional commentary.
10 : *
11 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * IDENTIFICATION
15 : * src/bin/psql/psqlscanslash.l
16 : *
17 : *-------------------------------------------------------------------------
18 : */
19 : #include "postgres_fe.h"
20 :
21 : #include <ctype.h>
22 :
23 : #include "common.h"
24 : #include "psqlscanslash.h"
25 :
26 : #include "common/logging.h"
27 : #include "fe_utils/conditional.h"
28 :
29 : #include "libpq-fe.h"
30 : }
31 :
32 : %{
33 : #include "fe_utils/psqlscan_int.h"
34 :
35 : /*
36 : * We must have a typedef YYSTYPE for yylex's first argument, but this lexer
37 : * doesn't presently make use of that argument, so just declare it as int.
38 : */
39 : typedef int YYSTYPE;
40 :
41 : /*
42 : * These variables do not need to be saved across calls. Yeah, it's a bit
43 : * of a hack, but putting them into PsqlScanStateData would be klugy too.
44 : */
45 : static enum slash_option_type option_type;
46 : static char *option_quote;
47 : static int unquoted_option_chars;
48 : static int backtick_start_offset;
49 :
50 :
51 : /* Return values from yylex() */
52 : #define LEXRES_EOL 0 /* end of input */
53 : #define LEXRES_OK 1 /* OK completion of backslash argument */
54 :
55 :
56 : static void evaluate_backtick(PsqlScanState state);
57 :
58 : #define ECHO psqlscan_emit(cur_state, yytext, yyleng)
59 :
60 : /* LCOV_EXCL_START */
61 :
62 : %}
63 :
64 : /* Except for the prefix, these options should match psqlscan.l */
65 : %option reentrant
66 : %option bison-bridge
67 : %option 8bit
68 : %option never-interactive
69 : %option nodefault
70 : %option noinput
71 : %option nounput
72 : %option noyywrap
73 : %option warn
74 : %option prefix="slash_yy"
75 :
76 : /*
77 : * Set the type of yyextra; we use it as a pointer back to the containing
78 : * PsqlScanState.
79 : */
80 : %option extra-type="PsqlScanState"
81 :
82 : /*
83 : * OK, here is a short description of lex/flex rules behavior.
84 : * The longest pattern which matches an input string is always chosen.
85 : * For equal-length patterns, the first occurring in the rules list is chosen.
86 : * INITIAL is the starting state, to which all non-conditional rules apply.
87 : * Exclusive states change parsing rules while the state is active. When in
88 : * an exclusive state, only those rules defined for that state apply.
89 : */
90 :
91 : /* Exclusive states for lexing backslash commands */
92 : %x xslashcmd
93 : %x xslashargstart
94 : %x xslasharg
95 : %x xslashquote
96 : %x xslashbackquote
97 : %x xslashdquote
98 : %x xslashwholeline
99 : %x xslashend
100 :
101 : /*
102 : * Assorted character class definitions that should match psqlscan.l.
103 : */
104 : space [ \t\n\r\f\v]
105 : quote '
106 : xeoctesc [\\][0-7]{1,3}
107 : xehexesc [\\]x[0-9A-Fa-f]{1,2}
108 : xqdouble {quote}{quote}
109 : dquote \"
110 : variable_char [A-Za-z\200-\377_0-9]
111 :
112 : other .
113 :
114 : %%
115 :
116 : %{
117 : /* Declare some local variables inside yylex(), for convenience */
118 : PsqlScanState cur_state = yyextra;
119 : PQExpBuffer output_buf = cur_state->output_buf;
120 :
121 : /*
122 : * Force flex into the state indicated by start_state. This has a
123 : * couple of purposes: it lets some of the functions below set a new
124 : * starting state without ugly direct access to flex variables, and it
125 : * allows us to transition from one flex lexer to another so that we
126 : * can lex different parts of the source string using separate lexers.
127 : */
128 : BEGIN(cur_state->start_state);
129 : %}
130 :
131 : /*
132 : * We don't really expect to be invoked in the INITIAL state in this
133 : * lexer; but if we are, just spit data to the output_buf until EOF.
134 : */
135 :
136 : {other}|\n { ECHO; }
137 :
138 : /*
139 : * Exclusive lexer states to handle backslash command lexing
140 : */
141 :
142 : <xslashcmd>{
143 : /* command name ends at whitespace or backslash; eat all else */
144 :
145 : {space}|"\\" {
146 : yyless(0);
147 : cur_state->start_state = YY_START;
148 : return LEXRES_OK;
149 : }
150 :
151 : {other} { ECHO; }
152 :
153 : }
154 :
155 : <xslashargstart>{
156 : /*
157 : * Discard any whitespace before argument, then go to xslasharg state.
158 : * An exception is that "|" is only special at start of argument, so we
159 : * check for it here.
160 : */
161 :
162 : {space}+ { }
163 :
164 : "|" {
165 : if (option_type == OT_FILEPIPE)
166 : {
167 : /* treat like whole-string case */
168 : ECHO;
169 : BEGIN(xslashwholeline);
170 : }
171 : else
172 : {
173 : /* vertical bar is not special otherwise */
174 : yyless(0);
175 : BEGIN(xslasharg);
176 : }
177 : }
178 :
179 : {other} {
180 : yyless(0);
181 : BEGIN(xslasharg);
182 : }
183 :
184 : }
185 :
186 : <xslasharg>{
187 : /*
188 : * Default processing of text in a slash command's argument.
189 : *
190 : * Note: unquoted_option_chars counts the number of characters at the
191 : * end of the argument that were not subject to any form of quoting.
192 : * psql_scan_slash_option needs this to strip trailing semicolons safely.
193 : */
194 :
195 : {space}|"\\" {
196 : /*
197 : * Unquoted space is end of arg; do not eat. Likewise
198 : * backslash is end of command or next command, do not eat
199 : *
200 : * XXX this means we can't conveniently accept options
201 : * that include unquoted backslashes; therefore, option
202 : * processing that encourages use of backslashes is rather
203 : * broken.
204 : */
205 : yyless(0);
206 : cur_state->start_state = YY_START;
207 : return LEXRES_OK;
208 : }
209 :
210 : {quote} {
211 : *option_quote = '\'';
212 : unquoted_option_chars = 0;
213 : BEGIN(xslashquote);
214 : }
215 :
216 : "`" {
217 : backtick_start_offset = output_buf->len;
218 : *option_quote = '`';
219 : unquoted_option_chars = 0;
220 : BEGIN(xslashbackquote);
221 : }
222 :
223 : {dquote} {
224 : ECHO;
225 : *option_quote = '"';
226 : unquoted_option_chars = 0;
227 : BEGIN(xslashdquote);
228 : }
229 :
230 : :{variable_char}+ {
231 : /* Possible psql variable substitution */
232 : if (cur_state->callbacks->get_variable == NULL)
233 : ECHO;
234 : else
235 : {
236 : char *varname;
237 : char *value;
238 :
239 : varname = psqlscan_extract_substring(cur_state,
240 : yytext + 1,
241 : yyleng - 1);
242 : value = cur_state->callbacks->get_variable(varname,
243 : PQUOTE_PLAIN,
244 : cur_state->cb_passthrough);
245 : free(varname);
246 :
247 : /*
248 : * The variable value is just emitted without any
249 : * further examination. This is consistent with the
250 : * pre-8.0 code behavior, if not with the way that
251 : * variables are handled outside backslash commands.
252 : * Note that we needn't guard against recursion here.
253 : */
254 : if (value)
255 : {
256 : appendPQExpBufferStr(output_buf, value);
257 : free(value);
258 : }
259 : else
260 : ECHO;
261 :
262 : *option_quote = ':';
263 : }
264 : unquoted_option_chars = 0;
265 : }
266 :
267 : :'{variable_char}+' {
268 : psqlscan_escape_variable(cur_state, yytext, yyleng,
269 : PQUOTE_SQL_LITERAL);
270 : *option_quote = ':';
271 : unquoted_option_chars = 0;
272 : }
273 :
274 :
275 : :\"{variable_char}+\" {
276 : psqlscan_escape_variable(cur_state, yytext, yyleng,
277 : PQUOTE_SQL_IDENT);
278 : *option_quote = ':';
279 : unquoted_option_chars = 0;
280 : }
281 :
282 : :\{\?{variable_char}+\} {
283 : psqlscan_test_variable(cur_state, yytext, yyleng);
284 : }
285 :
286 : :'{variable_char}* {
287 : /* Throw back everything but the colon */
288 : yyless(1);
289 : unquoted_option_chars++;
290 : ECHO;
291 : }
292 :
293 : :\"{variable_char}* {
294 : /* Throw back everything but the colon */
295 : yyless(1);
296 : unquoted_option_chars++;
297 : ECHO;
298 : }
299 :
300 : :\{\?{variable_char}* {
301 : /* Throw back everything but the colon */
302 : yyless(1);
303 : unquoted_option_chars++;
304 : ECHO;
305 : }
306 :
307 : :\{ {
308 : /* Throw back everything but the colon */
309 : yyless(1);
310 : unquoted_option_chars++;
311 : ECHO;
312 : }
313 :
314 : {other} {
315 : unquoted_option_chars++;
316 : ECHO;
317 : }
318 :
319 : }
320 :
321 : <xslashquote>{
322 : /*
323 : * single-quoted text: copy literally except for '' and backslash
324 : * sequences
325 : */
326 :
327 : {quote} { BEGIN(xslasharg); }
328 :
329 : {xqdouble} { appendPQExpBufferChar(output_buf, '\''); }
330 :
331 : "\\n" { appendPQExpBufferChar(output_buf, '\n'); }
332 : "\\t" { appendPQExpBufferChar(output_buf, '\t'); }
333 : "\\b" { appendPQExpBufferChar(output_buf, '\b'); }
334 : "\\r" { appendPQExpBufferChar(output_buf, '\r'); }
335 : "\\f" { appendPQExpBufferChar(output_buf, '\f'); }
336 :
337 : {xeoctesc} {
338 : /* octal case */
339 : appendPQExpBufferChar(output_buf,
340 : (char) strtol(yytext + 1, NULL, 8));
341 : }
342 :
343 : {xehexesc} {
344 : /* hex case */
345 : appendPQExpBufferChar(output_buf,
346 : (char) strtol(yytext + 2, NULL, 16));
347 : }
348 :
349 : "\\". { psqlscan_emit(cur_state, yytext + 1, 1); }
350 :
351 : {other}|\n { ECHO; }
352 :
353 : }
354 :
355 : <xslashbackquote>{
356 : /*
357 : * backticked text: copy everything until next backquote (expanding
358 : * variable references, but doing nought else), then evaluate.
359 : */
360 :
361 : "`" {
362 : /* In an inactive \if branch, don't evaluate the command */
363 : if (cur_state->cb_passthrough == NULL ||
364 : conditional_active((ConditionalStack) cur_state->cb_passthrough))
365 : evaluate_backtick(cur_state);
366 : BEGIN(xslasharg);
367 : }
368 :
369 : :{variable_char}+ {
370 : /* Possible psql variable substitution */
371 : if (cur_state->callbacks->get_variable == NULL)
372 : ECHO;
373 : else
374 : {
375 : char *varname;
376 : char *value;
377 :
378 : varname = psqlscan_extract_substring(cur_state,
379 : yytext + 1,
380 : yyleng - 1);
381 : value = cur_state->callbacks->get_variable(varname,
382 : PQUOTE_PLAIN,
383 : cur_state->cb_passthrough);
384 : free(varname);
385 :
386 : if (value)
387 : {
388 : appendPQExpBufferStr(output_buf, value);
389 : free(value);
390 : }
391 : else
392 : ECHO;
393 : }
394 : }
395 :
396 : :'{variable_char}+' {
397 : psqlscan_escape_variable(cur_state, yytext, yyleng,
398 : PQUOTE_SHELL_ARG);
399 : }
400 :
401 : :'{variable_char}* {
402 : /* Throw back everything but the colon */
403 : yyless(1);
404 : ECHO;
405 : }
406 :
407 : {other}|\n { ECHO; }
408 :
409 : }
410 :
411 : <xslashdquote>{
412 : /* double-quoted text: copy verbatim, including the double quotes */
413 :
414 : {dquote} {
415 : ECHO;
416 : BEGIN(xslasharg);
417 : }
418 :
419 : {other}|\n { ECHO; }
420 :
421 : }
422 :
423 : <xslashwholeline>{
424 : /* copy everything until end of input line */
425 : /* but suppress leading whitespace */
426 :
427 : {space}+ {
428 : if (output_buf->len > 0)
429 : ECHO;
430 : }
431 :
432 : {other} { ECHO; }
433 :
434 : }
435 :
436 : <xslashend>{
437 : /* at end of command, eat a double backslash, but not anything else */
438 :
439 : "\\\\" {
440 : cur_state->start_state = YY_START;
441 : return LEXRES_OK;
442 : }
443 :
444 : {other}|\n {
445 : yyless(0);
446 : cur_state->start_state = YY_START;
447 : return LEXRES_OK;
448 : }
449 :
450 : }
451 :
452 : <<EOF>> {
453 : if (cur_state->buffer_stack == NULL)
454 : {
455 : cur_state->start_state = YY_START;
456 : return LEXRES_EOL; /* end of input reached */
457 : }
458 :
459 : /*
460 : * We were expanding a variable, so pop the inclusion
461 : * stack and keep lexing
462 : */
463 : psqlscan_pop_buffer_stack(cur_state);
464 : psqlscan_select_top_buffer(cur_state);
465 : }
466 :
467 : %%
468 :
469 : /* LCOV_EXCL_STOP */
470 :
471 : /*
472 : * Scan the command name of a psql backslash command. This should be called
473 : * after psql_scan() returns PSCAN_BACKSLASH. It is assumed that the input
474 : * has been consumed through the leading backslash.
475 : *
476 : * The return value is a malloc'd copy of the command name, as parsed off
477 : * from the input.
478 : */
479 : char *
480 : psql_scan_slash_command(PsqlScanState state)
481 14018 : {
482 : PQExpBufferData mybuf;
483 :
484 : /* Must be scanning already */
485 : Assert(state->scanbufhandle != NULL);
486 :
487 : /* Build a local buffer that we'll return the data of */
488 : initPQExpBuffer(&mybuf);
489 14018 :
490 : /* Set current output target */
491 : state->output_buf = &mybuf;
492 14018 :
493 : /* Set input source */
494 : if (state->buffer_stack != NULL)
495 14018 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
496 0 : else
497 : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
498 14018 :
499 : /*
500 : * Set lexer start state. Note that this is sufficient to switch
501 : * state->scanner over to using the tables in this lexer file.
502 : */
503 : state->start_state = xslashcmd;
504 14018 :
505 : /* And lex. */
506 : yylex(NULL, state->scanner);
507 14018 :
508 : /* There are no possible errors in this lex state... */
509 :
510 : /*
511 : * In case the caller returns to using the regular SQL lexer, reselect the
512 : * appropriate initial state.
513 : */
514 : psql_scan_reselect_sql_lexer(state);
515 14018 :
516 : return mybuf.data;
517 14018 : }
518 :
519 : /*
520 : * Parse off the next argument for a backslash command, and return it as a
521 : * malloc'd string. If there are no more arguments, returns NULL.
522 : *
523 : * type tells what processing, if any, to perform on the option string;
524 : * for example, if it's a SQL identifier, we want to downcase any unquoted
525 : * letters.
526 : *
527 : * if quote is not NULL, *quote is set to 0 if no quoting was found, else
528 : * the last quote symbol used in the argument.
529 : *
530 : * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
531 : * be taken as part of the option string will be stripped.
532 : *
533 : * NOTE: the only possible syntax errors for backslash options are unmatched
534 : * quotes, which are detected when we run out of input. Therefore, on a
535 : * syntax error we just throw away the string and return NULL; there is no
536 : * need to worry about flushing remaining input.
537 : */
538 : char *
539 : psql_scan_slash_option(PsqlScanState state,
540 35982 : enum slash_option_type type,
541 : char *quote,
542 : bool semicolon)
543 : {
544 : PQExpBufferData mybuf;
545 : int lexresult PG_USED_FOR_ASSERTS_ONLY;
546 : int final_state;
547 : char local_quote;
548 :
549 : /* Must be scanning already */
550 : Assert(state->scanbufhandle != NULL);
551 :
552 : if (quote == NULL)
553 35982 : quote = &local_quote;
554 32574 : *quote = 0;
555 35982 :
556 : /* Build a local buffer that we'll return the data of */
557 : initPQExpBuffer(&mybuf);
558 35982 :
559 : /* Set up static variables that will be used by yylex */
560 : option_type = type;
561 35982 : option_quote = quote;
562 35982 : unquoted_option_chars = 0;
563 35982 :
564 : /* Set current output target */
565 : state->output_buf = &mybuf;
566 35982 :
567 : /* Set input source */
568 : if (state->buffer_stack != NULL)
569 35982 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
570 0 : else
571 : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
572 35982 :
573 : /* Set lexer start state */
574 : if (type == OT_WHOLE_LINE)
575 35982 : state->start_state = xslashwholeline;
576 1484 : else
577 : state->start_state = xslashargstart;
578 34498 :
579 : /* And lex. */
580 : lexresult = yylex(NULL, state->scanner);
581 35982 :
582 : /* Save final state for a moment... */
583 : final_state = state->start_state;
584 35982 :
585 : /*
586 : * In case the caller returns to using the regular SQL lexer, reselect the
587 : * appropriate initial state.
588 : */
589 : psql_scan_reselect_sql_lexer(state);
590 35982 :
591 : /*
592 : * Check the lex result: we should have gotten back either LEXRES_OK or
593 : * LEXRES_EOL (the latter indicating end of string). If we were inside a
594 : * quoted string, as indicated by final_state, EOL is an error.
595 : */
596 : Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
597 :
598 : switch (final_state)
599 35982 : {
600 : case xslashargstart:
601 18148 : /* empty arg */
602 : break;
603 18148 : case xslasharg:
604 16336 : /* Strip any unquoted trailing semicolons if requested */
605 : if (semicolon)
606 16336 : {
607 : while (unquoted_option_chars-- > 0 &&
608 6910 : mybuf.len > 0 &&
609 6048 : mybuf.data[mybuf.len - 1] == ';')
610 6048 : {
611 : mybuf.data[--mybuf.len] = '\0';
612 6 : }
613 : }
614 :
615 : /*
616 : * If SQL identifier processing was requested, then we strip out
617 : * excess double quotes and optionally downcase unquoted letters.
618 : */
619 : if (type == OT_SQLID || type == OT_SQLIDHACK)
620 16336 : {
621 : dequote_downcase_identifier(mybuf.data,
622 266 : (type != OT_SQLIDHACK),
623 : state->encoding);
624 : /* update mybuf.len for possible shortening */
625 : mybuf.len = strlen(mybuf.data);
626 266 : }
627 : break;
628 16336 : case xslashquote:
629 0 : case xslashbackquote:
630 : case xslashdquote:
631 : /* must have hit EOL inside quotes */
632 : pg_log_error("unterminated quoted string");
633 0 : termPQExpBuffer(&mybuf);
634 0 : return NULL;
635 0 : case xslashwholeline:
636 1498 :
637 : /*
638 : * In whole-line mode, we interpret semicolon = true as stripping
639 : * trailing whitespace as well as semicolons; this gives the
640 : * nearest equivalent to what semicolon = true does in normal
641 : * mode. Note there's no concept of quoting in this mode.
642 : */
643 : if (semicolon)
644 1498 : {
645 : while (mybuf.len > 0 &&
646 220 : (mybuf.data[mybuf.len - 1] == ';' ||
647 218 : (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
648 188 : isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
649 188 : {
650 : mybuf.data[--mybuf.len] = '\0';
651 30 : }
652 : }
653 : break;
654 1498 : default:
655 0 : /* can't get here */
656 : fprintf(stderr, "invalid YY_START\n");
657 0 : exit(1);
658 0 : }
659 :
660 : /*
661 : * An unquoted empty argument isn't possible unless we are at end of
662 : * command. Return NULL instead.
663 : */
664 : if (mybuf.len == 0 && *quote == 0)
665 35982 : {
666 : termPQExpBuffer(&mybuf);
667 19780 : return NULL;
668 19780 : }
669 :
670 : /* Else return the completed string. */
671 : return mybuf.data;
672 16202 : }
673 :
674 : /*
675 : * Eat up any unused \\ to complete a backslash command.
676 : */
677 : void
678 : psql_scan_slash_command_end(PsqlScanState state)
679 14016 : {
680 : /* Must be scanning already */
681 : Assert(state->scanbufhandle != NULL);
682 :
683 : /* Set current output target */
684 : state->output_buf = NULL; /* we won't output anything */
685 14016 :
686 : /* Set input source */
687 : if (state->buffer_stack != NULL)
688 14016 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
689 0 : else
690 : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
691 14016 :
692 : /* Set lexer start state */
693 : state->start_state = xslashend;
694 14016 :
695 : /* And lex. */
696 : yylex(NULL, state->scanner);
697 14016 :
698 : /* There are no possible errors in this lex state... */
699 :
700 : /*
701 : * We expect the caller to return to using the regular SQL lexer, so
702 : * reselect the appropriate initial state.
703 : */
704 : psql_scan_reselect_sql_lexer(state);
705 14016 : }
706 14016 :
707 : /*
708 : * Fetch current paren nesting depth
709 : */
710 : int
711 : psql_scan_get_paren_depth(PsqlScanState state)
712 252 : {
713 : return state->paren_depth;
714 252 : }
715 :
716 : /*
717 : * Set paren nesting depth
718 : */
719 : void
720 : psql_scan_set_paren_depth(PsqlScanState state, int depth)
721 216 : {
722 : Assert(depth >= 0);
723 : state->paren_depth = depth;
724 216 : }
725 216 :
726 : /*
727 : * De-quote and optionally downcase a SQL identifier.
728 : *
729 : * The string at *str is modified in-place; it can become shorter,
730 : * but not longer.
731 : *
732 : * If downcase is true then non-quoted letters are folded to lower case.
733 : * Ideally this behavior will match the backend's downcase_identifier();
734 : * but note that it could differ if LC_CTYPE is different in the frontend.
735 : *
736 : * Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
737 : * this is somewhat inconsistent with the SQL spec, which would have us
738 : * parse it as several identifiers. But for psql's purposes, we want a
739 : * string like "foo"."bar" to be treated as one option, so there's little
740 : * choice; this routine doesn't get to change the token boundaries.
741 : */
742 : void
743 : dequote_downcase_identifier(char *str, bool downcase, int encoding)
744 458 : {
745 : bool inquotes = false;
746 458 : char *cp = str;
747 458 :
748 : while (*cp)
749 3572 : {
750 : if (*cp == '"')
751 3114 : {
752 : if (inquotes && cp[1] == '"')
753 136 : {
754 : /* Keep the first quote, remove the second */
755 : cp++;
756 20 : }
757 : else
758 : inquotes = !inquotes;
759 116 : /* Collapse out quote at *cp */
760 : memmove(cp, cp + 1, strlen(cp));
761 136 : /* do not advance cp */
762 : }
763 : else
764 : {
765 : if (downcase && !inquotes)
766 2978 : *cp = pg_tolower((unsigned char) *cp);
767 276 : cp += PQmblenBounded(cp, encoding);
768 2978 : }
769 : }
770 : }
771 458 :
772 : /*
773 : * Evaluate a backticked substring of a slash command's argument.
774 : *
775 : * The portion of output_buf starting at backtick_start_offset is evaluated
776 : * as a shell command and then replaced by the command's output.
777 : */
778 : static void
779 : evaluate_backtick(PsqlScanState state)
780 0 : {
781 : PQExpBuffer output_buf = state->output_buf;
782 0 : char *cmd = output_buf->data + backtick_start_offset;
783 0 : PQExpBufferData cmd_output;
784 : FILE *fd;
785 : bool error = false;
786 0 : int exit_code = 0;
787 0 : char buf[512];
788 : size_t result;
789 :
790 : initPQExpBuffer(&cmd_output);
791 0 :
792 : fflush(NULL);
793 0 : fd = popen(cmd, "r");
794 0 : if (!fd)
795 0 : {
796 : pg_log_error("%s: %m", cmd);
797 0 : error = true;
798 0 : exit_code = -1;
799 0 : }
800 :
801 : if (!error)
802 0 : {
803 : do
804 : {
805 : result = fread(buf, 1, sizeof(buf), fd);
806 0 : if (ferror(fd))
807 0 : {
808 : pg_log_error("%s: %m", cmd);
809 0 : error = true;
810 0 : break;
811 0 : }
812 : appendBinaryPQExpBuffer(&cmd_output, buf, result);
813 0 : } while (!feof(fd));
814 0 : }
815 :
816 : if (fd)
817 0 : {
818 : /*
819 : * Although pclose's result always sets the shell result variables, we
820 : * historically have abandoned the backtick substitution only if it
821 : * returns -1.
822 : */
823 : exit_code = pclose(fd);
824 0 : if (exit_code == -1)
825 0 : {
826 : pg_log_error("%s: %m", cmd);
827 0 : error = true;
828 0 : }
829 : }
830 :
831 : if (PQExpBufferDataBroken(cmd_output))
832 0 : {
833 : pg_log_error("%s: out of memory", cmd);
834 0 : error = true;
835 0 : }
836 :
837 : /* Now done with cmd, delete it from output_buf */
838 : output_buf->len = backtick_start_offset;
839 0 : output_buf->data[output_buf->len] = '\0';
840 0 :
841 : /* If no error, transfer result to output_buf */
842 : if (!error)
843 0 : {
844 : /* strip any trailing newline (but only one) */
845 : if (cmd_output.len > 0 &&
846 0 : cmd_output.data[cmd_output.len - 1] == '\n')
847 0 : cmd_output.len--;
848 0 : appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
849 0 : }
850 :
851 : /* And finally, set the shell result variables */
852 : SetShellResultVariables(exit_code);
853 0 :
854 : termPQExpBuffer(&cmd_output);
855 0 : }
|