*** ./command.c.orig	2009-12-18 09:18:14.000000000 +0100
--- ./command.c	2009-12-30 20:02:04.002183749 +0100
***************
*** 48,53 ****
--- 48,54 ----
  #include "mainloop.h"
  #include "print.h"
  #include "psqlscan.h"
+ #include "psqlscript.h"
  #include "settings.h"
  #include "variables.h"
  
***************
*** 63,68 ****
--- 64,72 ----
  static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
  static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf);
  static void minimal_error_message(PGresult *res);
+ static PGresult *fetch_next(char *cursor_name);
+ static bool exec_boolean_expr(char *expr, bool *success);
+ 
  
  static void printSSLInfo(void);
  
***************
*** 70,77 ****
  static void checkWin32Codepage(void);
  #endif
  
- 
- 
  /*----------
   * HandleSlashCmds:
   *
--- 74,79 ----
***************
*** 102,110 ****
  
  	/* Parse off the command name */
  	cmd = psql_scan_slash_command(scan_state);
! 
! 	/* And try to execute it */
! 	status = exec_command(cmd, scan_state, query_buf);
  
  	if (status == PSQL_CMD_UNKNOWN)
  	{
--- 104,125 ----
  
  	/* Parse off the command name */
  	cmd = psql_scan_slash_command(scan_state);
! 	
! 	if (!pset.skip_mode 
! 		|| strcmp(cmd, "endif") == 0
! 		|| strcmp(cmd, "else") == 0
! 		|| strcmp(cmd, "elseif") == 0
! 		|| strcmp(cmd, "endforc") == 0
! 		|| strcmp(cmd, "if") == 0
! 		|| strcmp(cmd, "forc") == 0
! 		|| strcmp(cmd, "ifdef") == 0
! 		|| strcmp(cmd, "endifdef") == 0
! 		|| strcmp(cmd, "newcommand") == 0
! 		|| strcmp(cmd, "endnewcommand") == 0)
! 	{
! 		/* And try to execute it */
! 		status = exec_command(cmd, scan_state, query_buf);
! 	}
  
  	if (status == PSQL_CMD_UNKNOWN)
  	{
***************
*** 115,121 ****
  		status = PSQL_CMD_ERROR;
  	}
  
! 	if (status != PSQL_CMD_ERROR)
  	{
  		/* eat any remaining arguments after a valid command */
  		/* note we suppress evaluation of backticks here */
--- 130,136 ----
  		status = PSQL_CMD_ERROR;
  	}
  
! 	if (status != PSQL_CMD_ERROR && !pset.skip_mode)
  	{
  		/* eat any remaining arguments after a valid command */
  		/* note we suppress evaluation of backticks here */
***************
*** 619,624 ****
--- 634,868 ----
  		}
  	}
  
+ 	else if (strcmp(cmd, "else") == 0)
+ 	{
+ 		/* check current_stmt, should be PSQL_IF or PSQL_IFDEF */
+ 		if (pset.current_stmt->typ != PSQL_IF && pset.current_stmt->typ != PSQL_IFDEF)
+ 		{
+ 			psql_error("\\%s: without \\if or \\ifdef\n", cmd);
+ 			success = false;
+ 		}
+ 		else
+ 		{
+ 			psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt;
+ 			
+ 			if (!nestedif->outer_skip_mode)
+ 			{
+ 				if (!nestedif->successful)
+ 				{
+ 					pset.skip_mode = false;
+ 					nestedif->successful = true;
+ 				}
+ 				else
+ 					pset.skip_mode = true;
+ 			}
+ 		}
+ 	}
+ 
+ 	else if (strcmp(cmd, "endif") == 0)
+ 	{
+ 		pset.current_stmt = remove_stmt(pset.current_stmt, PSQL_IF, cmd, &status, &pset.skip_mode);
+ 	}
+ 	
+ 	else if (strcmp(cmd, "endifdef") == 0)
+ 	{
+ 		pset.current_stmt = remove_stmt(pset.current_stmt, PSQL_IFDEF, cmd, &status, &pset.skip_mode);
+ 	}
+ 
+ 	else if (strcmp(cmd, "endforc") == 0)
+ 	{
+ 		/* 
+ 		 * fetch record from cursor,
+ 		 * if not NULL, then pop stacked lines, and repeat processing
+ 		 */
+ 		if (!pset.current_loop || pset.current_loop->typ != PSQL_FORC)
+ 		{
+ 			psql_error("\\endforc without forc\n");
+ 			/* error already reported */
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else
+ 		{
+ 			char	   *cursor_name;
+ 			PGresult   *res;
+ 			
+ 			psql_assert(pset.current_loop);
+ 			psql_assert(pset.current_loop->typ == PSQL_FORC);
+ 			
+ 			cursor_name = psql_scan_slash_option(scan_state,
+ 									  OT_NORMAL, NULL, true);
+ 									  
+ 			if (cursor_name && strcmp(cursor_name, pset.current_loop->params.cursor_name) != 0)
+ 			{
+ 				psql_error("unmatched cursor name: %s is different than %s\n",
+ 									    cursor_name,
+ 									    pset.current_loop->params.cursor_name);
+ 				status = PSQL_CMD_ERROR;
+ 			} 
+ 			else if (pset.skip_mode || pset.current_loop->lines->len == 0)
+ 			{
+ 				/* 
+ 				 * Process statement, but doesn't activate anything.
+ 				 * because cycle is blocked in this moment, we can 
+ 				 * remove this cycle.
+ 				 */
+ 				pset.current_loop = remove_loop(pset.current_loop,
+ 										PSQL_FORC,
+ 										cmd,
+ 										&status,
+ 										&pset.skip_mode);
+ 				pset.current_stmt = remove_stmt(pset.current_stmt, 
+ 										PSQL_FORC, 
+ 										cmd, 
+ 										&status, 
+ 										&pset.skip_mode);
+ 			}
+ 			else
+ 			{
+ 				res = fetch_next(pset.current_loop->params.cursor_name);
+ 			
+ 				if (!res)
+ 				{
+ 					/* ToDo: free cur_nested_block */
+ 					success = false;
+ 				}
+ 				else
+ 				{
+ 					if (PQntuples(res) > 0)
+ 					{
+ 						int	i;
+ 				
+ 						psql_assert(PQntuples(res) == 1);
+ 						psql_assert(pset.current_loop->lines->len > 0);
+ 				
+ 						/* refresh cursor variables */
+ 						for (i = 0; i < PQnfields(res); i++)
+ 							SetVariable(pset.vars, PQfname(res,i),
+ 										PQgetvalue(res, 0, i));
+ 						
+ 						/* lock buffer - repeat same code */
+ 						pset.current_loop->writeable = false;
+ 						/* copy stacked code to readable buffer */
+ 						if (pset.current_loop->restored_lines == NULL)
+ 							pset.current_loop->restored_lines = pg_malloc(pset.current_loop->lines->len);
+ 						
+ 						memcpy(pset.current_loop->restored_lines, pset.current_loop->lines->data,
+ 											  pset.current_loop->lines->len);
+ 						pset.current_loop->reader_pos = pset.current_loop->restored_lines;
+ 					}
+ 					else
+ 					{
+ 						/* it was last cycle */
+ 					
+ 						/* 
+ 						 * because psql variables are not stacked, we don't remove
+ 						 * cursors variables on the end.
+ 						 */
+ 						pset.current_loop = remove_loop(pset.current_loop,
+ 											PSQL_FORC,
+ 											cmd,
+ 											&status,
+ 											&pset.skip_mode);
+ 						pset.current_stmt = remove_stmt(pset.current_stmt, 
+ 							    				PSQL_FORC, 
+ 								    			cmd, 
+ 											&status, 
+ 											&pset.skip_mode);
+ 					}
+ 				}
+ 			
+ 				PQclear(res);
+ 			}
+ 			
+ 			if (cursor_name)
+ 				free(cursor_name);
+ 		}
+ 	}
+ 	
+ 	/* \endnewcommand */
+ 	else if (strcmp(cmd, "endnewcommand") == 0)
+ 	{
+ 		if (pset.custom_statements == NULL || pset.custom_statements->writer == NULL)
+ 		{
+ 			psql_error("\\endnewcommand without newcommand\n");
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else
+ 		{
+ 			char	*name = pset.custom_statements->name;
+ 			psqlCustomStatement 	*next = pset.custom_statements->next;
+ 			psqlCustomStatement 	*current = pset.custom_statements;
+ 			
+ 			/* remove stmt from stack */
+ 			pset.current_stmt = remove_stmt(pset.current_stmt,
+ 								    PSQL_NEWCOMMAND,
+ 								    cmd,
+ 								    &status,
+ 								    &pset.skip_mode);
+ 
+ 			/* when outer skip_mode is enabled, then this definition is fake */
+ 			if (pset.skip_mode)
+ 			{
+ 				destroyPQExpBuffer(current->writer);
+ 				pset.custom_statements = next;
+ 				free(current);
+ 			}
+ 			else
+ 			{
+ 				/* 
+ 				 * Safe src of currently defined command. We have to remove 
+ 				 * last two lines (last line have to be empty).
+ 				 */
+ 				int len = current->writer->len - 1;
+ 				char *rightptr = current->writer->data + current->writer->len - 2; /* \n\0 */
+ 				
+ 				while (len > 0 && *rightptr != '\n')
+ 				{
+ 					len -= 1;
+ 					rightptr--;
+ 				}
+ 				
+ 				psql_assert(strcmp(rightptr+1,"\\endnewcommand\n") == 0);
+ 				*rightptr = '\0';
+ 				
+ 				/* drop older definition of command if exists */
+ 				while (next != NULL)
+ 				{
+ 					if (strcmp(name, next->name) == 0)
+ 					{
+ 						/* find command to overwrite */
+ 						current->next = next->next;
+ 						free(next->name);
+ 						free(next->src);
+ 						free(next);
+ 					
+ 						break;
+ 					}
+ 				
+ 					current = next;
+ 					next = next->next;
+ 				}
+ 				
+ 				if (len > 0)
+ 				{
+ 					/* store src */
+ 					pset.custom_statements->src = pg_strdup(pset.custom_statements->writer->data);
+ 					destroyPQExpBuffer(pset.custom_statements->writer);
+ 					pset.custom_statements->writer = NULL;
+ 				}
+ 				else
+ 				{
+ 					/* don't store empty command */
+ 					next = pset.custom_statements->next;
+ 					free(pset.custom_statements->name);
+ 					destroyPQExpBuffer(pset.custom_statements->writer);
+ 					free(pset.custom_statements);
+ 					pset.custom_statements = next;
+ 				}
+ 			}
+ 		}
+ 	}
+ 
  	/* \f -- change field separator */
  	else if (strcmp(cmd, "f") == 0)
  	{
***************
*** 629,634 ****
--- 873,936 ----
  		free(fname);
  	}
  
+ 	else if (strcmp(cmd, "forc") == 0)
+ 	{
+ 		char	   *cursor_name;
+ 
+ 		cursor_name = psql_scan_slash_option(scan_state,
+ 									  OT_NORMAL, NULL, true);
+ 		if (!cursor_name)
+ 		{
+ 			psql_error("\\for: missing cursor name\n");
+ 			/* error already reported */
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else if (pset.skip_mode)
+ 		{
+ 			/* do fake */
+ 			pset.current_loop = new_for_cursor(cursor_name, pset.current_loop, pset.skip_mode);
+ 			pset.current_stmt = new_loop(pset.current_stmt, PSQL_FORC, pset.skip_mode);
+ 		}
+ 		else
+ 		{
+ 			PGresult   *res;
+ 
+ 			/* 
+ 			 * fetch first record from cursor,
+ 			 * process and stack all lines to \endfor
+ 			 */
+ 			res = fetch_next(cursor_name);
+ 			
+ 			if (res)
+ 			{
+ 				/* create new loop */
+ 				pset.current_loop = new_for_cursor(cursor_name, pset.current_loop, pset.skip_mode);
+ 				pset.current_stmt = new_loop(pset.current_stmt, PSQL_FORC, pset.skip_mode);
+ 
+ 				if (PQntuples(res) > 0)
+ 				{
+ 					int	i;
+ 					
+ 					psql_assert(PQntuples(res) == 1);
+ 					
+ 					/* create or refresh cursor's variables */
+ 					for (i = 0; i < PQnfields(res); i++)
+ 						SetVariable(pset.vars, PQfname(res,i),
+ 									PQgetvalue(res, 0, i));
+ 				}
+ 				else
+ 				{
+ 					/* zero loop */
+ 					pset.skip_mode = true;
+ 				}
+ 			
+ 				PQclear(res);
+ 			}
+ 			else
+ 				success = false;
+ 		}
+ 	}
+ 
  	/* \g means send query */
  	else if (strcmp(cmd, "g") == 0)
  	{
***************
*** 685,696 ****
--- 987,1242 ----
  		}
  	}
  
+ 	/* \if expr */
+ 	else if (strcmp(cmd, "if") == 0 || strcmp(cmd, "elseif") == 0)
+ 	{
+ 		char	*expr;
+ 		char		*el;
+ 		
+ 		el = psql_scan_slash_option(scan_state,
+ 									OT_NORMAL, NULL, true);
+ 		expr = pg_strdup(el ? el : "");
+ 		free(el);
+ 		
+ 		while ((el  = psql_scan_slash_option(scan_state,
+ 								 OT_NORMAL, NULL, false)))
+ 		{
+ 			expr = realloc(expr, strlen(expr) + strlen(el) + 2);
+ 			if (!expr)
+ 			{
+ 				psql_error("out of memory\n");
+ 				exit(EXIT_FAILURE);
+ 			}
+ 			strcat(expr, " ");	/* don't drop spaces */
+ 			strcat(expr, el);
+ 			free(el);
+ 		}
+ 
+ 		if (strcmp(expr,"") == 0)
+ 		{
+ 			psql_error("\\%s: missing required argument\n", cmd);
+ 			success = false;
+ 		}
+ 		else if (strcmp(cmd, "elseif") == 0)
+ 		{
+ 			/* check current_stmt, should be PSQL_IF */
+ 			if (pset.current_stmt->typ != PSQL_IF)
+ 			{
+ 				psql_error("\\%s: without \\if\n", cmd);
+ 				success = false;
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * Don't create new nested_stmt for elseif.
+ 				 */
+ 				psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt;
+ 				if (!nestedif->successful && !nestedif->outer_skip_mode)
+ 				{
+ 					bool	result = exec_boolean_expr(expr, &success);
+ 					if (success)
+ 					{
+ 						pset.skip_mode = !result;
+ 						nestedif->successful = true;
+ 					}
+ 					else
+ 						pset.skip_mode = true;
+ 				}
+ 			}
+ 		}
+ 		else if (pset.skip_mode)
+ 		{
+ 			/* fake statement */
+ 			pset.current_stmt = new_if(pset.current_stmt, PSQL_IF, pset.skip_mode);
+ 			((psqlNestedIf *) pset.current_stmt)->successful = true;
+ 		}
+ 		else
+ 		{
+ 			bool result;
+ 			
+ 			pset.current_stmt = new_if(pset.current_stmt, PSQL_IF, pset.skip_mode);
+ 			result = exec_boolean_expr(expr, &success);
+ 			if (success)
+ 			{
+ 				pset.skip_mode = !result;
+ 				((psqlNestedIf *) pset.current_stmt)->successful = true;
+ 			}
+ 			else
+ 				pset.skip_mode = false;
+ 			free(expr);
+ 		}
+ 	}
+ 	
+ 	/* \ifdef variable */
+ 	else if (strcmp(cmd, "ifdef") == 0 || strcmp(cmd, "elseifdef") == 0)
+ 	{
+ 		char	*varname;
+ 		
+ 		varname = psql_scan_slash_option(scan_state,
+ 									OT_NORMAL, NULL, true);
+ 		
+ 		if (!varname)
+ 		{
+ 			psql_error("\\%s: missing required argument\n", cmd);
+ 			success = false;
+ 		}
+ 		else if (strcmp(cmd, "elseifdef") == 0)
+ 		{
+ 			/* check current_stmt, should be PSQL_IFDEF */
+ 			if (pset.current_stmt->typ != PSQL_IFDEF)
+ 			{
+ 				psql_error("\\%s: without \\if\n", cmd);
+ 				success = false;
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * Don't create new nested_stmt for elseif.
+ 				 */
+ 				psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt;
+ 				if (!nestedif->successful && !nestedif->outer_skip_mode)
+ 				{
+ 					const char *value = GetVariable(pset.vars, varname);
+ 
+ 					if (value)
+ 					{
+ 						pset.skip_mode = false;
+ 						nestedif->successful = true;
+ 					}
+ 					else
+ 						pset.skip_mode = true;
+ 				}
+ 			}
+ 		}
+ 		else if (pset.skip_mode)
+ 		{
+ 			/* fake statement */
+ 			pset.current_stmt = new_if(pset.current_stmt, PSQL_IFDEF, pset.skip_mode);
+ 			((psqlNestedIf *) pset.current_stmt)->successful = true;
+ 		}
+ 		else
+ 		{
+ 			const char *value = GetVariable(pset.vars, varname);
+ 			
+ 			pset.current_stmt = new_if(pset.current_stmt, PSQL_IFDEF, pset.skip_mode);
+ 			((psqlNestedIf *) pset.current_stmt)->successful = (value != NULL);
+ 			pset.skip_mode = (value == NULL);
+ 			
+ 			free(varname);
+ 		}
+ 	}
+ 	
+ 	/* \newcommand for define new command for psql */
+ 	else if (strcmp(cmd, "newcommand") == 0)
+ 	{
+ 		char	   *name;
+ 
+ 		name = psql_scan_slash_option(scan_state,
+ 									  OT_WHOLE_LINE, NULL, true);
+ 
+ 		if (!name)
+ 		{
+ 			psql_error("\\newcommand: missing command name\n");
+ 			/* error already reported */
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else if (pset.skip_mode)
+ 		{
+ 			/* do fake */
+ 			pset.current_stmt = new_newcommand(pset.current_stmt, PSQL_NEWCOMMAND, pset.skip_mode);
+ 			pset.custom_statements = new_custom_statement("***fake***", pset.custom_statements, &status);
+ 		}
+ 		else
+ 		{
+ 			pset.current_stmt = new_newcommand(pset.current_stmt, PSQL_NEWCOMMAND, pset.skip_mode);
+ 			pset.custom_statements = new_custom_statement(name, pset.custom_statements, &status);
+ 			
+ 			/* inside definition don't execute any statement */
+ 			pset.skip_mode = true;
+ 		}
+ 	}
+ 
  	/* \l is list databases */
  	else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
  		success = listAllDbs(false);
  	else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
  		success = listAllDbs(true);
  
+ 	else if (strcmp(cmd, "lf") == 0 || (strcmp(cmd, "lf-") == 0))
+ 	{
+ 		char	*func;
+ 		Oid		foid = InvalidOid;
+ 		
+ 		func = psql_scan_slash_option(scan_state,
+ 							    OT_WHOLE_LINE, NULL, true);
+ 		if (!query_buf)
+ 		{
+ 			psql_error("no query buffer\n");
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else
+ 		{
+ 			if (!func)
+ 			{
+ 				psql_error("missing function name");
+ 				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;
+ 			}
+ 			else
+ 			{
+ 				char *cursor = query_buf->data;
+ 				char	*line = cursor;
+ 				bool	print_line_number = false;
+ 				int	row_number = 1;
+ 				
+ 				if (strcmp(cmd, "lf-") == 0)
+ 					fputs(query_buf->data, pset.queryFout);
+ 				else
+ 				{
+ 					/* print source code with line numbers */
+ 					while (*cursor != '\0')
+ 					{
+ 						if (*cursor == '\n')
+ 						{
+ 							*cursor = '\0';
+ 							if (strncmp(line, "AS $function$", 13) == 0)
+ 							{
+ 								print_line_number = true;
+ 								if (strlen(line) == 13)
+ 								{
+ 									fprintf(pset.queryFout, "****\t%s\n", line);
+ 									line = ++cursor;
+ 									continue;
+ 								}
+ 							}
+ 							else if (strcmp(line, "$function$") ==0)
+ 								print_line_number = false;
+ 						
+ 							if (print_line_number)
+ 								fprintf(pset.queryFout, "%4d\t%s\n", row_number++, line);
+ 							else
+ 								fprintf(pset.queryFout, "****\t%s\n", line);
+ 							line = ++cursor;
+ 						}
+ 						else
+ 							cursor++;
+ 					}
+ 				}
+ 				if (func)
+ 					free(func);
+ 			}
+ 		}
+ 	}
+ 
  	/*
  	 * large object things
  	 */
***************
*** 1762,1767 ****
--- 2308,2316 ----
  	size_t		vallen = 0;
  
  	psql_assert(param);
+ 	
+ 	if (pset.skip_mode)
+ 		return true;
  
  	if (value)
  		vallen = strlen(value);
***************
*** 1804,1812 ****
  			popt->topt.line_style = &pg_asciiformat_old;
  		else if (pg_strncasecmp("unicode", value, vallen) == 0)
  			popt->topt.line_style = &pg_utf8format;
  		else
  		{
! 			psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n");
  			return false;
  		}
  
--- 2353,2373 ----
  			popt->topt.line_style = &pg_asciiformat_old;
  		else if (pg_strncasecmp("unicode", value, vallen) == 0)
  			popt->topt.line_style = &pg_utf8format;
+ 		else if (pg_strncasecmp("custom-single", value, vallen) == 0)
+ 			popt->topt.line_style = &pg_utf8_custom_single_format;
+ 		else if (pg_strncasecmp("custom-double-border", value, vallen) == 0)
+ 			popt->topt.line_style = &pg_utf8_custom_double_border_format;
+ 		else if (pg_strncasecmp("custom-double-border-header", value, vallen) == 0)
+ 			popt->topt.line_style = &pg_utf8_custom_double_border_header_format;
+ 		else if (pg_strncasecmp("custom-double-border-header-columns", value, vallen) == 0)
+ 			popt->topt.line_style = &pg_utf8_custom_double_border_header_columns_format;
+ 		else if (pg_strncasecmp("custom-elegant", value, vallen) == 0)
+ 			popt->topt.line_style = &pg_utf8_custom_elegant_format;
  		else
  		{
! 			psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode,\n"
! 			           "             custom-single, custom-double-border, custom-double-border-header,\n"
! 			           "             custom-double-border-header-columns, custom-elegant\n");
  			return false;
  		}
  
***************
*** 1814,1819 ****
--- 2375,2422 ----
  			printf(_("Line style is %s.\n"),
  				   get_line_style(&popt->topt)->name);
  	}
+ 	
+ 	/* set header style */
+ 	else if (strcmp(param, "headerstyle") == 0)
+ 	{
+ 		if (!value)
+ 			;
+ 		else if (pg_strncasecmp("left", value, vallen) == 0)
+ 			popt->topt.header_style = PRINT_HEADER_LEFT;
+ 		else if (pg_strncasecmp("center", value, vallen) == 0)
+ 			popt->topt.header_style = PRINT_HEADER_CENTER;
+ 		else if (pg_strncasecmp("right", value, vallen) == 0)
+ 			popt->topt.header_style = PRINT_HEADER_RIGHT;
+ 		else
+ 		{
+ 			psql_error("\\pset: allowed header styles are left, center, right.\n");
+ 			return false;
+ 		}
+ 		
+ 		if (!quiet)
+ 			printf(_("Header style is %s.\n"),
+ 				   get_header_style(popt->topt.header_style));
+ 	}
+ 	
+ 	/* set wrapping style (applied only on text types) */
+ 	else if (strcmp(param, "textwrapping") == 0)
+ 	{
+ 		if (!value)
+ 			;
+ 		else if (pg_strncasecmp("default", value, vallen) == 0)
+ 		    popt->topt.textwrapping = TEXTWRAPPING_DEFAULT;
+ 		else if (pg_strncasecmp("flush-left", value, vallen) == 0)
+ 		    popt->topt.textwrapping = TEXTWRAPPING_FLUSH_LEFT;
+ 		else
+ 		{
+ 			psql_error("\\pset: allowed textwrapping modes are default, flush-left\n");
+ 			return false;
+ 		}
+ 		
+ 		if (!quiet)
+ 			printf(_("textwrapping mode is %s \n"),
+ 					get_textwrapping_mode(popt->topt.textwrapping));
+ 	}
  
  	/* set border style/width */
  	else if (strcmp(param, "border") == 0)
***************
*** 1837,1842 ****
--- 2440,2458 ----
  				   ? _("Expanded display is on.\n")
  				   : _("Expanded display is off.\n"));
  	}
+ 	
+ 	/* enable multiline in headers */
+ 	else if (strcmp(param, "multiline-header") == 0)
+ 	{
+ 		if (value)
+ 			popt->topt.multiline_header = ParseVariableBool(value);
+ 		else
+ 			popt->topt.multiline_header = !popt->topt.multiline_header;
+ 		if (!quiet)
+ 			printf(popt->topt.multiline_header
+ 				   ? _("Multiline header is enabled.\n")
+ 				   : _("Multiline header is disabled.\n"));
+ 	}
  
  	/* locale-aware numeric output */
  	else if (strcmp(param, "numericlocale") == 0)
***************
*** 2157,2159 ****
--- 2773,2824 ----
  
  	destroyPQExpBuffer(msg);
  }
+ 
+ /* 
+  * fetch tuple from cursor
+  */
+ static PGresult *
+ fetch_next(char *cursor_name)
+ {
+ 	PQExpBufferData		fquery;
+ 	PGresult	*res;
+ 
+ 	initPQExpBuffer(&fquery);
+ 	printfPQExpBuffer(&fquery, "FETCH NEXT %s", cursor_name);
+ 	res = PSQLexec(fquery.data, false);
+ 	termPQExpBuffer(&fquery);
+ 	
+ 	return res;
+ }
+ 
+ /*
+  * exec boolean expression
+  */
+ static bool 
+ exec_boolean_expr(char *expr, bool *success)
+ {
+ 	PQExpBufferData expr_query;
+ 	PGresult   *r;
+ 	bool		result;
+ 	
+ 			
+ 	initPQExpBuffer(&expr_query);
+ 	printfPQExpBuffer(&expr_query, "SELECT (%s)::boolean", expr);
+ 
+ 	r = PSQLexec(expr_query.data, false);
+ 	termPQExpBuffer(&expr_query);
+ 	
+ 	if (!r)
+ 	{
+ 		*success = false;
+ 		PQclear(r);
+ 		
+ 		return false;
+ 	}
+     
+ 	*success = true;
+ 	result = !(PQgetisnull(r, 0, 0) || strcmp(PQgetvalue(r, 0, 0), "f") == 0);
+ 	PQclear(r);
+ 
+ 	return result;
+ }
*** ./help.c.orig	2009-12-18 09:18:14.000000000 +0100
--- ./help.c	2009-12-28 09:59:21.273911721 +0100
***************
*** 224,229 ****
--- 224,230 ----
  	fprintf(output, _("  \\du[+]  [PATTERN]      list roles (users)\n"));
  	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
  	fprintf(output, _("  \\l[+]                  list all databases\n"));
+ 	fprintf(output, _("  \\lf[-]  [PATTERN]      show function's source code\n"));
  	fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
  	fprintf(output, "\n");
  
***************
*** 235,241 ****
  			ON(pset.popt.topt.format == PRINT_HTML));
  	fprintf(output, _("  \\pset NAME [VALUE]     set table output option\n"
  					  "                         (NAME := {format|border|expanded|fieldsep|footer|null|\n"
! 					  "                         numericlocale|recordsep|tuples_only|title|tableattr|pager})\n"));
  	fprintf(output, _("  \\t [on|off]            show only rows (currently %s)\n"),
  			ON(pset.popt.topt.tuples_only));
  	fprintf(output, _("  \\T [STRING]            set HTML <table> tag attributes, or unset if none\n"));
--- 236,243 ----
  			ON(pset.popt.topt.format == PRINT_HTML));
  	fprintf(output, _("  \\pset NAME [VALUE]     set table output option\n"
  					  "                         (NAME := {format|border|expanded|fieldsep|footer|null|\n"
! 					  "                         numericlocale|recordsep|tuples_only|title|tableattr|pager|\n"
! 					  "                         textwrapping})\n"));
  	fprintf(output, _("  \\t [on|off]            show only rows (currently %s)\n"),
  			ON(pset.popt.topt.tuples_only));
  	fprintf(output, _("  \\T [STRING]            set HTML <table> tag attributes, or unset if none\n"));
***************
*** 263,269 ****
  	fprintf(output, _("  \\set [NAME [VALUE]]    set internal variable, or list all if no parameters\n"));
  	fprintf(output, _("  \\unset NAME            unset (delete) internal variable\n"));
  	fprintf(output, "\n");
! 
  	fprintf(output, _("Large Objects\n"));
  	fprintf(output, _("  \\lo_export LOBOID FILE\n"
  					  "  \\lo_import FILE [COMMENT]\n"
--- 265,284 ----
  	fprintf(output, _("  \\set [NAME [VALUE]]    set internal variable, or list all if no parameters\n"));
  	fprintf(output, _("  \\unset NAME            unset (delete) internal variable\n"));
  	fprintf(output, "\n");
! 	
! 	fprintf(output, _("Scripting\n"));
! 	fprintf(output, _("  \\forc CURSORNAME       iteration over cursor\n"));
! 	fprintf(output, _("  \\endforc [CURSORNAME]  end of iteration over cursor\n"));
! 	fprintf(output, _("  \\if VALUE              process body when VALUE is true \n"));
! 	fprintf(output, _("  \\endif                 end of conditional command\n"));
! 	fprintf(output, _("  \\else                  process next lines\n"));
! 	fprintf(output, _("  \\ifdef [NAME]          process body if variable NAME exists\n"));
! 	fprintf(output, _("  \\endifdef              end of ifdef command\n"));
! 	fprintf(output, _("  \\necommand name        define new psql command\n"));
! 	fprintf(output, _("  \\endnewcommand         end of newcommand command\n"));
! 	
! 	fprintf(output, _("\n"));
! 	
  	fprintf(output, _("Large Objects\n"));
  	fprintf(output, _("  \\lo_export LOBOID FILE\n"
  					  "  \\lo_import FILE [COMMENT]\n"
*** ./mainloop.c.orig	2009-12-18 09:18:14.000000000 +0100
--- ./mainloop.c	2009-12-31 12:12:53.032456447 +0100
***************
*** 16,22 ****
  
  #include "mb/pg_wchar.h"
  
- 
  /*
   * Main processing loop for reading lines of input
   *	and sending them to the backend.
--- 16,21 ----
***************
*** 58,63 ****
--- 57,66 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	pset.current_loop = NULL;
+ 	pset.current_stmt = NULL;
+ 	pset.custom_statement_readbuffer = NULL;
+ 	pset.skip_mode = false;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 122,137 ****
  		}
  
  		fflush(stdout);
  
  		/*
  		 * get another line
  		 */
! 		if (pset.cur_cmd_interactive)
  		{
  			/* May need to reset prompt, eg after \r command */
  			if (query_buf->len == 0)
  				prompt_status = PROMPT_READY;
  			line = gets_interactive(get_prompt(prompt_status));
  		}
  		else
  		{
--- 125,251 ----
  		}
  
  		fflush(stdout);
+ 		pset.custom_stmt_mode = false;
  
  		/*
  		 * get another line
  		 */
! 		if (pset.custom_statement_readbuffer)
! 		{
! 			psqlCustomStmtReaderData	*CStmtBuffer = pset.custom_statement_readbuffer;
! 		
! 			char *_line = CStmtBuffer->reader;
! 			char *c = CStmtBuffer->reader;
! 			while (*c != '\n' && *c != '\0')
! 				c++;
! 				
! 			if (*c == '\0')
! 			{
! 				/* last row in definition */
! 				line = pg_strdup(_line);
! 				pset.custom_statement_readbuffer = CStmtBuffer->outer;
! 				free(CStmtBuffer->src);
! 				free(CStmtBuffer);
! 			}
! 			else
! 			{
! 				*c++ = '\0';
! 				pset.custom_statement_readbuffer->reader = c;
! 				line = pg_strdup(_line);
! 			}
! 			pset.custom_stmt_mode = true;
! 		}
! 		else if (pset.current_loop && pset.current_loop->reader_pos)
  		{
+ 			if (*(pset.current_loop->reader_pos) != '\0')
+ 			{
+ 				char	*_line = pset.current_loop->reader_pos;
+ 				/* move string_reading_pos to next line */
+ 				char *c = pset.current_loop->reader_pos;
+ 				
+ 				while (*c != '\n' && *c != '\0')
+ 					c++;
+ 				
+ 				if (*c == '\n')
+ 					*c++ = '\0';
+ 				
+ 				/* store begin of next line */
+ 				pset.current_loop->reader_pos = c;
+ 				line = pg_strdup(_line);
+ 			}
+ 			else
+ 			{
+ 				line = NULL;
+ 				successResult = EXIT_FAILURE;
+ 			}
+ 		}
+ 		else if (pset.cur_cmd_interactive)
+ 		{
+ 			psqlCustomStatement	*cstmt = pset.custom_statements;
+ 		
  			/* May need to reset prompt, eg after \r command */
  			if (query_buf->len == 0)
  				prompt_status = PROMPT_READY;
  			line = gets_interactive(get_prompt(prompt_status));
+ 			
+ 			/* try to evaluate custom command, but only when there are not active writer */
+ 			if (line != NULL && !(pset.custom_statements && pset.custom_statements->writer))
+ 			{
+ 				while (cstmt != NULL)
+ 				{
+ 					psqlCustomStmtReaderData *reader;
+ 					PsqlScanState scan_state_custom_call;
+ 					int	i;
+ 					char		buffer[5];
+ 					int	namelen = strlen(cstmt->name);
+ 					char	*args = line + namelen;
+ 
+ 					
+ 					if (strncmp(line, cstmt->name, namelen) != 0)
+ 					{
+ 						cstmt = cstmt->next;
+ 						continue;
+ 					}
+ 					/* when line is longer, then command name, then char on namelen+1 have to be space */
+ 					else if (strlen(line) > namelen && line[namelen] != ' ')
+ 					{
+ 						cstmt = cstmt->next;
+ 						continue;
+ 					}
+ 
+ 					reader = pg_malloc(sizeof(psqlCustomStmtReaderData));
+ 					reader->outer = pset.custom_statement_readbuffer;
+ 					reader->src = pg_strdup(cstmt->src);
+ 					reader->reader = reader->src;
+ 					pset.custom_statement_readbuffer = reader;
+ 					
+ 					pg_append_history(line, history_buf);
+ 					pg_send_history(history_buf);
+ 						
+ 					scan_state_custom_call = psql_scan_create();
+ 					psql_scan_setup(scan_state_custom_call, args, strlen(args));
+ 						
+ 					for (i = 1; i <= 10; i++)
+ 					{
+ 						char *opt;
+ 						
+ 						snprintf(buffer, 10, "%d", i);
+ 						opt = psql_scan_slash_option(scan_state_custom_call,
+ 											 OT_NORMAL, NULL, true);
+ 						SetVariable(pset.vars, buffer, opt);
+ 					}
+ 					
+ 					psql_scan_finish(scan_state_custom_call);
+ 					psql_scan_destroy(scan_state_custom_call);
+ 					free(line);
+ 					line = NULL;
+ 					
+ 					break;
+ 				}
+ 			
+ 				if (line == NULL)
+ 					continue;
+ 			}
  		}
  		else
  		{
***************
*** 214,225 ****
  		/* Setting this will not have effect until next line. */
  		die_on_error = pset.on_error_stop;
  
  		/*
  		 * Parse line, looking for command separators.
  		 */
  		psql_scan_setup(scan_state, line, strlen(line));
  		success = true;
! 		line_saved_in_history = false;
  
  		while (success || !die_on_error)
  		{
--- 328,350 ----
  		/* Setting this will not have effect until next line. */
  		die_on_error = pset.on_error_stop;
  
+ 		/* save line, if stack buffer is opened */
+ 		if (pset.current_loop && pset.current_loop->writeable)
+ 			appendPQExpBuffer(pset.current_loop->lines, "%s\n", line);
+ 		
+ 		/* save command if writer is active */
+ 		if (pset.custom_statements && pset.custom_statements->writer)
+ 			appendPQExpBuffer(pset.custom_statements->writer, "%s\n", line);
+ 
  		/*
  		 * Parse line, looking for command separators.
  		 */
  		psql_scan_setup(scan_state, line, strlen(line));
  		success = true;
! 		
! 		/* stacked lines are saved in history */
! 		
! 		line_saved_in_history = (pset.current_loop && pset.current_loop->reader_pos != NULL) || pset.custom_stmt_mode;
  
  		while (success || !die_on_error)
  		{
***************
*** 248,260 ****
  				 */
  				if (pset.cur_cmd_interactive && !line_saved_in_history)
  				{
  					pg_append_history(line, history_buf);
  					pg_send_history(history_buf);
  					line_saved_in_history = true;
  				}
  
  				/* execute query */
! 				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
  
  				/* transfer query to previous_buf by pointer-swapping */
--- 373,389 ----
  				 */
  				if (pset.cur_cmd_interactive && !line_saved_in_history)
  				{
+ 				
  					pg_append_history(line, history_buf);
  					pg_send_history(history_buf);
  					line_saved_in_history = true;
  				}
  
  				/* execute query */
! 				if (!pset.skip_mode)
! 					success = SendQuery(query_buf->data);
! 				else
! 					success = PSQL_CMD_SEND;
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
  
  				/* transfer query to previous_buf by pointer-swapping */
*** ./Makefile.orig	2009-11-11 00:12:13.000000000 +0100
--- ./Makefile	2009-12-18 09:20:35.000000000 +0100
***************
*** 22,28 ****
  OBJS=	command.o common.o help.o input.o stringutils.o mainloop.o copy.o \
  	startup.o prompt.o variables.o large_obj.o print.o describe.o \
  	tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \
! 	sql_help.o \
  	$(WIN32RES)
  
  FLEXFLAGS = -Cfe
--- 22,28 ----
  OBJS=	command.o common.o help.o input.o stringutils.o mainloop.o copy.o \
  	startup.o prompt.o variables.o large_obj.o print.o describe.o \
  	tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \
! 	sql_help.o psqlscript.o \
  	$(WIN32RES)
  
  FLEXFLAGS = -Cfe
*** ./print.c.orig	2009-12-18 09:18:14.000000000 +0100
--- ./print.c	2009-12-31 12:16:14.652455771 +0100
***************
*** 52,57 ****
--- 52,59 ----
  		{ "-", "+", "+", "+" },
  		{ "-", "+", "+", "+" },
  		{ "-", "+", "+", "+" },
+ 		{ "",  "|", "|", "|" },
+ 		{ "-", "+", "+", "+" },
  		{ "",  "|", "|", "|" }
  	},
  	"|",
***************
*** 73,78 ****
--- 75,82 ----
  		{ "-", "+", "+", "+" },
  		{ "-", "+", "+", "+" },
  		{ "-", "+", "+", "+" },
+ 		{ "",  "|", "|", "|" },
+ 		{ "-", "+", "+", "+" },
  		{ "",  "|", "|", "|" }
  	},
  	":",
***************
*** 98,103 ****
--- 102,111 ----
  		/* ─, └, ┴, ┘ */
  		{ "\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230" },
  		/* N/A, │, │, │ */
+ 		{ "", "\342\224\202", "\342\224\202", "\342\224\202" },
+ 		/* ─, ├, ┼, ┤ */
+ 		{ "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" },
+ 		/* N/A, │, │, │ */
  		{ "", "\342\224\202", "\342\224\202", "\342\224\202" }
  	},
  	/* │ */
***************
*** 119,127 ****
  	true
  };
  
  
  /* Local functions */
! static int	strlen_max_width(unsigned char *str, int *target_width, int encoding);
  static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
  			  FILE **fout, bool *is_pager);
  
--- 127,298 ----
  	true
  };
  
+ const printTextFormat pg_utf8_custom_single_format =
+ {
+ 	"custom-single",
+ 	{
+ 		/* ─, ┌, ┬, ┐ */
+ 		{ "\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220" },
+ 		/* ─, ├, ┴, ┤ */
+ 		{ "\342\224\200", "\342\224\234", "\342\224\264", "\342\224\244" },
+ 		/* ─, └, ┴, ┘ */
+ 		{ "\342\224\200", "\342\224\224", "\342\224\200", "\342\224\230" },
+ 		/* N/A, │, │, │ */
+ 		{ "", "\342\224\202", "\342\224\202", "\342\224\202" },
+ 		/* ─, ├, ─, ┤ */
+ 		{ "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" },
+ 		/* N/A, │, │, │ */
+ 		{ "", "\342\224\202", "\342\224\202", "\342\224\202" }
+ 	},
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	true
+ };
+ 
+ const printTextFormat pg_utf8_custom_double_border_format =
+ {
+ 	"custom-double-border",
+ 	{
+ 		/* ═, ╔, ╤, ╗ */
+ 		{ "\342\225\220", "\342\225\224", "\342\225\244", "\342\225\227" },
+ 		/* ─, ╟, ┼, ╢ */
+ 		{ "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" },
+ 		/* ═, ╚, ╧, ╝ */
+ 		{ "\342\225\220", "\342\225\232", "\342\225\247", "\342\225\235" },
+ 		/* N/A, ║, │, ║ */
+ 		{ "", "\342\225\221", "\342\224\202", "\342\225\221" },
+ 		/* ─, ╟, ┼, ╢ */
+ 		{ "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" },
+ 		/* N/A, ║, │, ║ */
+ 		{ "", "\342\225\221", "\342\224\202", "\342\225\221" }
+ 	},
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	true
+ };
+ 
+ const printTextFormat pg_utf8_custom_double_border_header_format =
+ {
+ 	"custom-double-border-header",
+ 	{
+ 		/* ═, ╔, ╤, ╗ */
+ 		{ "\342\225\220", "\342\225\224", "\342\225\244", "\342\225\227" },
+ 		/* ═, ╠, ╪, ╣ */
+ 		{ "\342\225\220", "\342\225\240", "\342\225\252", "\342\225\243" },
+ 		/* ═, ╚, ╧, ╝ */
+ 		{ "\342\225\220", "\342\225\232", "\342\225\247", "\342\225\235" },
+ 		/* N/A, ║, │, ║ */
+ 		{ "", "\342\225\221", "\342\224\202", "\342\225\221" },
+ 		/* ─, ╟, ┼, ╢ */
+ 		{ "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" },
+ 		/* N/A, ║, │, ║ */
+ 		{ "", "\342\225\221", "\342\224\202", "\342\225\221" }
+ 	},
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	true
+ };
+ 
+ const printTextFormat pg_utf8_custom_double_border_header_columns_format =
+ {
+ 	"custom-double-border-header-columns",
+ 	{
+ 		/* ═, ╔, ╦, ╗ */
+ 		{ "\342\225\220", "\342\225\224", "\342\225\246", "\342\225\227" },
+ 		/* ═, ╠, ╬, ╣ */
+ 		{ "\342\225\220", "\342\225\240", "\342\225\254", "\342\225\243" },
+ 		/* ═, ╚, ╩, ╝ */
+ 		{ "\342\225\220", "\342\225\232", "\342\225\251", "\342\225\235" },
+ 		/* N/A, ║, ║, ║ */
+ 		{ "", "\342\225\221", "\342\225\221", "\342\225\221" },
+ 		/* ─, ╟, ╫, ╢ */
+ 		{ "\342\224\200", "\342\225\237", "\342\225\253", "\342\225\242" },
+ 		/* N/A, ║, ║, ║ */
+ 		{ "", "\342\225\221", "\342\225\221", "\342\225\221" }
+ 	},
+ 	/* ║ */
+ 	"\342\225\221",
+ 	/* ║ */
+ 	"\342\225\221",
+ 	/* ║ */
+ 	"\342\225\221",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	true
+ };
+ 
+ const printTextFormat pg_utf8_custom_elegant_format =
+ {
+ 	"custom-elegant",
+ 	{
+ 		/* ═, ╔, ╦, ╗ */
+ 		{ "\342\225\220", "\342\225\224", "\342\225\246", "\342\225\227" },
+ 		/* ═, ╠, ╩, ╣ */
+ 		{ "\342\225\220", "\342\225\240", "\342\225\251", "\342\225\243" },
+ 		/* ═, ╚, ═, ╝ */
+ 		{ "\342\225\220", "\342\225\232", "\342\225\220", "\342\225\235" },
+ 		/* N/A, ║, │, ║ */
+ 		{ "", "\342\225\221", "\342\224\202", "\342\225\221" },
+ 		/* ─, ╟, ┼, ╢ */
+ 		{ "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" },
+ 		/* N/A, ║, ║, ║ */
+ 		{ "", "\342\225\221", "\342\225\221", "\342\225\221" }
+ 	},
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	/* │ */
+ 	"\342\224\202",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	" ",
+ 	true
+ };
+ 
  
  /* Local functions */
! static int strlen_max_width(unsigned char *str, int *target_width, int encoding, 
! 						    printTextWrappingMode wmod, 
! 						    bool *word_overflow);
! 
  static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
  			  FILE **fout, bool *is_pager);
  
***************
*** 450,456 ****
  
  	if (border == 1)
  		fputs(lformat->hrule, fout);
! 	else if (border == 2)
  		fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
  
  	for (i = 0; i < ncolumns; i++)
--- 621,627 ----
  
  	if (border == 1)
  		fputs(lformat->hrule, fout);
! 	else if (border > 1)
  		fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
  
  	for (i = 0; i < ncolumns; i++)
***************
*** 468,474 ****
  		}
  	}
  
! 	if (border == 2)
  		fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
  	else if (border == 1)
  		fputs(lformat->hrule, fout);
--- 639,645 ----
  		}
  	}
  
! 	if (border > 1)
  		fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
  	else if (border == 1)
  		fputs(lformat->hrule, fout);
***************
*** 489,494 ****
--- 660,666 ----
  	unsigned short opt_border = cont->opt->border;
  	const printTextFormat *format = get_line_style(cont->opt);
  	const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
+ 	const printTextLineFormat *hformat = &format->lrule[PRINT_RULE_HEADER];
  
  	unsigned int col_count = 0,
  				cell_count = 0;
***************
*** 522,529 ****
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 2)
! 		opt_border = 2;
  
  	if (cont->ncolumns > 0)
  	{
--- 694,701 ----
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 3)
! 		opt_border = 3;
  
  	if (cont->ncolumns > 0)
  	{
***************
*** 564,569 ****
--- 736,760 ----
  					nl_lines,
  					bytes_required;
  
+ 		/*
+ 		 * Search and replace pipe character '|' in headers. Headers are
+ 		 * possible a constant strings, so we have to copy content.
+ 		 */
+ 		if (cont->opt->multiline_header)
+ 		{
+ 			const char	*header = cont->headers[i];
+ 			char		c;
+ 			char 	   *subst_header = pg_malloc(strlen(header));
+ 			char	   *cp;
+ 			
+ 			cp = subst_header;
+ 			while ((c = *header++) != '\0')
+ 				*cp++ = (c != '|') ? c : '\n';
+ 			*cp = '\0';
+ 			
+ 			cont->headers[i] = subst_header;
+ 		}
+ 
  		pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
  				   encoding, &width, &nl_lines, &bytes_required);
  		if (width > max_width[i])
***************
*** 797,803 ****
  			int			more_col_wrapping;
  			int			curr_nl_line;
  
! 			if (opt_border == 2)
  				_print_horizontal_line(col_count, width_wrap, opt_border,
  									   PRINT_RULE_TOP, format, fout);
  
--- 988,994 ----
  			int			more_col_wrapping;
  			int			curr_nl_line;
  
! 			if (opt_border == 2 || opt_border == 3)
  				_print_horizontal_line(col_count, width_wrap, opt_border,
  									   PRINT_RULE_TOP, format, fout);
  
***************
*** 811,818 ****
  			memset(header_done, false, col_count * sizeof(bool));
  			while (more_col_wrapping)
  			{
! 				if (opt_border == 2)
! 					fputs(dformat->leftvrule, fout);
  
  				for (i = 0; i < cont->ncolumns; i++)
  				{
--- 1002,1009 ----
  			memset(header_done, false, col_count * sizeof(bool));
  			while (more_col_wrapping)
  			{
! 				if (opt_border == 2 || opt_border == 3)
! 					fputs(hformat->leftvrule, fout);
  
  				for (i = 0; i < cont->ncolumns; i++)
  				{
***************
*** 827,836 ****
  					if (!header_done[i])
  					{
  						nbspace = width_wrap[i] - this_line->width;
! 
! 						/* centered */
! 						fprintf(fout, "%-*s%s%-*s",
! 								nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
  
  						if (!(this_line + 1)->ptr)
  						{
--- 1018,1036 ----
  					if (!header_done[i])
  					{
  						nbspace = width_wrap[i] - this_line->width;
! 						
! 						if (cont->opt->header_style == PRINT_HEADER_CENTER)
! 							/* centered */
! 							fprintf(fout, "%-*s%s%-*s",
! 									nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
! 						else if (cont->opt->header_style == PRINT_HEADER_LEFT)
! 							/* left aligned */
! 							fprintf(fout, "%s%-*s",
! 									this_line->ptr, nbspace, "");
! 						else if (cont->opt->header_style == PRINT_HEADER_RIGHT)
! 							/* right aligned */
! 							fprintf(fout, "%-*s%s",
! 									nbspace, "", this_line->ptr);
  
  						if (!(this_line + 1)->ptr)
  						{
***************
*** 842,857 ****
  						fprintf(fout, "%*s", width_wrap[i], "");
  
  					if (opt_border != 0 || format->wrap_right_border == true)
! 						fputs(!header_done[i] ? format->header_nl_right : " ",
! 							  fout);
  
  					if (opt_border != 0 && i < col_count - 1)
! 						fputs(dformat->midvrule, fout);
  				}
  				curr_nl_line++;
  
! 				if (opt_border == 2)
! 					fputs(dformat->rightvrule, fout);
  				fputc('\n', fout);
  			}
  
--- 1042,1062 ----
  						fprintf(fout, "%*s", width_wrap[i], "");
  
  					if (opt_border != 0 || format->wrap_right_border == true)
! 					{
! 						if (cont->opt->multiline_header)
! 							/* don't show wrap marks in multiline headre mode */
! 							fputs(" ", fout);
! 						else
! 							fputs(!header_done[i] ? format->header_nl_right : " ", fout);
! 					}
  
  					if (opt_border != 0 && i < col_count - 1)
! 						fputs(hformat->midvrule, fout);
  				}
  				curr_nl_line++;
  
! 				if (opt_border > 1)
! 					fputs(hformat->rightvrule, fout);
  				fputc('\n', fout);
  			}
  
***************
*** 868,873 ****
--- 1073,1083 ----
  		if (cancel_pressed)
  			break;
  
+ 		if (opt_border == 3 && i > 1)
+ 			_print_horizontal_line(col_count, width_wrap, opt_border,
+ 							   PRINT_RULE_BETWEEN_DATA, format, fout);
+ 
+ 
  		/*
  		 * Format each cell.  Format again, if it's a numeric formatting
  		 * locale (e.g. 123,456 vs. 123456)
***************
*** 901,907 ****
  			more_lines = false;
  
  			/* left border */
! 			if (opt_border == 2)
  				fputs(dformat->leftvrule, fout);
  
  			/* for each column */
--- 1111,1117 ----
  			more_lines = false;
  
  			/* left border */
! 			if (opt_border > 1)
  				fputs(dformat->leftvrule, fout);
  
  			/* for each column */
***************
*** 911,917 ****
  				struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
  				int			bytes_to_output;
  				int			chars_to_output = width_wrap[j];
! 				bool		finalspaces = (opt_border == 2 || j < col_count - 1);
  
  				/* Print left-hand wrap or newline mark */
  				if (opt_border != 0)
--- 1121,1127 ----
  				struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
  				int			bytes_to_output;
  				int			chars_to_output = width_wrap[j];
! 				bool		finalspaces = (opt_border > 1 || j < col_count - 1);
  
  				/* Print left-hand wrap or newline mark */
  				if (opt_border != 0)
***************
*** 932,941 ****
  				}
  				else
  				{
  					/* Get strlen() of the characters up to width_wrap */
  					bytes_to_output =
  						strlen_max_width(this_line->ptr + bytes_output[j],
! 										 &chars_to_output, encoding);
  
  					/*
  					 * If we exceeded width_wrap, it means the display width
--- 1142,1156 ----
  				}
  				else
  				{
+ 					bool	word_overflow;
+ 					
  					/* Get strlen() of the characters up to width_wrap */
  					bytes_to_output =
  						strlen_max_width(this_line->ptr + bytes_output[j],
! 										 &chars_to_output, 
! 										 encoding,
! 										 cont->opt->textwrapping,
! 										 &word_overflow);
  
  					/*
  					 * If we exceeded width_wrap, it means the display width
***************
*** 961,966 ****
--- 1176,1184 ----
  					}
  
  					bytes_output[j] += bytes_to_output;
+ 					/* if we break test in space, then skip space */
+ 					if (!word_overflow)
+ 						bytes_output[j] += 1;
  
  					/* Do we have more text to wrap? */
  					if (*(this_line->ptr + bytes_output[j]) != '\0')
***************
*** 1003,1009 ****
  					fputs(format->wrap_right, fout);
  				else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
  					fputs(format->nl_right, fout);
! 				else if (opt_border == 2 || j < col_count - 1)
  					fputc(' ', fout);
  
  				/* Print column divider, if not the last column */
--- 1221,1227 ----
  					fputs(format->wrap_right, fout);
  				else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
  					fputs(format->nl_right, fout);
! 				else if (opt_border > 1 || j < col_count - 1)
  					fputc(' ', fout);
  
  				/* Print column divider, if not the last column */
***************
*** 1021,1027 ****
  			}
  
  			/* end-of-row border */
! 			if (opt_border == 2)
  				fputs(dformat->rightvrule, fout);
  			fputc('\n', fout);
  
--- 1239,1245 ----
  			}
  
  			/* end-of-row border */
! 			if (opt_border > 1)
  				fputs(dformat->rightvrule, fout);
  			fputc('\n', fout);
  
***************
*** 1030,1036 ****
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border == 2 && !cancel_pressed)
  			_print_horizontal_line(col_count, width_wrap, opt_border,
  								   PRINT_RULE_BOTTOM, format, fout);
  
--- 1248,1254 ----
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border > 1 && !cancel_pressed)
  			_print_horizontal_line(col_count, width_wrap, opt_border,
  								   PRINT_RULE_BOTTOM, format, fout);
  
***************
*** 1058,1064 ****
--- 1276,1286 ----
  	free(header_done);
  	free(bytes_output);
  	for (i = 0; i < col_count; i++)
+ 	{
  		free(format_buf[i]);
+ 		if (cont->opt->multiline_header)
+ 			free((char *) cont->headers[i]);
+ 	}
  	free(format_buf);
  
  	if (is_pager)
***************
*** 1080,1086 ****
  	unsigned int	i;
  	int		reclen = 0;
  
! 	if (opt_border == 2)
  		fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
  	else if (opt_border == 1)
  		fputs(lformat->hrule, fout);
--- 1302,1308 ----
  	unsigned int	i;
  	int		reclen = 0;
  
! 	if (opt_border > 1)
  		fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
  	else if (opt_border == 1)
  		fputs(lformat->hrule, fout);
***************
*** 1092,1098 ****
  		else
  			reclen = fprintf(fout, "[ RECORD %lu ]", record);
  	}
! 	if (opt_border != 2)
  		reclen++;
  	if (reclen < 0)
  		reclen = 0;
--- 1314,1320 ----
  		else
  			reclen = fprintf(fout, "[ RECORD %lu ]", record);
  	}
! 	if (opt_border != 2 && opt_border != 3)
  		reclen++;
  	if (reclen < 0)
  		reclen = 0;
***************
*** 1118,1124 ****
  		reclen = 0;
  	for (i = reclen; i < dwidth; i++)
  		fputs(opt_border > 0 ? lformat->hrule : " ", fout);
! 	if (opt_border == 2)
  		fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
  	fputc('\n', fout);
  }
--- 1340,1346 ----
  		reclen = 0;
  	for (i = reclen; i < dwidth; i++)
  		fputs(opt_border > 0 ? lformat->hrule : " ", fout);
! 	if (opt_border > 1)
  		fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
  	fputc('\n', fout);
  }
***************
*** 1147,1154 ****
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 2)
! 		opt_border = 2;
  
  	if (cont->cells[0] == NULL && cont->opt->start_table &&
  		cont->opt->stop_table)
--- 1369,1376 ----
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 3)
! 		opt_border = 3;
  
  	if (cont->cells[0] == NULL && cont->opt->start_table &&
  		cont->opt->stop_table)
***************
*** 1238,1244 ****
  			if (!opt_tuples_only)
  				print_aligned_vertical_line(cont, record++, hwidth, dwidth,
  											pos, fout);
! 			else if (i != 0 || !cont->opt->start_table || opt_border == 2)
  				print_aligned_vertical_line(cont, 0, hwidth, dwidth,
  											pos, fout);
  		}
--- 1460,1466 ----
  			if (!opt_tuples_only)
  				print_aligned_vertical_line(cont, record++, hwidth, dwidth,
  											pos, fout);
! 			else if (i != 0 || !cont->opt->start_table || opt_border > 1)
  				print_aligned_vertical_line(cont, 0, hwidth, dwidth,
  											pos, fout);
  		}
***************
*** 1255,1261 ****
  		dcomplete = hcomplete = 0;
  		while (!dcomplete || !hcomplete)
  		{
! 			if (opt_border == 2)
  				fprintf(fout, "%s ", dformat->leftvrule);
  			if (!hcomplete)
  			{
--- 1477,1483 ----
  		dcomplete = hcomplete = 0;
  		while (!dcomplete || !hcomplete)
  		{
! 			if (opt_border > 1)
  				fprintf(fout, "%s ", dformat->leftvrule);
  			if (!hcomplete)
  			{
***************
*** 1313,1319 ****
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border == 2 && !cancel_pressed)
  			print_aligned_vertical_line(cont, 0, hwidth, dwidth,
  										PRINT_RULE_BOTTOM, fout);
  
--- 1535,1541 ----
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border > 1 && !cancel_pressed)
  			print_aligned_vertical_line(cont, 0, hwidth, dwidth,
  										PRINT_RULE_BOTTOM, fout);
  
***************
*** 1623,1630 ****
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 2)
! 		opt_border = 2;
  
  	if (cont->opt->start_table)
  	{
--- 1845,1852 ----
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 3)
! 		opt_border = 3;
  
  	if (cont->opt->start_table)
  	{
***************
*** 1639,1645 ****
  		/* begin environment and set alignments and borders */
  		fputs("\\begin{tabular}{", fout);
  
! 		if (opt_border == 2)
  			fputs("| ", fout);
  		for (i = 0; i < cont->ncolumns; i++)
  		{
--- 1861,1867 ----
  		/* begin environment and set alignments and borders */
  		fputs("\\begin{tabular}{", fout);
  
! 		if (opt_border > 1)
  			fputs("| ", fout);
  		for (i = 0; i < cont->ncolumns; i++)
  		{
***************
*** 1647,1658 ****
  			if (opt_border != 0 && i < cont->ncolumns - 1)
  				fputs(" | ", fout);
  		}
! 		if (opt_border == 2)
  			fputs(" |", fout);
  
  		fputs("}\n", fout);
  
! 		if (!opt_tuples_only && opt_border == 2)
  			fputs("\\hline\n", fout);
  
  		/* print headers */
--- 1869,1880 ----
  			if (opt_border != 0 && i < cont->ncolumns - 1)
  				fputs(" | ", fout);
  		}
! 		if (opt_border > 1)
  			fputs(" |", fout);
  
  		fputs("}\n", fout);
  
! 		if (!opt_tuples_only && opt_border > 1)
  			fputs("\\hline\n", fout);
  
  		/* print headers */
***************
*** 1696,1702 ****
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border == 2)
  			fputs("\\hline\n", fout);
  
  		fputs("\\end{tabular}\n\n\\noindent ", fout);
--- 1918,1924 ----
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border > 1)
  			fputs("\\hline\n", fout);
  
  		fputs("\\end{tabular}\n\n\\noindent ", fout);
***************
*** 1750,1756 ****
  			fputs("cl", fout);
  		else if (opt_border == 1)
  			fputs("c|l", fout);
! 		else if (opt_border == 2)
  			fputs("|c|l|", fout);
  		fputs("}\n", fout);
  	}
--- 1972,1978 ----
  			fputs("cl", fout);
  		else if (opt_border == 1)
  			fputs("c|l", fout);
! 		else if (opt_border > 1)
  			fputs("|c|l|", fout);
  		fputs("}\n", fout);
  	}
***************
*** 1765,1771 ****
  				break;
  			if (!opt_tuples_only)
  			{
! 				if (opt_border == 2)
  				{
  					fputs("\\hline\n", fout);
  					fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
--- 1987,1993 ----
  				break;
  			if (!opt_tuples_only)
  			{
! 				if (opt_border > 1)
  				{
  					fputs("\\hline\n", fout);
  					fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
***************
*** 1785,1791 ****
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border == 2)
  			fputs("\\hline\n", fout);
  
  		fputs("\\end{tabular}\n\n\\noindent ", fout);
--- 2007,2013 ----
  
  	if (cont->opt->stop_table)
  	{
! 		if (opt_border > 1)
  			fputs("\\hline\n", fout);
  
  		fputs("\\end{tabular}\n\n\\noindent ", fout);
***************
*** 1849,1856 ****
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 2)
! 		opt_border = 2;
  
  	if (cont->opt->start_table)
  	{
--- 2071,2078 ----
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 3)
! 		opt_border = 3;
  
  	if (cont->opt->start_table)
  	{
***************
*** 1864,1870 ****
  
  		/* begin environment and set alignments and borders */
  		fputs(".LP\n.TS\n", fout);
! 		if (opt_border == 2)
  			fputs("center box;\n", fout);
  		else
  			fputs("center;\n", fout);
--- 2086,2092 ----
  
  		/* begin environment and set alignments and borders */
  		fputs(".LP\n.TS\n", fout);
! 		if (opt_border > 1)
  			fputs("center box;\n", fout);
  		else
  			fputs("center;\n", fout);
***************
*** 1950,1957 ****
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 2)
! 		opt_border = 2;
  
  	if (cont->opt->start_table)
  	{
--- 2172,2179 ----
  	if (cancel_pressed)
  		return;
  
! 	if (opt_border > 3)
! 		opt_border = 3;
  
  	if (cont->opt->start_table)
  	{
***************
*** 1965,1971 ****
  
  		/* begin environment and set alignments and borders */
  		fputs(".LP\n.TS\n", fout);
! 		if (opt_border == 2)
  			fputs("center box;\n", fout);
  		else
  			fputs("center;\n", fout);
--- 2187,2193 ----
  
  		/* begin environment and set alignments and borders */
  		fputs(".LP\n.TS\n", fout);
! 		if (opt_border > 1)
  			fputs("center box;\n", fout);
  		else
  			fputs("center;\n", fout);
***************
*** 1989,1995 ****
  			{
  				if (current_format != 1)
  				{
! 					if (opt_border == 2 && record > 1)
  						fputs("_\n", fout);
  					if (current_format != 0)
  						fputs(".T&\n", fout);
--- 2211,2217 ----
  			{
  				if (current_format != 1)
  				{
! 					if (opt_border > 1 && record > 1)
  						fputs("_\n", fout);
  					if (current_format != 0)
  						fputs(".T&\n", fout);
***************
*** 2467,2472 ****
--- 2689,2701 ----
  			case CASHOID:
  				align = 'r';
  				break;
+ 			case TEXTOID:
+ 			case VARCHAROID:
+ 				if (opt->topt.textwrapping == TEXTWRAPPING_FLUSH_LEFT)
+ 					align = 'w';
+ 				else
+ 					align = 'l';
+ 				break;
  			default:
  				align = 'l';
  				break;
***************
*** 2561,2581 ****
  		return &pg_asciiformat;
  }
  
  /*
   * Compute the byte distance to the end of the string or *target_width
   * display character positions, whichever comes first.	Update *target_width
   * to be the number of display character positions actually filled.
   */
  static int
! strlen_max_width(unsigned char *str, int *target_width, int encoding)
  {
  	unsigned char *start = str;
  	unsigned char *end = str + strlen((char *) str);
  	int			curr_width = 0;
  
  	while (str < end)
  	{
! 		int			char_width = PQdsplen((char *) str, encoding);
  
  		/*
  		 * If the display width of the new character causes the string to
--- 2790,2843 ----
  		return &pg_asciiformat;
  }
  
+ const char *
+ get_header_style(printHeaderStyle style)
+ {
+ 	if (style == PRINT_HEADER_LEFT)
+ 		return "left";
+ 	else if (style == PRINT_HEADER_CENTER)
+ 		return "center";
+ 	else
+ 		return "right";
+ }
+ 
+ const char *
+ get_textwrapping_mode(printTextWrappingMode wmod)
+ {
+ 	if (wmod == TEXTWRAPPING_DEFAULT)
+ 		return "default";
+ 	else if (wmod == TEXTWRAPPING_FLUSH_LEFT)
+ 		return "flush-left";
+ 	else
+ 		return "unknown textwrapping mode";
+ }
+ 
  /*
   * Compute the byte distance to the end of the string or *target_width
   * display character positions, whichever comes first.	Update *target_width
   * to be the number of display character positions actually filled.
   */
  static int
! strlen_max_width(unsigned char *str, int *target_width, int encoding, 
! 						    printTextWrappingMode wmod, 
! 						    bool *word_overflow)
  {
  	unsigned char *start = str;
  	unsigned char *end = str + strlen((char *) str);
  	int			curr_width = 0;
+ 	int		last_space_width = 0;
+ 	unsigned char *last_space = NULL;
+ 	bool			overflow = false;
  
  	while (str < end)
  	{
! 		int	char_width = PQdsplen((char *) str, encoding);
! 		
! 		if (wmod == TEXTWRAPPING_FLUSH_LEFT && *str == ' ')
! 		{
! 			last_space = str;
! 			last_space_width = curr_width;
! 		}
  
  		/*
  		 * If the display width of the new character causes the string to
***************
*** 2584,2597 ****
  		 * accept it.
  		 */
  		if (*target_width < curr_width + char_width && curr_width != 0)
  			break;
  
  		curr_width += char_width;
- 
  		str += PQmblen((char *) str, encoding);
  	}
  
! 	*target_width = curr_width;
! 
  	return str - start;
  }
--- 2846,2872 ----
  		 * accept it.
  		 */
  		if (*target_width < curr_width + char_width && curr_width != 0)
+ 		{
+ 			overflow = true;
  			break;
+ 		}
  
  		curr_width += char_width;
  		str += PQmblen((char *) str, encoding);
  	}
  
! 	if (last_space != NULL && overflow)
! 	{
! 		
! 		*target_width = last_space_width;
! 		*word_overflow = false;
! 		str = last_space;
! 	}
! 	else
! 	{
! 		*target_width = curr_width;
! 		*word_overflow = true;
! 	}
! 	
  	return str - start;
  }
*** ./print.h.orig	2009-12-18 09:18:14.000000000 +0100
--- ./print.h	2009-12-30 09:38:29.560059344 +0100
***************
*** 38,44 ****
  	PRINT_RULE_TOP,				/* top horizontal line */
  	PRINT_RULE_MIDDLE,			/* intra-data horizontal line */
  	PRINT_RULE_BOTTOM,			/* bottom horizontal line */
! 	PRINT_RULE_DATA				/* data line (hrule is unused here) */
  } printTextRule;
  
  typedef enum printTextLineWrap
--- 38,46 ----
  	PRINT_RULE_TOP,				/* top horizontal line */
  	PRINT_RULE_MIDDLE,			/* intra-data horizontal line */
  	PRINT_RULE_BOTTOM,			/* bottom horizontal line */
! 	PRINT_RULE_DATA,			/* data line (hrule is unused here) */
! 	PRINT_RULE_BETWEEN_DATA,		/* line between data rows */
! 	PRINT_RULE_HEADER			/* header rule (hrule is unused here) */
  } printTextRule;
  
  typedef enum printTextLineWrap
***************
*** 49,59 ****
  	PRINT_LINE_WRAP_NEWLINE		/* Newline in data */
  } printTextLineWrap;
  
  typedef struct printTextFormat
  {
  	/* A complete line style */
  	const char *name;				/* for display purposes */
! 	printTextLineFormat lrule[4];	/* indexed by enum printTextRule */
  	const char *midvrule_nl;	/* vertical line for continue after newline */
  	const char *midvrule_wrap;	/* vertical line for wrapped data */
  	const char *midvrule_blank;	/* vertical line for blank data */
--- 51,67 ----
  	PRINT_LINE_WRAP_NEWLINE		/* Newline in data */
  } printTextLineWrap;
  
+ typedef enum printTextWrappingMode
+ {
+ 	TEXTWRAPPING_DEFAULT,		/* Default PostgreSQL behave */
+ 	TEXTWRAPPING_FLUSH_LEFT		/* Flush to left mode */
+ } printTextWrappingMode;
+ 
  typedef struct printTextFormat
  {
  	/* A complete line style */
  	const char *name;				/* for display purposes */
! 	printTextLineFormat lrule[6];	/* indexed by enum printTextRule */
  	const char *midvrule_nl;	/* vertical line for continue after newline */
  	const char *midvrule_wrap;	/* vertical line for wrapped data */
  	const char *midvrule_blank;	/* vertical line for blank data */
***************
*** 67,79 ****
  									 * when border=0? */
  } printTextFormat;
  
  typedef struct printTableOpt
  {
  	enum printFormat format;	/* see enum above */
  	bool		expanded;		/* expanded/vertical output (if supported by
  								 * output format) */
  	unsigned short int border;	/* Print a border around the table. 0=none,
! 								 * 1=dividing lines, 2=full */
  	unsigned short int pager;	/* use pager for output (if to stdout and
  								 * stdout is a tty) 0=off 1=on 2=always */
  	bool		tuples_only;	/* don't output headers, row counts, etc. */
--- 75,94 ----
  									 * when border=0? */
  } printTextFormat;
  
+ typedef enum printHeaderStyle
+ {
+ 	PRINT_HEADER_CENTER = 0,
+ 	PRINT_HEADER_LEFT,
+ 	PRINT_HEADER_RIGHT
+ } printHeaderStyle;
+ 
  typedef struct printTableOpt
  {
  	enum printFormat format;	/* see enum above */
  	bool		expanded;		/* expanded/vertical output (if supported by
  								 * output format) */
  	unsigned short int border;	/* Print a border around the table. 0=none,
! 								 * 1=dividing lines, 2=add border, 3=add between lines */
  	unsigned short int pager;	/* use pager for output (if to stdout and
  								 * stdout is a tty) 0=off 1=on 2=always */
  	bool		tuples_only;	/* don't output headers, row counts, etc. */
***************
*** 81,86 ****
--- 96,102 ----
  	bool		stop_table;		/* print stop decoration, eg </table> */
  	unsigned long prior_records;	/* start offset for record counters */
  	const printTextFormat *line_style;	/* line style (NULL for default) */
+ 	printTextWrappingMode textwrapping;	/* mode of text wrapping */
  	char	   *fieldSep;		/* field separator for unaligned text mode */
  	char	   *recordSep;		/* record separator for unaligned text mode */
  	bool		numericLocale;	/* locale-aware numeric units separator and
***************
*** 89,94 ****
--- 105,112 ----
  	int			encoding;		/* character encoding */
  	int			env_columns;	/* $COLUMNS on psql start, 0 is unset */
  	int			columns;		/* target width for wrapped format */
+ 	printHeaderStyle header_style;	/* header style */
+ 	bool		multiline_header;	/* allows multilines header */
  } printTableOpt;
  
  /*
***************
*** 121,127 ****
  	const char **cell;			/* Pointer to the last added cell */
  	printTableFooter *footers;	/* Pointer to the first footer */
  	printTableFooter *footer;	/* Pointer to the last added footer */
! 	char	   *aligns;			/* Array of alignment specifiers; 'l' or 'r',
  								 * one per column */
  	char	   *align;			/* Pointer to the last added alignment */
  } printTableContent;
--- 139,145 ----
  	const char **cell;			/* Pointer to the last added cell */
  	printTableFooter *footers;	/* Pointer to the first footer */
  	printTableFooter *footer;	/* Pointer to the last added footer */
! 	char	   *aligns;			/* Array of alignment specifiers; 'l', 'f' or 'r',
  								 * one per column */
  	char	   *align;			/* Pointer to the last added alignment */
  } printTableContent;
***************
*** 144,149 ****
--- 162,172 ----
  extern const printTextFormat pg_asciiformat_old;
  extern const printTextFormat pg_utf8format;
  
+ extern const printTextFormat pg_utf8_custom_single_format;
+ extern const printTextFormat pg_utf8_custom_double_border_format;
+ extern const printTextFormat pg_utf8_custom_double_border_header_format;
+ extern const printTextFormat pg_utf8_custom_double_border_header_columns_format;
+ extern const printTextFormat pg_utf8_custom_elegant_format;
  
  extern FILE *PageOutput(int lines, unsigned short int pager);
  extern void ClosePager(FILE *pagerpipe);
***************
*** 168,173 ****
--- 191,200 ----
  
  extern void setDecimalLocale(void);
  extern const printTextFormat *get_line_style(const printTableOpt *opt);
+ extern const char *get_textwrapping_mode(printTextWrappingMode wmod);
+ 
+ extern const char *get_header_style(printHeaderStyle style);
+ 
  
  #ifndef __CYGWIN__
  #define DEFAULT_PAGER "more"
*** ./psqlscan.l.orig	2009-11-12 01:13:00.000000000 +0100
--- ./psqlscan.l	2009-12-30 11:57:01.576058871 +0100
***************
*** 691,697 ****
  
  					value = GetVariable(pset.vars, yytext + 1);
  
! 					if (value)
  					{
  						/* It is a variable, perform substitution */
  						push_new_buffer(value);
--- 691,697 ----
  
  					value = GetVariable(pset.vars, yytext + 1);
  
! 					if (value  && !pset.skip_mode)
  					{
  						/* It is a variable, perform substitution */
  						push_new_buffer(value);
***************
*** 707,712 ****
--- 707,802 ----
  					}
  				}
  
+ :"{"[A-Za-z0-9_]+"}"	{
+ 					/* Possible psql literal quote variable substitution */
+ 					char *varname = pg_strdup(yytext + 2);
+ 					const char *value;
+ 					
+ 					varname[yyleng - 3] = '\0';
+ 					
+ 					value = GetVariable(pset.vars, varname);
+ 					free(varname);
+ 					
+ 					if (value  && !pset.skip_mode)
+ 					{
+ 						/* It is a variable, perform substitution */
+ 						const char *values[1];
+ 						PGresult *res;
+ 						
+ 						values[0] = value;
+ 						res = PQexecParams(pset.db,
+ 									    "SELECT quote_literal($1)",
+ 									    1,
+ 									    NULL,
+ 									    values,
+ 									    NULL,
+ 									    NULL,
+ 									    0);
+ 									    
+ 						if (PQresultStatus(res) == PGRES_TUPLES_OK)
+ 							push_new_buffer(PQgetvalue(res, 0, 0));
+ 						else
+ 							psql_error("cannot execute quote_ident function");
+ 						
+ 						PQclear(res);
+ 						/* yy_scan_string already made buffer active */
+ 					}
+ 					else
+ 					{
+ 						/*
+ 						 * if the variable doesn't exist we'll copy the
+ 						 * string as is
+ 						 */
+ 						ECHO;
+ 					}
+ 				}
+ 
+ :"["[A-Za-z0-9_]+"]"	{
+ 					/* Possible psql ident variable substitution */
+ 					char *varname = pg_strdup(yytext + 2);
+ 					const char *value;
+ 					
+ 					varname[yyleng - 3] = '\0';
+ 					
+ 					value = GetVariable(pset.vars, varname);
+ 					free(varname);
+ 					
+ 					if (value && !pset.skip_mode)
+ 					{
+ 						/* It is a variable, perform substitution */
+ 						const char *values[1];
+ 						PGresult *res;
+ 						
+ 						values[0] = value;
+ 						res = PQexecParams(pset.db,
+ 									    "SELECT quote_ident($1)",
+ 									    1,
+ 									    NULL,
+ 									    values,
+ 									    NULL,
+ 									    NULL,
+ 									    0);
+ 									    
+ 						if (PQresultStatus(res) == PGRES_TUPLES_OK)
+ 							push_new_buffer(PQgetvalue(res, 0, 0));
+ 						else
+ 							psql_error("cannot execute quote_ident function");
+ 						
+ 						PQclear(res);
+ 						/* yy_scan_string already made buffer active */
+ 					}
+ 					else
+ 					{
+ 						/*
+ 						 * if the variable doesn't exist we'll copy the
+ 						 * string as is
+ 						 */
+ 						ECHO;
+ 					}
+ 					
+ 			}
+ 
+ 
  	/*
  	 * Back to backend-compatible rules.
  	 */
***************
*** 996,1001 ****
--- 1086,1102 ----
  
  "`"				{ return LEXRES_OK; }
  
+ :[A-Za-z0-9_]*			{
+ 					const char *value;
+ 					
+ 					value = GetVariable(pset.vars, yytext + 1);
+ 					
+ 					if (value)
+ 						appendPQExpBufferStr(output_buf, value);
+ 					else
+ 						ECHO;
+ 					}
+ 
  {other}|\n		{ ECHO; }
  
  }
*** ./psqlscript.c.orig	2009-12-14 11:08:06.000000000 +0100
--- ./psqlscript.c	2009-12-31 09:29:29.081331753 +0100
***************
*** 0 ****
--- 1,188 ----
+ #include "postgres_fe.h"
+ #include "psqlscript.h"
+ #include "pqexpbuffer.h"
+ #include "command.h"
+ #include "common.h"
+ 
+ psqlNestedLoop *
+ remove_loop(psqlNestedLoop *nloop, ScriptStatementType stype, 
+ 				const char *cmd, backslashResult *status,
+ 				bool *skip_mode)
+ {
+ 	psqlNestedLoop *outer;
+ 	
+ 	/* 
+ 	 * without \set ON_ERROR_STOP is possible continue in
+ 	 * problematic sequences, so this function have to be
+ 	 * more than usually robust.
+ 	 */
+ 	if (nloop == NULL)
+ 	{
+ 		psql_error("\\%s: unexpected final statement\n", cmd);
+ 		*status = PSQL_CMD_ERROR;
+ 		return NULL;
+ 	}
+ 
+ 	
+ 	outer = nloop->outer;
+ 	
+ 	/* loop with zero cycles don't fill restored lines */
+ 	if (nloop->restored_lines != NULL)
+ 		free(nloop->restored_lines);
+ 
+ 	/* local buffer send to outer buffer */
+ 	if (nloop->lines != NULL)
+ 	{
+ 		if (outer && outer->writeable)
+ 			appendPQExpBuffer(outer->lines, "%s", nloop->lines->data);
+ 		
+ 		if (outer && outer->reader_pos)
+ 			outer->reader_pos += nloop->lines->len;
+ 			
+ 		destroyPQExpBuffer(nloop->lines);
+ 	}
+ 	
+ 	/* set original skip mode */
+ 	*skip_mode = nloop->outer_skip_mode;
+ 	
+ 	switch (nloop->typ)
+ 	{
+ 		case PSQL_FORC:
+ 			psql_assert(nloop->params.cursor_name != NULL);
+ 			free(nloop->params.cursor_name);
+ 			if (stype != PSQL_FORC)
+ 			{
+ 				psql_error("\\%s: expected \\endforc\n", cmd);
+ 				*status = PSQL_CMD_ERROR;
+ 			}
+ 			break;
+ 		
+ 		default:
+ 			psql_error("unknow block type\n");
+ 			*status = PSQL_CMD_ERROR;
+ 	}
+ 	free(nloop);
+ 	
+ 	return outer;
+ }
+ 
+ psqlNestedLoop *
+ new_for_cursor(char *cursor_name, psqlNestedLoop *outer, bool skip_mode)
+ {
+ 	psqlNestedLoop *forc = pg_malloc(sizeof(psqlNestedLoop));
+ 	
+ 	forc->typ = PSQL_FORC;
+ 	forc->lines = createPQExpBuffer();
+ 	forc->writeable = true;
+ 	forc->params.cursor_name = cursor_name;
+ 	forc->outer = outer;
+ 	forc->restored_lines = NULL;
+ 	forc->reader_pos = NULL;
+ 	
+ 	/* save skip mode */
+ 	forc->outer_skip_mode = skip_mode;
+ 	
+ 	/* propagete reader from outer loop */
+ 	if (outer)
+ 		forc->reader_pos = outer->reader_pos;
+ 
+ 	return forc;
+ }
+ 
+ /*
+  * define new statement - stack of statements controlls statement's 
+  * nesting (for syntax control) and carry stack of skip_modes.
+  */
+ psqlNestedStmt *
+ new_loop(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode)
+ {
+ 	psqlNestedStmt *stmt = pg_malloc(sizeof(psqlNestedStmt));
+ 	
+ 	stmt->typ = stype;
+ 	stmt->outer_skip_mode = skip_mode;
+ 	stmt->outer = outer;
+ 	
+ 	return stmt;
+ }
+ 
+ psqlNestedStmt *
+ new_if(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode)
+ {
+ 	psqlNestedIf *stmt = pg_malloc(sizeof(psqlNestedIf));
+ 	
+ 	stmt->typ = stype;
+ 	stmt->outer_skip_mode = skip_mode;
+ 	stmt->outer = outer;
+ 	stmt->successful = false;
+ 	
+ 	return (psqlNestedStmt*) stmt;
+ }
+ 
+ 
+ psqlNestedStmt *
+ remove_stmt(psqlNestedStmt *stmt, ScriptStatementType stype, 
+ 					const char *cmd, backslashResult *status,
+ 					bool *skip_mode)
+ {
+ 	psqlNestedStmt *outer;
+ 
+ 	/* 
+ 	 * without \set ON_ERROR_STOP is possible continue in
+ 	 * problematic sequences, so this function have to be
+ 	 * more than usually robust.
+ 	 */
+ 	if (stmt == NULL)
+ 	{
+ 		psql_error("\\%s: unexpected final statement\n", cmd);
+ 		*status = PSQL_CMD_ERROR;
+ 		return NULL;
+ 	}
+ 
+ 	outer = stmt->outer;
+ 	*skip_mode = stmt->outer_skip_mode;
+ 	
+ 	if (stmt->typ != stype)
+ 	{
+ 		free(stmt);
+ 		psql_error("\\%s: unexpected final statement\n", cmd);
+ 		*status = PSQL_CMD_ERROR;
+ 		return outer;
+ 	}
+ 	
+ 	free(stmt);
+ 	
+ 	return outer;
+ }
+ 
+ psqlNestedStmt *
+ new_newcommand(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode)
+ {
+ 	psqlNestedStmt *stmt = pg_malloc(sizeof(psqlNestedStmt));
+ 	
+ 	stmt->typ = stype;
+ 	stmt->outer_skip_mode = skip_mode;
+ 	stmt->outer = outer;
+ 	
+ 	return stmt;
+ }
+ 
+ psqlCustomStatement *
+ new_custom_statement(char *name, psqlCustomStatement *custom_statements, backslashResult *status)
+ {
+ 	psqlCustomStatement *cust_stmt;
+ 
+ 	/* don't allows nested definitions */
+ 	if (custom_statements != NULL && custom_statements->writer != NULL)
+ 	{
+ 		psql_error("nested definitions are not allowed\n");
+ 		*status = PSQL_CMD_ERROR;
+ 	}
+ 	
+ 	cust_stmt = pg_malloc(sizeof(psqlCustomStatement));
+ 	cust_stmt->name = name;
+ 	cust_stmt->next = custom_statements;
+ 	cust_stmt->src = NULL;
+ 	cust_stmt->writer = createPQExpBuffer();
+ 	
+ 	return cust_stmt;
+ }
*** ./psqlscript.h.orig	2009-12-14 10:26:19.000000000 +0100
--- ./psqlscript.h	2009-12-28 14:34:49.257788620 +0100
***************
*** 0 ****
--- 1,73 ----
+ 
+ #ifndef PSQLSCRIPT_H
+ #define PSQLSCRIPT_H
+ 
+ #include "pqexpbuffer.h"
+ #include "command.h"
+ 
+ typedef enum 
+ {
+ 	PSQL_FORC,
+ 	PSQL_IF,
+ 	PSQL_IFDEF,
+ 	PSQL_NEWCOMMAND
+ } ScriptStatementType;
+ 
+ typedef struct _psqlNestedStmt
+ {
+ 	ScriptStatementType	typ;
+ 	bool outer_skip_mode;		/* skip mode state before start of statement */
+ 	struct _psqlNestedStmt *outer;		/* pointer to outer block (if exists) */
+ } psqlNestedStmt;
+ 
+ typedef struct _psqlNestedIf
+ {
+ 	ScriptStatementType	typ;
+ 	bool outer_skip_mode;		/* skip mode state before start of statement */
+ 	struct _psqlNestedStmt *outer;		/* pointer to outer block (if exists) */
+ 	bool		successful;		/* true when any part is processed */
+ } psqlNestedIf;
+ 
+ typedef struct _psqlNestedLoop
+ {
+ 	ScriptStatementType	typ;
+ 	bool outer_skip_mode;		/* skip mode state before start of loop */
+ 	struct _psqlNestedLoop *outer;		/* pointer to outer block (if exists) */
+ 	PQExpBuffer	lines;
+ 	bool	writeable;
+ 	union
+ 	{
+ 		char	*cursor_name;
+ 	} params;
+ 	char *restored_lines;			/* begin of allocated space for restored lines */
+ 	char *reader_pos;		/* current position for reading in restored lines */
+ } psqlNestedLoop;
+ 
+ typedef struct _psqlCustomStatement
+ {
+ 	char	*name;
+ 	struct _psqlCustomStatement *next;
+ 	char *src;
+ 	PQExpBuffer	writer;
+ } psqlCustomStatement;
+ 
+ typedef struct _psqlCustomStmtReaderData
+ {
+ 	struct _psqlCustomStmtReaderData *outer;
+ 	char *reader;
+ 	char	*src;
+ } psqlCustomStmtReaderData;
+ 
+ extern psqlNestedLoop *new_for_cursor(char *cursor_name, psqlNestedLoop *outer, bool skip_mode);
+ extern psqlNestedLoop *remove_loop(psqlNestedLoop *nloop, ScriptStatementType stype, 
+ 				const char *cmd, backslashResult *status, bool *skip_mode);
+ 
+ extern psqlNestedStmt *new_loop(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode);
+ extern psqlNestedStmt *new_if(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode);
+ extern psqlNestedStmt *remove_stmt(psqlNestedStmt *stmt, ScriptStatementType stype, 
+ 				const char *cmd, backslashResult *status, bool *skip_mode);
+ extern psqlNestedStmt *new_newcommand(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode);
+ extern psqlCustomStatement *new_custom_statement(char *name, psqlCustomStatement *custom_statements, 
+ 											backslashResult *status);
+ 
+ #endif
*** ./settings.h.orig	2009-12-18 09:18:14.000000000 +0100
--- ./settings.h	2009-12-30 14:48:05.540059631 +0100
***************
*** 9,14 ****
--- 9,16 ----
  #define SETTINGS_H
  
  #include "libpq-fe.h"
+ #include "pqexpbuffer.h"
+ #include "psqlscript.h"
  
  #include "variables.h"
  #include "print.h"
***************
*** 111,116 ****
--- 113,127 ----
  	const char *prompt2;
  	const char *prompt3;
  	PGVerbosity verbosity;		/* current error verbosity level */
+ 	/* 
+ 	 * Support for construct
+ 	 */
+ 	psqlNestedLoop		*current_loop;		/* pointer to loop's stack */
+ 	psqlNestedStmt		*current_stmt;		/* pointer to meta controll's stack */
+ 	psqlCustomStatement	*custom_statements;	/* pointer to list of cust. statements */
+ 	psqlCustomStmtReaderData  *custom_statement_readbuffer;	/* used for expended custom statements */
+ 	bool	custom_stmt_mode;			/* execution of custom statement */
+ 	bool	skip_mode;
  } PsqlSettings;
  
  extern PsqlSettings pset;
*** ./startup.c.orig	2009-12-18 09:18:14.000000000 +0100
--- ./startup.c	2009-12-30 15:23:00.154059260 +0100
***************
*** 122,135 ****
--- 122,139 ----
  	pset.queryFoutPipe = false;
  	pset.cur_cmd_source = stdin;
  	pset.cur_cmd_interactive = false;
+ 	pset.custom_statements = NULL;
  
  	/* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
  	pset.popt.topt.format = PRINT_ALIGNED;
+ 	pset.popt.topt.textwrapping = TEXTWRAPPING_DEFAULT;
+ 	pset.popt.topt.header_style = PRINT_HEADER_CENTER;
  	pset.popt.topt.border = 1;
  	pset.popt.topt.pager = 1;
  	pset.popt.topt.start_table = true;
  	pset.popt.topt.stop_table = true;
  	pset.popt.default_footer = true;
+ 	pset.popt.topt.multiline_header = false;
  	/* We must get COLUMNS here before readline() sets it */
  	pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0;
  
*** ./tab-complete.c.orig	2009-12-11 04:34:56.000000000 +0100
--- ./tab-complete.c	2009-12-30 20:02:39.164184063 +0100
***************
*** 630,638 ****
  		"\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
  		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
  		"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
! 		"\\e", "\\echo", "\\ef", "\\encoding",
! 		"\\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
--- 630,639 ----
  		"\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
  		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
  		"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
! 		"\\e", "\\echo", "\\ef", "\\encoding", "\\endif", "\\endforc", "\\else", "\\elseif",
! 		"\\endifdef", "\\f", "\\forc", "\\g", "\\h", "\\help", "\\H", "\\i", "\\if", "\\ifdef",
! 		"\\newcommand", "\\endnewcommand",
! 		"\\l", "\\lf", "\\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
***************
*** 2265,2270 ****
--- 2266,2274 ----
  
  	else if (strcmp(prev_wd, "\\ef") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ 	
+ 	else if (strncmp(prev_wd, "\\lf", strlen("\\lf")) == 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);
***************
*** 2277,2283 ****
  		static const char *const my_list[] =
  		{"format", "border", "expanded",
  		 "null", "fieldsep", "tuples_only", "title", "tableattr",
! 		 "linestyle", "pager", "recordsep", NULL};
  
  		COMPLETE_WITH_LIST(my_list);
  	}
--- 2281,2288 ----
  		static const char *const my_list[] =
  		{"format", "border", "expanded",
  		 "null", "fieldsep", "tuples_only", "title", "tableattr",
! 		 "linestyle", "pager", "recordsep", "headerstyle", "multiline-header", 
! 		 "textwrapping", NULL};
  
  		COMPLETE_WITH_LIST(my_list);
  	}
