*** ./doc/src/sgml/ref/psql-ref.sgml.orig	2010-03-21 01:43:40.000000000 +0100
--- ./doc/src/sgml/ref/psql-ref.sgml	2010-03-29 18:19:24.496359110 +0200
***************
*** 1328,1334 ****
  
  
        <varlistentry>
!         <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional></literal></term>
  
          <listitem>
          <para>
--- 1328,1334 ----
  
  
        <varlistentry>
!         <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional> <optional> linenumber </optional></literal></term>
  
          <listitem>
          <para>
***************
*** 1358,1369 ****
          systems, <filename>notepad.exe</filename> on Windows systems.
          </para>
          </tip>
          </listitem>
        </varlistentry>
  
  
        <varlistentry>
!         <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional></literal></term>
  
          <listitem>
          <para>
--- 1358,1375 ----
          systems, <filename>notepad.exe</filename> on Windows systems.
          </para>
          </tip>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor.
+         </para>
          </listitem>
        </varlistentry>
  
  
        <varlistentry>
!         <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional> <optional> linenumber </optional> </literal></term>
  
          <listitem>
          <para>
***************
*** 1386,1391 ****
--- 1392,1404 ----
           If no function is specified, a blank <command>CREATE FUNCTION</>
           template is presented for editing.
          </para>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor. It count lines from start of function body, not from
+         start of text.
+         </para>
          </listitem>
        </varlistentry>
  
***************
*** 2105,2110 ****
--- 2118,2135 ----
  
  
        <varlistentry>
+         <term><literal>\sf[+] <replaceable class="parameter">function_description</replaceable> <optional> linenumber </optional> </literal></term>
+ 
+         <listitem>
+         <para>
+          This command fetches and shows the definition of the named function,
+          in the form of a <command>CREATE OR REPLACE FUNCTION</> command.
+          If the form <literal>\sf+</literal> is used, then lines are numbered.
+         </para>
+         </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>\t</literal></term>
          <listitem>
          <para>
***************
*** 2112,2117 ****
--- 2137,2148 ----
          footer. This command is equivalent to <literal>\pset
          tuples_only</literal> and is provided for convenience.
          </para>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor.
+         </para>
          </listitem>
        </varlistentry>
  
*** ./src/bin/psql/command.c.orig	2010-03-29 13:46:55.227484734 +0200
--- ./src/bin/psql/command.c	2010-07-21 14:25:32.453803925 +0200
***************
*** 57,63 ****
  			 PsqlScanState scan_state,
  			 PQExpBuffer query_buf);
  static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
! 		bool *edited);
  static bool do_connect(char *dbname, char *user, char *host, char *port);
  static bool do_shell(const char *command);
  static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
--- 57,63 ----
  			 PsqlScanState scan_state,
  			 PQExpBuffer query_buf);
  static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
! 		bool *edited, int lineno);
  static bool do_connect(char *dbname, char *user, char *host, char *port);
  static bool do_shell(const char *command);
  static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
***************
*** 65,70 ****
--- 65,72 ----
  static void minimal_error_message(PGresult *res);
  
  static void printSSLInfo(void);
+ static bool first_row_is_empty(char *src);
+ static int get_lineno_for_navigation(char *func, backslashResult *status);
  
  #ifdef WIN32
  static void checkWin32Codepage(void);
***************
*** 488,503 ****
  		else
  		{
  			char	   *fname;
! 
  			fname = psql_scan_slash_option(scan_state,
! 										   OT_NORMAL, NULL, true);
! 			expand_tilde(&fname);
  			if (fname)
! 				canonicalize_path(fname);
! 			if (do_edit(fname, query_buf, NULL))
! 				status = PSQL_CMD_NEWEDIT;
! 			else
! 				status = PSQL_CMD_ERROR;
  			free(fname);
  		}
  	}
--- 490,527 ----
  		else
  		{
  			char	   *fname;
! 			char		*ln;
! 			int  lineno;
! 			
  			fname = psql_scan_slash_option(scan_state,
! 								   OT_NORMAL, NULL, true);
! 			
! 			/* try to get lineno */
  			if (fname)
! 			{
! 				ln = psql_scan_slash_option(scan_state,
! 									   OT_NORMAL, NULL, true);
! 				if (ln)
! 				{
! 					if (atoi(ln) < 1)
! 					{
! 						psql_error("line number is unacceptable\n");
! 						status = PSQL_CMD_ERROR;
! 					}
! 					else
! 						lineno = atoi(ln);
! 				}
! 			}
! 			if (status != PSQL_CMD_ERROR)
! 			{
! 				expand_tilde(&fname);
! 				if (fname)
! 					canonicalize_path(fname);
! 				if (do_edit(fname, query_buf, NULL, lineno))
! 					status = PSQL_CMD_NEWEDIT;
! 				else
! 					status = PSQL_CMD_ERROR;
! 			}
  			free(fname);
  		}
  	}
***************
*** 508,513 ****
--- 532,539 ----
  	 */
  	else if (strcmp(cmd, "ef") == 0)
  	{
+ 		int	lineno;
+ 	
  		if (!query_buf)
  		{
  			psql_error("no query buffer\n");
***************
*** 520,555 ****
  
  			func = psql_scan_slash_option(scan_state,
  										  OT_WHOLE_LINE, NULL, true);
! 			if (!func)
  			{
! 				/* set up an empty command to fill in */
! 				printfPQExpBuffer(query_buf,
! 								  "CREATE FUNCTION ( )\n"
! 								  " RETURNS \n"
! 								  " LANGUAGE \n"
! 								  " -- common options:  IMMUTABLE  STABLE  STRICT  SECURITY DEFINER\n"
! 								  "AS $function$\n"
! 								  "\n$function$\n");
! 			}
! 			else if (!lookup_function_oid(pset.db, func, &foid))
! 			{
! 				/* error already reported */
! 				status = PSQL_CMD_ERROR;
! 			}
! 			else if (!get_create_function_cmd(pset.db, foid, query_buf))
! 			{
! 				/* error already reported */
! 				status = PSQL_CMD_ERROR;
  			}
- 			if (func)
- 				free(func);
  		}
  
  		if (status != PSQL_CMD_ERROR)
  		{
  			bool		edited = false;
  
! 			if (!do_edit(0, query_buf, &edited))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
--- 546,590 ----
  
  			func = psql_scan_slash_option(scan_state,
  										  OT_WHOLE_LINE, NULL, true);
! 			lineno = get_lineno_for_navigation(func, &status);
! 			
! 			if (status != PSQL_CMD_ERROR)
  			{
! 				if (!func)
! 				{
! 					/* set up an empty command to fill in */
! 					printfPQExpBuffer(query_buf,
! 									  "CREATE FUNCTION ( )\n"
! 									  " RETURNS \n"
! 									  " LANGUAGE \n"
! 									  " -- common options:  IMMUTABLE  STABLE  STRICT  SECURITY DEFINER\n"
! 									  "AS $function$\n"
! 									  "\n$function$\n");
! 				}
! 				else if (!lookup_function_oid(pset.db, func, &foid))
! 				{
! 					/* error already reported */
! 					status = PSQL_CMD_ERROR;
! 				}
! 				else if (!get_create_function_cmd(pset.db, foid, query_buf))
! 				{
! 					/* error already reported */
! 					status = PSQL_CMD_ERROR;
! 				}
! 				if (func)
! 					free(func);
  			}
  		}
  
  		if (status != PSQL_CMD_ERROR)
  		{
  			bool		edited = false;
  
! 			/* correct lineno when wirst row of function's body is empty */
! 			if (!first_row_is_empty(query_buf->data))
! 				lineno--;
! 
! 			if (!do_edit(0, query_buf, &edited, lineno))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
***************
*** 933,938 ****
--- 968,1061 ----
  		free(fname);
  	}
  
+ 	/*
+ 	 * \sf -- show the named function
+ 	 */
+ 	else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0)
+ 	{
+ 		int	skip_lines = -1;
+ 		bool	with_lno;
+ 		
+ 		with_lno = (strcmp(cmd, "sf+") == 0);
+ 		
+ 		if (!query_buf)
+ 		{
+ 			psql_error("no query buffer\n");
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else
+ 		{
+ 			char	   *func;
+ 			Oid			foid = InvalidOid;
+ 
+ 			func = psql_scan_slash_option(scan_state,
+ 										  OT_WHOLE_LINE, NULL, true);
+ 			skip_lines = get_lineno_for_navigation(func, &status) - 1;
+ 			
+ 			if (status != PSQL_CMD_ERROR)
+ 			{
+ 				if (!func)
+ 				{
+ 					/* show error for empty command */
+ 					psql_error("missing a function name\n");
+ 					status = PSQL_CMD_ERROR;
+ 				}
+ 				else if (!lookup_function_oid(pset.db, func, &foid))
+ 				{
+ 					/* error already reported */
+ 					status = PSQL_CMD_ERROR;
+ 				}
+ 				else if (!get_create_function_cmd(pset.db, foid, query_buf))
+ 				{
+ 					/* error already reported */
+ 					status = PSQL_CMD_ERROR;
+ 				}
+ 			}
+ 			if (func)
+ 				free(func);
+ 		}
+ 
+ 		if (status != PSQL_CMD_ERROR)
+ 		{
+ 			int	lineno = 0;
+ 			char	*c = query_buf->data;
+ 			char	*ptr;
+ 			
+ 			/* 
+ 			 * PL doesn't calculate first row of function's body
+ 			 * when first row is empty. So checks first row, and
+ 			 * correct lineno when it is necessary.
+ 			 */
+ 			if (first_row_is_empty(query_buf->data))
+ 				lineno--;
+ 
+ 			while (*c)
+ 			{
+ 				/* find next end of line */
+ 				for (ptr = c; *c != '\n'; c++);
+ 				*c++ = '\0';
+ 				lineno++;
+ 				
+ 				/* skip first n lines */
+ 				if (skip_lines > 0 && (skip_lines > lineno))
+ 					continue;
+ 				
+ 				if (!with_lno)
+ 					printf("%s\n", ptr);
+ 				else
+ 				{
+ 					/* don't show lineno for first three rows and last row */
+ 					if ((*c == '\0' && lineno != 3) || lineno < 4)
+ 						printf("****  %s\n", ptr);
+ 					else
+ 						printf("%4d  %s\n", lineno - 3, ptr);
+ 				}
+ 			}
+ 			printf("\n");
+ 			fflush(stdout);
+ 		}
+ 	}
+ 
  	/* \set -- generalized set variable/option command */
  	else if (strcmp(cmd, "set") == 0)
  	{
***************
*** 1506,1514 ****
   */
  
  static bool
! editFile(const char *fname)
  {
  	const char *editorName;
  	char	   *sys;
  	int			result;
  
--- 1629,1638 ----
   */
  
  static bool
! editFile(const char *fname, int lineno)
  {
  	const char *editorName;
+ 	const char *navigation_cmd;
  	char	   *sys;
  	int			result;
  
***************
*** 1522,1527 ****
--- 1646,1655 ----
  		editorName = getenv("VISUAL");
  	if (!editorName)
  		editorName = DEFAULT_EDITOR;
+ 	
+ 	navigation_cmd = getenv("PSQL_NAVIGATION_COMMAND");
+ 	if (!navigation_cmd)
+ 		navigation_cmd = DEFAULT_NAVIGATION_COMMAND;
  
  	/*
  	 * On Unix the EDITOR value should *not* be quoted, since it might include
***************
*** 1530,1540 ****
  	 * severe brain damage in their command shell plus the fact that standard
  	 * program paths include spaces.
  	 */
! 	sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
  #ifndef WIN32
! 	sprintf(sys, "exec %s '%s'", editorName, fname);
  #else
! 	sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
  #endif
  	result = system(sys);
  	if (result == -1)
--- 1658,1678 ----
  	 * severe brain damage in their command shell plus the fact that standard
  	 * program paths include spaces.
  	 */
! 	sys = pg_malloc(strlen(editorName) + strlen(fname) + 20 + 1);
  #ifndef WIN32
! 	if (lineno > 0)
! 		sprintf(sys, "exec %s %s%d '%s'", editorName, navigation_cmd, lineno, fname);
! 	else
! 		sprintf(sys, "exec %s '%s'", editorName, fname);
  #else
! 	if (lineno > 0)
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" %s%d \"%s\"" SYSTEMQUOTE, 
! 									    editorName, 
! 									    navigation_cmd,
! 									    lineno, 
! 									    fname);
! 	else
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
  #endif
  	result = system(sys);
  	if (result == -1)
***************
*** 1549,1555 ****
  
  /* call this one */
  static bool
! do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
  {
  	char		fnametmp[MAXPGPATH];
  	FILE	   *stream = NULL;
--- 1687,1693 ----
  
  /* call this one */
  static bool
! do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited, int lineno)
  {
  	char		fnametmp[MAXPGPATH];
  	FILE	   *stream = NULL;
***************
*** 1641,1647 ****
  
  	/* call editor */
  	if (!error)
! 		error = !editFile(fname);
  
  	if (!error && stat(fname, &after) != 0)
  	{
--- 1779,1785 ----
  
  	/* call editor */
  	if (!error)
! 		error = !editFile(fname, lineno);
  
  	if (!error && stat(fname, &after) != 0)
  	{
***************
*** 2137,2142 ****
--- 2275,2303 ----
  	return result;
  }
  
+ 
+ /*
+  * When first row of prosrc has only new-line char, then is ignored.
+  * This functions returns true, when first row is empty, else returns
+  * false.
+  */
+ static bool
+ first_row_is_empty(char *src)
+ {
+ 	char *ptr;
+ 	
+ #define DEFAULT_BODY_SEPARATOR		"$function$"
+ 	
+ 	ptr = strstr(src, DEFAULT_BODY_SEPARATOR);
+ 	
+ 	psql_assert(ptr != NULL);
+ 	
+ 	ptr += strlen(DEFAULT_BODY_SEPARATOR);
+ 	
+ 	return *ptr == '\n';
+ }
+ 
+ 
  /*
   * Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the
   * function with the given OID.  If successful, the result is stored in buf.
***************
*** 2169,2174 ****
--- 2330,2336 ----
  	return result;
  }
  
+ 
  /*
   * Report just the primary error; this is to avoid cluttering the output
   * with, for instance, a redisplay of the internally generated query
***************
*** 2197,2199 ****
--- 2359,2435 ----
  
  	destroyPQExpBuffer(msg);
  }
+ 
+ 
+ /*
+  * Returns lineno used in \sf and \ef commands. 
+  *
+  * These commands can be completed with number used as line
+  * number for navigation in showed lines / open file. The most 
+  * simple method for parsing is reading isolated digits from 
+  * right - \ef foo nn, \ef foo(..)nn. Returns -1 when
+  * lineno isn't defined.
+ */
+ static int
+ get_lineno_for_navigation(char *func, backslashResult *status)
+ {
+ 	char *endfunc;
+ 	char *c;
+ 	int lineno = -1;
+ 	
+ 	if (!func)
+ 		return lineno;
+ 	
+ 	endfunc = func + strlen(func) - 1;
+ 	c = endfunc;
+ 	
+ 	/* skip useles whitespaces */
+ 	while (c >= func)
+ 		if (isblank(*c))
+ 			c--;
+ 		else
+ 			break;
+ 	
+ 	/* search the most left digit of continuously number */
+ 	while (c >= func)
+ 		if (!isdigit(*c))
+ 			break;
+ 		else
+ 			c--;
+ 	
+ 	/* 
+ 	 * when left char isn't blank and isn't a right parenthesis
+ 	 * then command hasn't a lineno.
+ 	 */
+ 	if (c < endfunc && c > func)
+ 	{
+ 		if (isblank(*c) || *c == ')')
+ 		{
+ 			c++;
+ 			
+ 			if (atoi(c) < 1)
+ 			{
+ 				psql_error("line number is unacceptable\n");
+ 				*status = PSQL_CMD_ERROR;
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * Function get_create_function_cmd appends a few lines
+ 				 * to function's body. But we would to like use a line 
+ 				 * numbers use a PL parsers - so add three lines to lineno:
+ 				 *   CREATE OR REPLACE FUNCTION ..
+ 				 *     RETURNS ...
+ 				 *     LANGUAGE ...
+ 				 *     AS $finction$
+ 				 */
+ 				lineno = atoi(c) + 4;
+ 				
+ 				/* remove lineno from function descriptor */
+ 				*c = '\0';
+ 			}
+ 		}
+ 	}
+ 	
+ 	return lineno;
+ }
*** ./src/bin/psql/help.c.orig	2010-03-07 18:02:34.000000000 +0100
--- ./src/bin/psql/help.c	2010-03-29 16:31:31.563360499 +0200
***************
*** 174,186 ****
  	fprintf(output, "\n");
  
  	fprintf(output, _("Query Buffer\n"));
! 	fprintf(output, _("  \\e [FILE]              edit the query buffer (or file) with external editor\n"));
! 	fprintf(output, _("  \\ef [FUNCNAME]         edit function definition with external editor\n"));
  	fprintf(output, _("  \\p                     show the contents of the query buffer\n"));
  	fprintf(output, _("  \\r                     reset (clear) the query buffer\n"));
  #ifdef USE_READLINE
  	fprintf(output, _("  \\s [FILE]              display history or save it to file\n"));
  #endif
  	fprintf(output, _("  \\w FILE                write query buffer to file\n"));
  	fprintf(output, "\n");
  
--- 174,187 ----
  	fprintf(output, "\n");
  
  	fprintf(output, _("Query Buffer\n"));
! 	fprintf(output, _("  \\e [FILE] [lno]        edit the query buffer (or file) with external editor\n"));
! 	fprintf(output, _("  \\ef [FUNCNAME] [lno]   edit function definition with external editor\n"));
  	fprintf(output, _("  \\p                     show the contents of the query buffer\n"));
  	fprintf(output, _("  \\r                     reset (clear) the query buffer\n"));
  #ifdef USE_READLINE
  	fprintf(output, _("  \\s [FILE]              display history or save it to file\n"));
  #endif
+ 	fprintf(output, _("  \\sf[+] FUNCNAME [lno]  show finction definition\n"));
  	fprintf(output, _("  \\w FILE                write query buffer to file\n"));
  	fprintf(output, "\n");
  
*** ./src/bin/psql/settings.h.orig	2010-01-02 17:57:59.000000000 +0100
--- ./src/bin/psql/settings.h	2010-07-20 16:10:36.288804290 +0200
***************
*** 18,25 ****
--- 18,27 ----
  
  #if defined(WIN32) || defined(__CYGWIN__)
  #define DEFAULT_EDITOR	"notepad.exe"
+ #define DEFAULT_NAVIGATION_COMMAND	" /"
  #else
  #define DEFAULT_EDITOR	"vi"
+ #define DEFAULT_NAVIGATION_COMMAND 	" +"
  #endif
  
  #define DEFAULT_PROMPT1 "%/%R%# "
*** ./src/bin/psql/tab-complete.c.orig	2010-02-26 03:01:20.000000000 +0100
--- ./src/bin/psql/tab-complete.c	2010-03-29 16:27:06.543359758 +0200
***************
*** 639,645 ****
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
! 		"\\set", "\\t", "\\T",
  		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
  	};
  
--- 639,645 ----
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
! 		"\\set", "\\sf", "\\t", "\\T",
  		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
  	};
  
***************
*** 2446,2451 ****
--- 2446,2454 ----
  
  	else if (strcmp(prev_wd, "\\ef") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ 	
+ 		else if (strncmp(prev_wd, "\\sf", 2) == 0)
+ 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
  
  	else if (strcmp(prev_wd, "\\encoding") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
