📄 command.c
字号:
/* \set -- generalized set variable/option command */ else if (strcmp(cmd, "set") == 0) { char *opt0 = scan_option(&string, OT_NORMAL, NULL, false); if (!opt0) { /* list all variables */ PrintVariables(pset.vars); success = true; } else { /* * Set variable to the concatenation of the arguments. */ char *newval = NULL; char *opt; opt = scan_option(&string, OT_NORMAL, NULL, false); newval = xstrdup(opt ? opt : ""); free(opt); while ((opt = scan_option(&string, OT_NORMAL, NULL, false))) { newval = realloc(newval, strlen(newval) + strlen(opt) + 1); if (!newval) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } strcat(newval, opt); free(opt); } if (SetVariable(pset.vars, opt0, newval)) { /* Check for special variables */ if (strcmp(opt0, "VERBOSITY") == 0) SyncVerbosityVariable(); } else { psql_error("\\%s: error\n", cmd); success = false; } free(newval); } free(opt0); } /* \t -- turn off headers and row count */ else if (strcmp(cmd, "t") == 0) success = do_pset("tuples_only", NULL, &pset.popt, quiet); /* \T -- define html <table ...> attributes */ else if (strcmp(cmd, "T") == 0) { char *value = scan_option(&string, OT_NORMAL, NULL, false); success = do_pset("tableattr", value, &pset.popt, quiet); free(value); } /* \timing -- toggle timing of queries */ else if (strcmp(cmd, "timing") == 0) { pset.timing = !pset.timing; if (!quiet) { if (pset.timing) puts(gettext("Timing is on.")); else puts(gettext("Timing is off.")); } } /* \unset */ else if (strcmp(cmd, "unset") == 0) { char *opt = scan_option(&string, OT_NORMAL, NULL, false); if (!opt) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else if (!SetVariable(pset.vars, opt, NULL)) { psql_error("\\%s: error\n", cmd); success = false; } free(opt); } /* \w -- write query buffer to file */ else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0) { FILE *fd = NULL; bool is_pipe = false; char *fname = NULL; if (!query_buf) { psql_error("no query buffer\n"); status = CMD_ERROR; } else { fname = scan_option(&string, OT_FILEPIPE, NULL, true); if (!fname) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { if (fname[0] == '|') { is_pipe = true; fd = popen(&fname[1], "w"); } else fd = fopen(fname, "w"); if (!fd) { psql_error("%s: %s\n", fname, strerror(errno)); success = false; } } } if (fd) { int result; if (query_buf && query_buf->len > 0) fprintf(fd, "%s\n", query_buf->data); if (is_pipe) result = pclose(fd); else result = fclose(fd); if (result == EOF) { psql_error("%s: %s\n", fname, strerror(errno)); success = false; } } free(fname); } /* \x -- toggle expanded table representation */ else if (strcmp(cmd, "x") == 0) success = do_pset("expanded", NULL, &pset.popt, quiet); /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) { char *pattern = scan_option(&string, OT_NORMAL, NULL, true); success = permissionsList(pattern); if (pattern) free(pattern); } /* \! -- shell escape */ else if (strcmp(cmd, "!") == 0) { success = do_shell(options_string); /* wind pointer to end of line */ if (string) string += strlen(string); } /* \? -- slash command help */ else if (strcmp(cmd, "?") == 0) slashUsage(pset.popt.topt.pager);#if 0 /* * These commands don't do anything. I just use them to test the * parser. */ else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0) { int i = 0; char *value; fprintf(stderr, "+ optstr = |%s|\n", options_string); while ((value = scan_option(&string, OT_NORMAL, NULL, true))) { fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value); free(value); } }#endif else status = CMD_UNKNOWN; 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 = xstrdup(""); 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 = xstrdup(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 xstrdup(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 = malloc(token_len + 1); if (!return_val) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } 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;#ifdef USE_ASSERT_CHECKING assert(source);#endif length = Min(len, strlen(source)) + 1; tmp = destination = malloc(length); if (!tmp) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } 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 * * Connects to a database (new_dbname) as a certain user (new_user). * The new user can be NULL. A db name of "-" is the same as the old one. * (That is, the one currently in pset. But pset.db can also be NULL. A NULL * dbname is handled by libpq.) * Returns true if all ok, false if the new connection couldn't be established. * The old connection will be kept if the session is interactive. */static booldo_connect(const char *new_dbname, const char *new_user){ PGconn *oldconn = pset.db;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -