From c7f23494c1103f87bcf1ef7cbfcd626e73edb337 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Wed, 6 Jul 2011 11:45:13 -0400 Subject: [PATCH] Add \ir command to psql. \ir is short for "include relative"; when used from a script, the supplied pathname will be interpreted relative to the input file, rather than to the current working directory. Gurjeet Singh, reviewed by Josh Kupershmidt, with substantial further cleanup by me. --- doc/src/sgml/ref/psql-ref.sgml | 15 ++++++++++++++ src/bin/psql/command.c | 37 +++++++++++++++++++++++++++++----- src/bin/psql/command.h | 2 +- src/bin/psql/help.c | 3 ++- src/bin/psql/settings.h | 2 +- src/bin/psql/startup.c | 6 +++--- src/bin/psql/tab-complete.c | 3 ++- src/include/port.h | 1 + src/port/path.c | 11 ++++++++++ 9 files changed, 68 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 132a7b354b..6385c78d1d 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1625,6 +1625,21 @@ Tue Oct 26 21:40:57 CEST 1999 + + \ir filename + + + The \ir command is similar to \i, but resolves + relative pathnames differently. When executing in interactive mode, + the two commands behave identically. However, when invoked from a + script, \ir interprets pathnames relative to the + directory in which the script is located, rather than the current + working directory. + + + + + \l (or \list) \l+ (or \list+) diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 378330b96a..16ff9e91e5 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -784,8 +784,9 @@ exec_command(const char *cmd, } - /* \i is include file */ - else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) + /* \i and \ir include files */ + else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0 + || strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0) { char *fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); @@ -797,8 +798,12 @@ exec_command(const char *cmd, } else { + bool include_relative; + + include_relative = (strcmp(cmd, "ir") == 0 + || strcmp(cmd, "include_relative") == 0); expand_tilde(&fname); - success = (process_file(fname, false) == EXIT_SUCCESS); + success = (process_file(fname, false, include_relative) == EXIT_SUCCESS); free(fname); } } @@ -1969,15 +1974,19 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf, * process_file * * Read commands from filename and then them to the main processing loop - * Handler for \i, but can be used for other things as well. Returns + * Handler for \i and \ir, but can be used for other things as well. Returns * MainLoop() error code. + * + * If use_relative_path is true and filename is not an absolute path, then open + * the file from where the currently processed file (if any) is located. */ int -process_file(char *filename, bool single_txn) +process_file(char *filename, bool single_txn, bool use_relative_path) { FILE *fd; int result; char *oldfilename; + char relpath[MAXPGPATH]; PGresult *res; if (!filename) @@ -1986,6 +1995,24 @@ process_file(char *filename, bool single_txn) if (strcmp(filename, "-") != 0) { canonicalize_path(filename); + + /* + * If we were asked to resolve the pathname relative to the location + * of the currently executing script, and there is one, and this is + * a relative pathname, then prepend all but the last pathname + * component of the current script to this pathname. + */ + if (use_relative_path && pset.inputfile && !is_absolute_path(filename) + && !has_drive_prefix(filename)) + { + snprintf(relpath, MAXPGPATH, "%s", pset.inputfile); + get_parent_directory(relpath); + join_path_components(relpath, relpath, filename); + canonicalize_path(relpath); + + filename = relpath; + } + fd = fopen(filename, PG_BINARY_R); } else diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h index 852d645cfd..9d0c31c103 100644 --- a/src/bin/psql/command.h +++ b/src/bin/psql/command.h @@ -27,7 +27,7 @@ typedef enum _backslashResult extern backslashResult HandleSlashCmds(PsqlScanState scan_state, PQExpBuffer query_buf); -extern int process_file(char *filename, bool single_txn); +extern int process_file(char *filename, bool single_txn, bool use_relative_path); extern bool do_pset(const char *param, const char *value, diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index ac5edca65d..e56ab61ac6 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -158,7 +158,7 @@ slashUsage(unsigned short int pager) { FILE *output; - output = PageOutput(92, pager); + output = PageOutput(93, pager); /* if you add/remove a line here, change the row count above */ @@ -184,6 +184,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n")); fprintf(output, _(" \\echo [STRING] write string to standard output\n")); fprintf(output, _(" \\i FILE execute commands from file\n")); + fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n")); fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n")); fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n")); fprintf(output, "\n"); diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index 7228f9d0ee..3aebf53299 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -81,7 +81,7 @@ typedef struct _psqlSettings bool cur_cmd_interactive; int sversion; /* backend server version */ const char *progname; /* in case you renamed psql */ - char *inputfile; /* for error reporting */ + char *inputfile; /* file being currently processed, if any */ char *dirname; /* current directory for \s display */ uint64 lineno; /* also for error reporting */ diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 7b8078c21e..3c17eece7b 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -256,7 +256,7 @@ main(int argc, char *argv[]) if (!options.no_psqlrc) process_psqlrc(argv[0]); - successResult = process_file(options.action_string, options.single_txn); + successResult = process_file(options.action_string, options.single_txn, false); } /* @@ -604,9 +604,9 @@ process_psqlrc_file(char *filename) sprintf(psqlrc, "%s-%s", filename, PG_VERSION); if (access(psqlrc, R_OK) == 0) - (void) process_file(psqlrc, false); + (void) process_file(psqlrc, false, false); else if (access(filename, R_OK) == 0) - (void) process_file(filename, false); + (void) process_file(filename, false, false); free(psqlrc); } diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 32f418306c..4f7df367e5 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -735,7 +735,7 @@ psql_completion(char *text, int start, int end) "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL", "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\e", "\\echo", "\\ef", "\\encoding", - "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l", + "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\sf", "\\t", "\\T", @@ -2874,6 +2874,7 @@ psql_completion(char *text, int start, int end) strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 || strcmp(prev_wd, "\\g") == 0 || strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 || + strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 || strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 || strcmp(prev_wd, "\\s") == 0 || strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 diff --git a/src/include/port.h b/src/include/port.h index 4c7ed64317..2cab65fbde 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -34,6 +34,7 @@ extern bool pg_set_block(pgsocket sock); /* Portable path handling for Unix/Win32 (in path.c) */ +extern bool has_drive_prefix(const char *filename); extern char *first_dir_separator(const char *filename); extern char *last_dir_separator(const char *filename); extern char *first_path_var_separator(const char *pathlist); diff --git a/src/port/path.c b/src/port/path.c index 6991bc7247..13ca4f3f1c 100644 --- a/src/port/path.c +++ b/src/port/path.c @@ -74,6 +74,17 @@ skip_drive(const char *path) #define skip_drive(path) (path) #endif +/* + * has_drive_prefix + * + * Return true if the given pathname has a drive prefix. + */ +bool +has_drive_prefix(const char *path) +{ + return skip_drive(path) != path; +} + /* * first_dir_separator *