*** ./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-03-29 16:17:22.220361245 +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);
***************
*** 488,500 ****
  		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;
--- 488,517 ----
  		else
  		{
  			char	   *fname;
! 			char		*lineno;
! 			int  ln,
! 				*lnptr = NULL;
! 			
! 			
  			fname = psql_scan_slash_option(scan_state,
! 								   OT_NORMAL, NULL, true);
! 			
! 			/* try to get lineno */
! 			if (fname)
! 			{
! 				lineno = psql_scan_slash_option(scan_state,
! 									   OT_NORMAL, NULL, true);
! 				if (lineno)
! 				{
! 					ln = atoi(lineno);
! 					lnptr = &ln;
! 				}
! 			}
! 			
  			expand_tilde(&fname);
  			if (fname)
  				canonicalize_path(fname);
! 			if (do_edit(fname, query_buf, NULL, lnptr))
  				status = PSQL_CMD_NEWEDIT;
  			else
  				status = PSQL_CMD_ERROR;
***************
*** 508,513 ****
--- 525,533 ----
  	 */
  	else if (strcmp(cmd, "ef") == 0)
  	{
+ 		int	lineno;
+ 		int	*lnptr = NULL;
+ 	
  		if (!query_buf)
  		{
  			psql_error("no query buffer\n");
***************
*** 520,525 ****
--- 540,574 ----
  
  			func = psql_scan_slash_option(scan_state,
  										  OT_WHOLE_LINE, NULL, true);
+ 			
+ 			/* 
+ 			 * parse linenumber from the right
+ 			 */
+ 			if (func && *func)
+ 			{
+ 				char *endfunc = func + strlen(func) - 1;
+ 				char *c;
+ 				
+ 				c = endfunc;
+ 				while (c >= func)
+ 					if (!isdigit(*c))
+ 						break;
+ 					else
+ 						c--;
+ 				
+ 				if (c < endfunc && c > func)
+ 				{
+ 					if (*c == ' ' || *c == ')')
+ 					{
+ 						c++;
+ 						/* append rows for SQL decoration */
+ 						lineno = atoi(c) + 3;
+ 						*c = '\0';
+ 						lnptr = &lineno;
+ 					}
+ 				}
+ 			}
+ 			
  			if (!func)
  			{
  				/* set up an empty command to fill in */
***************
*** 549,555 ****
  		{
  			bool		edited = false;
  
! 			if (!do_edit(0, query_buf, &edited))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
--- 598,604 ----
  		{
  			bool		edited = false;
  
! 			if (!do_edit(0, query_buf, &edited, lnptr))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
***************
*** 933,938 ****
--- 982,1092 ----
  		free(fname);
  	}
  
+ 	/*
+ 	 * \sf -- show the named function
+ 	 */
+ 	else if (strncmp(cmd, "sf", 2) == 0)
+ 	{
+ 		int	lineno;
+ 		int	*lnptr = NULL;
+ 		bool	with_lno = false;
+ 		
+ 		if (strcmp(cmd, "sf+") == 0)
+ 			with_lno = true;
+ 	
+ 		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);
+ 			
+ 			/* 
+ 			 * parse linenumber from the right
+ 			 */
+ 			if (func && *func)
+ 			{
+ 				char *endfunc = func + strlen(func) - 1;
+ 				char *c;
+ 				
+ 				c = endfunc;
+ 				while (c >= func)
+ 					if (!isdigit(*c))
+ 						break;
+ 					else
+ 						c--;
+ 				
+ 				if (c < endfunc && c > func)
+ 				{
+ 					if (*c == ' ' || *c == ')')
+ 					{
+ 						c++;
+ 						/* append rows for SQL decoration */
+ 						lineno = atoi(c) + 3;
+ 						*c = '\0';
+ 						lnptr = &lineno;
+ 					}
+ 				}
+ 			}
+ 			
+ 			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;
+ 			
+ 			printf("\n");
+ 			while (*c)
+ 			{
+ 				for (ptr = c; *c != '\n'; c++);
+ 				*c++ = '\0';
+ 				lineno++;
+ 				
+ 				if (lnptr && (*lnptr > 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,1512 ****
   */
  
  static bool
! editFile(const char *fname)
  {
  	const char *editorName;
  	char	   *sys;
--- 1660,1666 ----
   */
  
  static bool
! editFile(const char *fname, int *lineno)
  {
  	const char *editorName;
  	char	   *sys;
***************
*** 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)
--- 1684,1703 ----
  	 * 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)
! 		sprintf(sys, "exec %s '%s'", editorName, fname);
! 	else
! 		sprintf(sys, "exec %s +%d '%s'", editorName, *lineno, fname);
  #else
! 	if (!lineno)
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
! 	else
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" +%d \"%s\"" SYSTEMQUOTE, 
! 									    editorName, 
! 									    *lineno, 
! 									    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;
--- 1712,1718 ----
  
  /* 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)
  	{
--- 1804,1810 ----
  
  	/* call editor */
  	if (!error)
! 		error = !editFile(fname, lineno);
  
  	if (!error && stat(fname, &after) != 0)
  	{
***************
*** 2197,2199 ****
--- 2360,2363 ----
  
  	destroyPQExpBuffer(msg);
  }
+ 
*** ./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/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);
