*** doc/src/sgml/libpq.sgml	2 Dec 2009 14:07:25 -0000	1.292
--- doc/src/sgml/libpq.sgml	14 Jan 2010 17:02:59 -0000
***************
*** 5786,5791 **** myEventProc(PGEventId evtId, void *evtIn
--- 5786,5803 ----
      <listitem>
       <para>
        <indexterm>
+        <primary><envar>PGSERVICEFILE</envar></primary>
+       </indexterm>
+       <envar>PGSERVICEFILE</envar> specifies the name of the per-user
+       connection service file.  If not set, it defaults
+       to <filename>~/.pg_service.conf</>
+       (see <xref linkend="libpq-pgservice">).
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       <indexterm>
         <primary><envar>PGREALM</envar></primary>
        </indexterm>
        <envar>PGREALM</envar> sets the Kerberos realm to use with
***************
*** 5979,5985 **** myEventProc(PGEventId evtId, void *evtIn
         <primary><envar>PGSYSCONFDIR</envar></primary>
        </indexterm>
        <envar>PGSYSCONFDIR</envar> sets the directory containing the
!       <filename>pg_service.conf</> file.
       </para>
      </listitem>
  
--- 5991,5998 ----
         <primary><envar>PGSYSCONFDIR</envar></primary>
        </indexterm>
        <envar>PGSYSCONFDIR</envar> sets the directory containing the
!       <filename>pg_service.conf</> file and in a future version
!       possibly other system-wide configuration files.
       </para>
      </listitem>
  
***************
*** 6055,6060 **** myEventProc(PGEventId evtId, void *evtIn
--- 6068,6076 ----
    <indexterm zone="libpq-pgservice">
     <primary>pg_service.conf</primary>
    </indexterm>
+   <indexterm zone="libpq-pgservice">
+    <primary>.pg_service.conf</primary>
+   </indexterm>
  
    <para>
     The connection service file allows libpq connection parameters to be
***************
*** 6066,6077 **** myEventProc(PGEventId evtId, void *evtIn
    </para>
  
    <para>
!    To use this feature, copy
!    <filename>share/pg_service.conf.sample</filename> to
!    <filename>etc/pg_service.conf</filename> and edit the file to add
!    service names and parameters. This file can be used for client-only
!    installs too. The file's location can also be specified by the
!    <envar>PGSYSCONFDIR</envar> environment variable.
    </para>
   </sect1>
  
--- 6082,6111 ----
    </para>
  
    <para>
!    The connection service file can be a per-user service file
!    at <filename>~/.pg_service.conf</filename> or the location
!    specified by the environment variable <envar>PGSERVICEFILE</envar>,
!    or it can be a system-wide file
!    at <filename>etc/pg_service.conf</filename> or in the directory
!    specified by the environment variable
!    <envar>PGSYSCONFDIR</envar>.  If service definitions with the same
!    name exist in the user and the system file, the user file takes
!    precedence.
!   </para>
! 
!   <para>
!    The file uses an <quote>INI file</quote> format where the section
!    name is the service name and the parameters are connection
!    parameters.  For example:
! <programlisting>
! # comment
! [mydb]
! host=somehost
! port=5433
! user=admin
! </programlisting>
!    An example file is provided at
!    <filename>share/pg_service.conf.sample</filename>.
    </para>
   </sect1>
  
*** src/interfaces/libpq/fe-connect.c	2 Jan 2010 16:58:11 -0000	1.382
--- src/interfaces/libpq/fe-connect.c	14 Jan 2010 17:02:59 -0000
***************
*** 269,274 **** static void defaultNoticeReceiver(void *
--- 269,279 ----
  static void defaultNoticeProcessor(void *arg, const char *message);
  static int parseServiceInfo(PQconninfoOption *options,
  				 PQExpBuffer errorMessage);
+ static int parseServiceFile(const char *serviceFile,
+ 							const char *service,
+ 							PQconninfoOption *options,
+ 							PQExpBuffer errorMessage,
+ 							bool *group_found);
  static char *pwdfMatchesString(char *buf, char *token);
  static char *PasswordFromFile(char *hostname, char *port, char *dbname,
  				 char *username);
***************
*** 3088,3096 **** parseServiceInfo(PQconninfoOption *optio
  {
  	char	   *service = conninfo_getval(options, "service");
  	char		serviceFile[MAXPGPATH];
  	bool		group_found = false;
! 	int			linenr = 0,
! 				i;
  
  	/*
  	 * We have to special-case the environment variable PGSERVICE here, since
--- 3093,3102 ----
  {
  	char	   *service = conninfo_getval(options, "service");
  	char		serviceFile[MAXPGPATH];
+ 	char	   *env;
  	bool		group_found = false;
! 	int			status;
! 	struct stat stat_buf;
  
  	/*
  	 * We have to special-case the environment variable PGSERVICE here, since
***************
*** 3100,3253 **** parseServiceInfo(PQconninfoOption *optio
  	if (service == NULL)
  		service = getenv("PGSERVICE");
  
  	/*
  	 * This could be used by any application so we can't use the binary
  	 * location to find our config files.
  	 */
  	snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
  			 getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
  
! 	if (service != NULL)
  	{
! 		FILE	   *f;
! 		char		buf[MAXBUFSIZE],
! 				   *line;
  
! 		f = fopen(serviceFile, "r");
! 		if (f == NULL)
  		{
! 			printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
  							  serviceFile);
! 			return 1;
  		}
  
! 		while ((line = fgets(buf, sizeof(buf), f)) != NULL)
! 		{
! 			linenr++;
! 
! 			if (strlen(line) >= sizeof(buf) - 1)
! 			{
! 				fclose(f);
! 				printfPQExpBuffer(errorMessage,
! 								  libpq_gettext("line %d too long in service file \"%s\"\n"),
! 								  linenr,
! 								  serviceFile);
! 				return 2;
! 			}
  
! 			/* ignore EOL at end of line */
! 			if (strlen(line) && line[strlen(line) - 1] == '\n')
! 				line[strlen(line) - 1] = 0;
! 
! 			/* ignore leading blanks */
! 			while (*line && isspace((unsigned char) line[0]))
! 				line++;
  
! 			/* ignore comments and empty lines */
! 			if (strlen(line) == 0 || line[0] == '#')
! 				continue;
  
! 			/* Check for right groupname */
! 			if (line[0] == '[')
  			{
! 				if (group_found)
! 				{
! 					/* group info already read */
! 					fclose(f);
! 					return 0;
! 				}
! 
! 				if (strncmp(line + 1, service, strlen(service)) == 0 &&
! 					line[strlen(service) + 1] == ']')
! 					group_found = true;
! 				else
! 					group_found = false;
  			}
  			else
  			{
! 				if (group_found)
! 				{
! 					/*
! 					 * Finally, we are in the right group and can parse the
! 					 * line
! 					 */
! 					char	   *key,
! 							   *val;
! 					bool		found_keyword;
  
  #ifdef USE_LDAP
! 					if (strncmp(line, "ldap", 4) == 0)
! 					{
! 						int			rc = ldapServiceLookup(line, options, errorMessage);
  
! 						/* if rc = 2, go on reading for fallback */
! 						switch (rc)
! 						{
! 							case 0:
! 								fclose(f);
! 								return 0;
! 							case 1:
! 							case 3:
! 								fclose(f);
! 								return 3;
! 							case 2:
! 								continue;
! 						}
  					}
  #endif
  
! 					key = line;
! 					val = strchr(line, '=');
! 					if (val == NULL)
! 					{
! 						printfPQExpBuffer(errorMessage,
! 										  libpq_gettext("syntax error in service file \"%s\", line %d\n"),
! 										  serviceFile,
! 										  linenr);
! 						fclose(f);
! 						return 3;
! 					}
! 					*val++ = '\0';
  
! 					/*
! 					 * Set the parameter --- but don't override any previous
! 					 * explicit setting.
! 					 */
! 					found_keyword = false;
! 					for (i = 0; options[i].keyword; i++)
  					{
! 						if (strcmp(options[i].keyword, key) == 0)
! 						{
! 							if (options[i].val == NULL)
! 								options[i].val = strdup(val);
! 							found_keyword = true;
! 							break;
! 						}
  					}
  
! 					if (!found_keyword)
! 					{
! 						printfPQExpBuffer(errorMessage,
! 										  libpq_gettext("syntax error in service file \"%s\", line %d\n"),
! 										  serviceFile,
! 										  linenr);
! 						fclose(f);
! 						return 3;
! 					}
  				}
  			}
  		}
- 
- 		fclose(f);
- 
- 		if (!group_found)
- 		{
- 			printfPQExpBuffer(errorMessage,
- 							  libpq_gettext("definition of service \"%s\" not found\n"), service);
- 			return 3;
- 		}
  	}
  
  	return 0;
  }
  
--- 3106,3301 ----
  	if (service == NULL)
  		service = getenv("PGSERVICE");
  
+ 	if (service == NULL)
+ 		return 0;
+ 
+ 	if ((env = getenv("PGSERVICEFILE")) != NULL)
+ 		strlcpy(serviceFile, env, sizeof(serviceFile));
+ 	else
+ 	{
+ 		char		homedir[MAXPGPATH];
+ 
+ 		if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+ 		{
+ 			printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file"));
+ 			return 1;
+ 		}
+ 		snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
+ 		errno = 0;
+ 		if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
+ 			goto next_file;
+ 	}
+ 
+ 	status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
+ 	if (group_found || status != 0)
+ 		return status;
+ 
+ next_file:
  	/*
  	 * This could be used by any application so we can't use the binary
  	 * location to find our config files.
  	 */
  	snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
  			 getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
+ 	errno = 0;
+ 	if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
+ 		goto last_file;
  
! 	status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
! 	if (status != 0)
! 		return status;
! 
! last_file:
! 	if (!group_found)
  	{
! 		printfPQExpBuffer(errorMessage,
! 						  libpq_gettext("definition of service \"%s\" not found\n"), service);
! 		return 3;
! 	}
  
! 	return 0;
! }
! 
! static int
! parseServiceFile(const char *serviceFile,
! 				 const char *service,
! 				 PQconninfoOption *options,
! 				 PQExpBuffer errorMessage,
! 				 bool *group_found)
! {	
! 	int			linenr = 0,
! 				i;
! 	FILE	   *f;
! 	char		buf[MAXBUFSIZE],
! 			   *line;
! 
! 	f = fopen(serviceFile, "r");
! 	if (f == NULL)
! 	{
! 		printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
! 						  serviceFile);
! 		return 1;
! 	}
! 
! 	while ((line = fgets(buf, sizeof(buf), f)) != NULL)
! 	{
! 		linenr++;
! 
! 		if (strlen(line) >= sizeof(buf) - 1)
  		{
! 			fclose(f);
! 			printfPQExpBuffer(errorMessage,
! 							  libpq_gettext("line %d too long in service file \"%s\"\n"),
! 							  linenr,
  							  serviceFile);
! 			return 2;
  		}
  
! 		/* ignore EOL at end of line */
! 		if (strlen(line) && line[strlen(line) - 1] == '\n')
! 			line[strlen(line) - 1] = 0;
  
! 		/* ignore leading blanks */
! 		while (*line && isspace((unsigned char) line[0]))
! 			line++;
  
! 		/* ignore comments and empty lines */
! 		if (strlen(line) == 0 || line[0] == '#')
! 			continue;
  
! 		/* Check for right groupname */
! 		if (line[0] == '[')
! 		{
! 			if (*group_found)
  			{
! 				/* group info already read */
! 				fclose(f);
! 				return 0;
  			}
+ 
+ 			if (strncmp(line + 1, service, strlen(service)) == 0 &&
+ 				line[strlen(service) + 1] == ']')
+ 				*group_found = true;
  			else
+ 				*group_found = false;
+ 		}
+ 		else
+ 		{
+ 			if (*group_found)
  			{
! 				/*
! 				 * Finally, we are in the right group and can parse
! 				 * the line
! 				 */
! 				char	   *key,
! 						   *val;
! 				bool		found_keyword;
  
  #ifdef USE_LDAP
! 				if (strncmp(line, "ldap", 4) == 0)
! 				{
! 					int			rc = ldapServiceLookup(line, options, errorMessage);
  
! 					/* if rc = 2, go on reading for fallback */
! 					switch (rc)
! 					{
! 						case 0:
! 							fclose(f);
! 							return 0;
! 						case 1:
! 						case 3:
! 							fclose(f);
! 							return 3;
! 						case 2:
! 							continue;
  					}
+ 				}
  #endif
  
! 				key = line;
! 				val = strchr(line, '=');
! 				if (val == NULL)
! 				{
! 					printfPQExpBuffer(errorMessage,
! 									  libpq_gettext("syntax error in service file \"%s\", line %d\n"),
! 									  serviceFile,
! 									  linenr);
! 					fclose(f);
! 					return 3;
! 				}
! 				*val++ = '\0';
  
! 				/*
! 				 * Set the parameter --- but don't override any previous
! 				 * explicit setting.
! 				 */
! 				found_keyword = false;
! 				for (i = 0; options[i].keyword; i++)
! 				{
! 					if (strcmp(options[i].keyword, key) == 0)
  					{
! 						if (options[i].val == NULL)
! 							options[i].val = strdup(val);
! 						found_keyword = true;
! 						break;
  					}
+ 				}
  
! 				if (!found_keyword)
! 				{
! 					printfPQExpBuffer(errorMessage,
! 									  libpq_gettext("syntax error in service file \"%s\", line %d\n"),
! 									  serviceFile,
! 									  linenr);
! 					fclose(f);
! 					return 3;
  				}
  			}
  		}
  	}
  
+ 	fclose(f);
+ 
  	return 0;
  }
  
