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.
This commit is contained in:
Robert Haas 2011-07-06 11:45:13 -04:00
parent 5ac6b76789
commit c7f23494c1
9 changed files with 68 additions and 12 deletions

View File

@ -1625,6 +1625,21 @@ Tue Oct 26 21:40:57 CEST 1999
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>\ir <replaceable class="parameter">filename</replaceable></literal></term>
<listitem>
<para>
The <literal>\ir</> command is similar to <literal>\i</>, but resolves
relative pathnames differently. When executing in interactive mode,
the two commands behave identically. However, when invoked from a
script, <literal>\ir</literal> interprets pathnames relative to the
directory in which the script is located, rather than the current
working directory.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>\l</literal> (or <literal>\list</literal>)</term> <term><literal>\l</literal> (or <literal>\list</literal>)</term>
<term><literal>\l+</literal> (or <literal>\list+</literal>)</term> <term><literal>\l+</literal> (or <literal>\list+</literal>)</term>

View File

@ -784,8 +784,9 @@ exec_command(const char *cmd,
} }
/* \i is include file */ /* \i and \ir include files */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) 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, char *fname = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true); OT_NORMAL, NULL, true);
@ -797,8 +798,12 @@ exec_command(const char *cmd,
} }
else else
{ {
bool include_relative;
include_relative = (strcmp(cmd, "ir") == 0
|| strcmp(cmd, "include_relative") == 0);
expand_tilde(&fname); expand_tilde(&fname);
success = (process_file(fname, false) == EXIT_SUCCESS); success = (process_file(fname, false, include_relative) == EXIT_SUCCESS);
free(fname); free(fname);
} }
} }
@ -1969,15 +1974,19 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
* process_file * process_file
* *
* Read commands from filename and then them to the main processing loop * 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. * 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 int
process_file(char *filename, bool single_txn) process_file(char *filename, bool single_txn, bool use_relative_path)
{ {
FILE *fd; FILE *fd;
int result; int result;
char *oldfilename; char *oldfilename;
char relpath[MAXPGPATH];
PGresult *res; PGresult *res;
if (!filename) if (!filename)
@ -1986,6 +1995,24 @@ process_file(char *filename, bool single_txn)
if (strcmp(filename, "-") != 0) if (strcmp(filename, "-") != 0)
{ {
canonicalize_path(filename); 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); fd = fopen(filename, PG_BINARY_R);
} }
else else

View File

@ -27,7 +27,7 @@ typedef enum _backslashResult
extern backslashResult HandleSlashCmds(PsqlScanState scan_state, extern backslashResult HandleSlashCmds(PsqlScanState scan_state,
PQExpBuffer query_buf); 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, extern bool do_pset(const char *param,
const char *value, const char *value,

View File

@ -158,7 +158,7 @@ slashUsage(unsigned short int pager)
{ {
FILE *output; FILE *output;
output = PageOutput(92, pager); output = PageOutput(93, pager);
/* if you add/remove a line here, change the row count above */ /* 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, _(" \\copy ... perform SQL COPY with data stream to the client host\n"));
fprintf(output, _(" \\echo [STRING] write string to standard output\n")); fprintf(output, _(" \\echo [STRING] write string to standard output\n"));
fprintf(output, _(" \\i FILE execute commands from file\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, _(" \\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, _(" \\qecho [STRING] write string to query output stream (see \\o)\n"));
fprintf(output, "\n"); fprintf(output, "\n");

View File

@ -81,7 +81,7 @@ typedef struct _psqlSettings
bool cur_cmd_interactive; bool cur_cmd_interactive;
int sversion; /* backend server version */ int sversion; /* backend server version */
const char *progname; /* in case you renamed psql */ 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 */ char *dirname; /* current directory for \s display */
uint64 lineno; /* also for error reporting */ uint64 lineno; /* also for error reporting */

View File

@ -256,7 +256,7 @@ main(int argc, char *argv[])
if (!options.no_psqlrc) if (!options.no_psqlrc)
process_psqlrc(argv[0]); 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); sprintf(psqlrc, "%s-%s", filename, PG_VERSION);
if (access(psqlrc, R_OK) == 0) if (access(psqlrc, R_OK) == 0)
(void) process_file(psqlrc, false); (void) process_file(psqlrc, false, false);
else if (access(filename, R_OK) == 0) else if (access(filename, R_OK) == 0)
(void) process_file(filename, false); (void) process_file(filename, false, false);
free(psqlrc); free(psqlrc);
} }

View File

@ -735,7 +735,7 @@ psql_completion(char *text, int start, int end)
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL", "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
"\\e", "\\echo", "\\ef", "\\encoding", "\\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", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
"\\set", "\\sf", "\\t", "\\T", "\\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, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
strcmp(prev_wd, "\\g") == 0 || strcmp(prev_wd, "\\g") == 0 ||
strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 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, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
strcmp(prev_wd, "\\s") == 0 || strcmp(prev_wd, "\\s") == 0 ||
strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0

View File

@ -34,6 +34,7 @@ extern bool pg_set_block(pgsocket sock);
/* Portable path handling for Unix/Win32 (in path.c) */ /* 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 *first_dir_separator(const char *filename);
extern char *last_dir_separator(const char *filename); extern char *last_dir_separator(const char *filename);
extern char *first_path_var_separator(const char *pathlist); extern char *first_path_var_separator(const char *pathlist);

View File

@ -74,6 +74,17 @@ skip_drive(const char *path)
#define skip_drive(path) (path) #define skip_drive(path) (path)
#endif #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 * first_dir_separator
* *