diff options
Diffstat (limited to 'src/bin/psql/command.c')
-rw-r--r-- | src/bin/psql/command.c | 678 |
1 files changed, 117 insertions, 561 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 2b8fcb9dcde..6b03e35c6f6 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.112 2004/01/26 22:35:32 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.113 2004/02/19 19:40:08 tgl Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -36,47 +36,33 @@ #include "large_obj.h" #include "mainloop.h" #include "print.h" +#include "psqlscan.h" #include "settings.h" #include "variables.h" #include "mb/pg_wchar.h" -/* functions for use in this file */ +/* functions for use in this file */ static backslashResult exec_command(const char *cmd, - const char *options_string, - const char **continue_parse, - PQExpBuffer query_buf, - volatile int *paren_level); - -/* different ways for scan_option to handle parameter words */ -enum option_type -{ - OT_NORMAL, /* normal case */ - OT_SQLID, /* treat as SQL identifier */ - OT_SQLIDHACK, /* SQL identifier, but don't downcase */ - OT_FILEPIPE /* it's a filename or pipe */ -}; - -static char *scan_option(char **string, enum option_type type, - char *quote, bool semicolon); -static char *unescape(const unsigned char *source, size_t len); - + PsqlScanState scan_state, + PQExpBuffer query_buf); static bool do_edit(const char *filename_arg, PQExpBuffer query_buf); static bool do_connect(const char *new_dbname, const char *new_user); static bool do_shell(const char *command); + /*---------- * HandleSlashCmds: * * Handles all the different commands that start with '\', * ordinarily called by MainLoop(). * - * 'line' is the current input line, which should not start with a '\' - * but with the actual command name - * (that is taken care of by MainLoop) + * scan_state is a lexer working state that is set to continue scanning + * just after the '\'. The lexer is advanced past the command and all + * arguments on return. * * 'query_buf' contains the query-so-far, which may be modified by - * execution of the backslash command (for example, \r clears it) + * execution of the backslash command (for example, \r clears it). * query_buf can be NULL if there is no query so far. * * Returns a status code indicating what action is desired, see command.h. @@ -84,124 +70,88 @@ static bool do_shell(const char *command); */ backslashResult -HandleSlashCmds(const char *line, - PQExpBuffer query_buf, - const char **end_of_cmd, - volatile int *paren_level) +HandleSlashCmds(PsqlScanState scan_state, + PQExpBuffer query_buf) { backslashResult status = CMD_SKIP_LINE; - char *my_line; - char *options_string = NULL; - size_t blank_loc; - const char *continue_parse = NULL; /* tell the mainloop where the - * backslash command ended */ + char *cmd; + char *arg; - psql_assert(line); - my_line = pg_strdup(line); + psql_assert(scan_state); - /* - * Find the first whitespace. line[blank_loc] will now be the - * whitespace character or the \0 at the end - * - * Also look for a backslash, so stuff like \p\g works. - */ - blank_loc = strcspn(my_line, " \t\n\r\\"); + /* Parse off the command name */ + cmd = psql_scan_slash_command(scan_state); - if (my_line[blank_loc] == '\\') - { - continue_parse = &my_line[blank_loc]; - my_line[blank_loc] = '\0'; - /* If it's a double backslash, we skip it. */ - if (my_line[blank_loc + 1] == '\\') - continue_parse += 2; - } - /* do we have an option string? */ - else if (my_line[blank_loc] != '\0') - { - options_string = &my_line[blank_loc + 1]; - my_line[blank_loc] = '\0'; - } + /* And try to execute it */ + status = exec_command(cmd, scan_state, query_buf); - status = exec_command(my_line, options_string, &continue_parse, query_buf, paren_level); - - if (status == CMD_UNKNOWN) + if (status == CMD_UNKNOWN && strlen(cmd) > 1) { /* * If the command was not recognized, try to parse it as a * one-letter command with immediately following argument (a * still-supported, but no longer encouraged, syntax). */ - char new_cmd[2]; + char new_cmd[2]; - new_cmd[0] = my_line[0]; + /* don't change cmd until we know it's okay */ + new_cmd[0] = cmd[0]; new_cmd[1] = '\0'; - /* use line for options, because my_line was clobbered above */ - status = exec_command(new_cmd, line + 1, &continue_parse, query_buf, paren_level); + psql_scan_slash_pushback(scan_state, cmd + 1); - /* - * continue_parse must be relative to my_line for calculation - * below - */ - continue_parse += my_line - line; + status = exec_command(new_cmd, scan_state, query_buf); + + if (status != CMD_UNKNOWN) + { + /* adjust cmd for possible messages below */ + cmd[1] = '\0'; #if 0 /* turned out to be too annoying */ - if (status != CMD_UNKNOWN && isalpha((unsigned char) new_cmd[0])) - psql_error("Warning: This syntax is deprecated.\n"); + if (isalpha((unsigned char) cmd[0])) + psql_error("Warning: This syntax is deprecated.\n"); #endif + } } if (status == CMD_UNKNOWN) { if (pset.cur_cmd_interactive) - fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), my_line); + fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), cmd); else - psql_error("invalid command \\%s\n", my_line); + psql_error("invalid command \\%s\n", cmd); status = CMD_ERROR; } - if (continue_parse && *continue_parse && *(continue_parse + 1) == '\\') - continue_parse += 2; - - if (end_of_cmd) + /* eat the rest of the options, if any */ + while ((arg = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false))) { - if (continue_parse) - *end_of_cmd = line + (continue_parse - my_line); - else - *end_of_cmd = line + strlen(line); + if (status != CMD_ERROR) + psql_error("\\%s: extra argument \"%s\" ignored\n", cmd, arg); + free(arg); } - free(my_line); + /* if there is a trailing \\, swallow it */ + psql_scan_slash_command_end(scan_state); + + free(cmd); return status; } - - +/* + * Subroutine to actually try to execute a backslash command. + */ static backslashResult exec_command(const char *cmd, - const char *options_string, - const char **continue_parse, - PQExpBuffer query_buf, - volatile int *paren_level) + PsqlScanState scan_state, + PQExpBuffer query_buf) { bool success = true; /* indicate here if the command ran ok or * failed */ bool quiet = QUIET(); backslashResult status = CMD_SKIP_LINE; - char *string, - *string_cpy, - *val; - - /* - * The 'string' variable will be overwritten to point to the next - * token, hence we need an extra pointer so we can free this at the - * end. - */ - if (options_string) - string = string_cpy = pg_strdup(options_string); - else - string = string_cpy = NULL; /* * \a -- toggle field alignment This makes little sense but we keep it @@ -218,7 +168,8 @@ exec_command(const char *cmd, /* \C -- override table title (formerly change HTML caption) */ else if (strcmp(cmd, "C") == 0) { - char *opt = scan_option(&string, OT_NORMAL, NULL, true); + char *opt = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); success = do_pset("title", opt, &pset.popt, quiet); free(opt); @@ -249,8 +200,10 @@ exec_command(const char *cmd, * files can be expected to double-quote all mixed-case \connect * arguments, and then we can get rid of OT_SQLIDHACK. */ - opt1 = scan_option(&string, OT_SQLIDHACK, &opt1q, true); - opt2 = scan_option(&string, OT_SQLIDHACK, &opt2q, true); + opt1 = psql_scan_slash_option(scan_state, + OT_SQLIDHACK, &opt1q, true); + opt2 = psql_scan_slash_option(scan_state, + OT_SQLIDHACK, &opt2q, true); if (opt2) /* gave username */ @@ -270,7 +223,8 @@ exec_command(const char *cmd, /* \cd */ else if (strcmp(cmd, "cd") == 0) { - char *opt = scan_option(&string, OT_NORMAL, NULL, true); + char *opt = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); char *dir; if (opt) @@ -311,9 +265,11 @@ exec_command(const char *cmd, /* \copy */ else if (strcasecmp(cmd, "copy") == 0) { - success = do_copy(options_string); - if (options_string) - string += strlen(string); + char *opt = psql_scan_slash_option(scan_state, + OT_WHOLE_LINE, NULL, false); + + success = do_copy(opt); + free(opt); } /* \copyright */ @@ -327,7 +283,8 @@ exec_command(const char *cmd, bool show_verbose; /* We don't do SQLID reduction on the pattern yet */ - pattern = scan_option(&string, OT_NORMAL, NULL, true); + pattern = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); show_verbose = strchr(cmd, '+') ? true : false; @@ -412,7 +369,8 @@ exec_command(const char *cmd, } else { - fname = scan_option(&string, OT_NORMAL, NULL, true); + fname = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); expand_tilde(&fname); status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR; free(fname); @@ -433,7 +391,8 @@ exec_command(const char *cmd, else fout = stdout; - while ((value = scan_option(&string, OT_NORMAL, "ed, false))) + while ((value = psql_scan_slash_option(scan_state, + OT_NORMAL, "ed, false))) { if (!quoted && strcmp(value, "-n") == 0) no_newline = true; @@ -454,7 +413,8 @@ exec_command(const char *cmd, /* \encoding -- set/show client side encoding */ else if (strcmp(cmd, "encoding") == 0) { - char *encoding = scan_option(&string, OT_NORMAL, NULL, false); + char *encoding = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); if (!encoding) { @@ -481,7 +441,8 @@ exec_command(const char *cmd, /* \f -- change field separator */ else if (strcmp(cmd, "f") == 0) { - char *fname = scan_option(&string, OT_NORMAL, NULL, false); + char *fname = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); success = do_pset("fieldsep", fname, &pset.popt, quiet); free(fname); @@ -490,7 +451,8 @@ exec_command(const char *cmd, /* \g means send query */ else if (strcmp(cmd, "g") == 0) { - char *fname = scan_option(&string, OT_FILEPIPE, NULL, false); + char *fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, false); if (!fname) pset.gfname = NULL; @@ -506,11 +468,11 @@ exec_command(const char *cmd, /* help */ else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { - helpSQL(options_string ? &options_string[strspn(options_string, " \t\n\r")] : NULL, - pset.popt.topt.pager); - /* set pointer to end of line */ - if (string) - string += strlen(string); + char *opt = psql_scan_slash_option(scan_state, + OT_WHOLE_LINE, NULL, false); + + helpSQL(opt, pset.popt.topt.pager); + free(opt); } /* HTML mode */ @@ -526,7 +488,8 @@ exec_command(const char *cmd, /* \i is include file */ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) { - char *fname = scan_option(&string, OT_NORMAL, NULL, true); + char *fname = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); if (!fname) { @@ -555,8 +518,10 @@ exec_command(const char *cmd, char *opt1, *opt2; - opt1 = scan_option(&string, OT_NORMAL, NULL, true); - opt2 = scan_option(&string, OT_NORMAL, NULL, true); + opt1 = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + opt2 = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); if (strcmp(cmd + 3, "export") == 0) { @@ -611,7 +576,8 @@ exec_command(const char *cmd, /* \o -- set query output */ else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0) { - char *fname = scan_option(&string, OT_FILEPIPE, NULL, true); + char *fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, true); expand_tilde(&fname); success = setQFout(fname); @@ -631,8 +597,10 @@ exec_command(const char *cmd, /* \pset -- set printing parameters */ else if (strcmp(cmd, "pset") == 0) { - char *opt0 = scan_option(&string, OT_NORMAL, NULL, false); - char *opt1 = scan_option(&string, OT_NORMAL, NULL, false); + char *opt0 = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); + char *opt1 = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); if (!opt0) { @@ -654,8 +622,7 @@ exec_command(const char *cmd, else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) { resetPQExpBuffer(query_buf); - if (paren_level) - *paren_level = 0; + psql_scan_reset(scan_state); if (!quiet) puts(gettext("Query buffer reset (cleared).")); } @@ -663,7 +630,8 @@ exec_command(const char *cmd, /* \s save history in a file or show it on the screen */ else if (strcmp(cmd, "s") == 0) { - char *fname = scan_option(&string, OT_NORMAL, NULL, true); + char *fname = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); expand_tilde(&fname); success = saveHistory(fname ? fname : "/dev/tty"); @@ -676,7 +644,8 @@ exec_command(const char *cmd, /* \set -- generalized set variable/option command */ else if (strcmp(cmd, "set") == 0) { - char *opt0 = scan_option(&string, OT_NORMAL, NULL, false); + char *opt0 = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); if (!opt0) { @@ -689,14 +658,16 @@ exec_command(const char *cmd, /* * Set variable to the concatenation of the arguments. */ - char *newval = NULL; + char *newval; char *opt; - opt = scan_option(&string, OT_NORMAL, NULL, false); + opt = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); newval = pg_strdup(opt ? opt : ""); free(opt); - while ((opt = scan_option(&string, OT_NORMAL, NULL, false))) + while ((opt = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false))) { newval = realloc(newval, strlen(newval) + strlen(opt) + 1); if (!newval) @@ -732,7 +703,8 @@ exec_command(const char *cmd, /* \T -- define html <table ...> attributes */ else if (strcmp(cmd, "T") == 0) { - char *value = scan_option(&string, OT_NORMAL, NULL, false); + char *value = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); success = do_pset("tableattr", value, &pset.popt, quiet); free(value); @@ -754,7 +726,8 @@ exec_command(const char *cmd, /* \unset */ else if (strcmp(cmd, "unset") == 0) { - char *opt = scan_option(&string, OT_NORMAL, NULL, false); + char *opt = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); if (!opt) { @@ -783,7 +756,8 @@ exec_command(const char *cmd, } else { - fname = scan_option(&string, OT_FILEPIPE, NULL, true); + fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, true); expand_tilde(&fname); if (!fname) @@ -839,7 +813,8 @@ exec_command(const char *cmd, /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) { - char *pattern = scan_option(&string, OT_NORMAL, NULL, true); + char *pattern = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); success = permissionsList(pattern); if (pattern) @@ -849,10 +824,11 @@ exec_command(const char *cmd, /* \! -- shell escape */ else if (strcmp(cmd, "!") == 0) { - success = do_shell(options_string); - /* wind pointer to end of line */ - if (string) - string += strlen(string); + char *opt = psql_scan_slash_option(scan_state, + OT_WHOLE_LINE, NULL, false); + + success = do_shell(opt); + free(opt); } /* \? -- slash command help */ @@ -870,8 +846,8 @@ exec_command(const char *cmd, int i = 0; char *value; - fprintf(stderr, "+ optstr = |%s|\n", options_string); - while ((value = scan_option(&string, OT_NORMAL, NULL, true))) + while ((value = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true))) { fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value); free(value); @@ -885,431 +861,11 @@ exec_command(const char *cmd, if (!success) status = CMD_ERROR; - /* eat the rest of the options string */ - while ((val = scan_option(&string, OT_NORMAL, NULL, false))) - { - if (status != CMD_UNKNOWN) - psql_error("\\%s: extra argument \"%s\" ignored\n", cmd, val); - if (val) - free(val); - } - - if (options_string && continue_parse) - *continue_parse = options_string + (string - string_cpy); - free(string_cpy); - return status; } -/* - * scan_option() - * - * *string points to possible option string on entry; on exit, it's updated - * to point past the option string (if any). - * - * type tells what processing, if any, to perform on the option string; - * for example, if it's a SQL identifier, we want to downcase any unquoted - * letters. - * - * if quote is not NULL, *quote is set to 0 if no quoting was found, else - * the quote symbol. - * - * if semicolon is true, trailing semicolon(s) that would otherwise be taken - * as part of the option string will be stripped. - * - * Return value is NULL if no option found, else a malloc'd copy of the - * processed option value. - */ -static char * -scan_option(char **string, enum option_type type, char *quote, bool semicolon) -{ - unsigned int pos; - char *options_string; - char *return_val; - - if (quote) - *quote = 0; - - if (!string || !(*string)) - return NULL; - - options_string = *string; - /* skip leading whitespace */ - pos = strspn(options_string, " \t\n\r"); - - switch (options_string[pos]) - { - /* - * End of line: no option present - */ - case '\0': - *string = &options_string[pos]; - return NULL; - - /* - * Next command: treat like end of line - * - * XXX this means we can't conveniently accept options that start - * with a backslash; therefore, option processing that - * encourages use of backslashes is rather broken. - */ - case '\\': - *string = &options_string[pos]; - return NULL; - - /* - * A single quote has a psql internal meaning, such as for - * delimiting file names, and it also allows for such escape - * sequences as \t. - */ - case '\'': - { - unsigned int jj; - unsigned short int bslash_count = 0; - - for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding)) - { - if (options_string[jj] == '\'' && bslash_count % 2 == 0) - break; - - if (options_string[jj] == '\\') - bslash_count++; - else - bslash_count = 0; - } - - if (options_string[jj] == 0) - { - psql_error("parse error at the end of line\n"); - *string = &options_string[jj]; - return NULL; - } - - return_val = unescape(&options_string[pos + 1], jj - pos - 1); - *string = &options_string[jj + 1]; - if (quote) - *quote = '\''; - return return_val; - } - - /* - * Backticks are for command substitution, like in shells - */ - case '`': - { - bool error = false; - FILE *fd; - char *file; - PQExpBufferData output; - char buf[512]; - size_t result, - len; - - len = strcspn(options_string + pos + 1, "`"); - if (options_string[pos + 1 + len] == 0) - { - psql_error("parse error at the end of line\n"); - *string = &options_string[pos + 1 + len]; - return NULL; - } - - options_string[pos + 1 + len] = '\0'; - file = options_string + pos + 1; - - fd = popen(file, "r"); - if (!fd) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - } - - initPQExpBuffer(&output); - - if (!error) - { - do - { - result = fread(buf, 1, 512, fd); - if (ferror(fd)) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - break; - } - appendBinaryPQExpBuffer(&output, buf, result); - } while (!feof(fd)); - appendPQExpBufferChar(&output, '\0'); - } - - if (fd && pclose(fd) == -1) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - } - - if (!error) - { - if (output.data[strlen(output.data) - 1] == '\n') - output.data[strlen(output.data) - 1] = '\0'; - return_val = output.data; - } - else - { - return_val = pg_strdup(""); - termPQExpBuffer(&output); - } - - options_string[pos + 1 + len] = '`'; - *string = options_string + pos + len + 2; - if (quote) - *quote = '`'; - return return_val; - } - - /* - * Variable substitution - */ - case ':': - { - size_t token_end; - const char *value; - char save_char; - - token_end = strcspn(&options_string[pos + 1], " \t\n\r"); - save_char = options_string[pos + token_end + 1]; - options_string[pos + token_end + 1] = '\0'; - value = GetVariable(pset.vars, options_string + pos + 1); - return_val = pg_strdup(value ? value : ""); - options_string[pos + token_end + 1] = save_char; - *string = &options_string[pos + token_end + 1]; - /* XXX should we set *quote to ':' here? */ - return return_val; - } - - /* - * | could be the beginning of a pipe if so, take rest of line - * as command - */ - case '|': - if (type == OT_FILEPIPE) - { - *string += strlen(*string); - return pg_strdup(options_string + pos); - } - /* fallthrough for other option types */ - - /* - * Default case: token extends to next whitespace, except that - * whitespace within double quotes doesn't end the token. - * - * If we are processing the option as a SQL identifier, then - * downcase unquoted letters and remove double-quotes --- but - * doubled double-quotes become output double-quotes, per - * spec. - * - * Note that a string like FOO"BAR"BAZ will be converted to - * fooBARbaz; this is somewhat inconsistent with the SQL spec, - * which would have us parse it as several identifiers. But - * for psql's purposes, we want a string like "foo"."bar" to - * be treated as one option, so there's little choice. - */ - default: - { - bool inquotes = false; - size_t token_len; - char *cp; - - /* Find end of option */ - - cp = &options_string[pos]; - for (;;) - { - /* Find next quote, whitespace, or end of string */ - cp += strcspn(cp, "\" \t\n\r"); - if (inquotes) - { - if (*cp == '\0') - { - psql_error("parse error at the end of line\n"); - *string = cp; - return NULL; - } - if (*cp == '"') - inquotes = false; - cp++; - } - else - { - if (*cp != '"') - break; /* whitespace or end of string */ - if (quote) - *quote = '"'; - inquotes = true; - cp++; - } - } - - *string = cp; - - /* Copy the option */ - token_len = cp - &options_string[pos]; - - return_val = pg_malloc(token_len + 1); - memcpy(return_val, &options_string[pos], token_len); - return_val[token_len] = '\0'; - - /* Strip any trailing semi-colons if requested */ - if (semicolon) - { - int i; - - for (i = token_len - 1; - i >= 0 && return_val[i] == ';'; - i--) - /* skip */ ; - - if (i < 0) - { - /* nothing left after stripping the semicolon... */ - free(return_val); - return NULL; - } - - if (i < (int) token_len - 1) - return_val[i + 1] = '\0'; - } - - /* - * If SQL identifier processing was requested, then we - * strip out excess double quotes and downcase unquoted - * letters. - */ - if (type == OT_SQLID || type == OT_SQLIDHACK) - { - inquotes = false; - cp = return_val; - - while (*cp) - { - if (*cp == '"') - { - if (inquotes && cp[1] == '"') - { - /* Keep the first quote, remove the second */ - cp++; - } - inquotes = !inquotes; - /* Collapse out quote at *cp */ - memmove(cp, cp + 1, strlen(cp)); - /* do not advance cp */ - } - else - { - if (!inquotes && type == OT_SQLID) - { - if (isupper((unsigned char) *cp)) - *cp = tolower((unsigned char) *cp); - } - cp += PQmblen(cp, pset.encoding); - } - } - } - - return return_val; - } - } -} - - - -/* - * unescape - * - * Replaces \n, \t, and the like. - * - * The return value is malloc'ed. - */ -static char * -unescape(const unsigned char *source, size_t len) -{ - const unsigned char *p; - bool esc = false; /* Last character we saw was the escape - * character */ - char *destination, - *tmp; - size_t length; - - psql_assert(source); - - length = Min(len, strlen(source)) + 1; - - tmp = destination = pg_malloc(length); - - for (p = source; p - source < (int) len && *p; p += PQmblen(p, pset.encoding)) - { - if (esc) - { - char c; - - switch (*p) - { - case 'n': - c = '\n'; - break; - case 't': - c = '\t'; - break; - case 'b': - c = '\b'; - break; - case 'r': - c = '\r'; - break; - case 'f': - c = '\f'; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - c = parse_char((char **) &p); - break; - - default: - c = *p; - } - *tmp++ = c; - esc = false; - } - - else if (*p == '\\') - esc = true; - - else - { - int i; - const unsigned char *mp = p; - - for (i = 0; i < PQmblen(p, pset.encoding); i++) - *tmp++ = *mp++; - esc = false; - } - } - - *tmp = '\0'; - return destination; -} - - - /* do_connect * -- handler for \connect * |