]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.9-20111125
authorWietse Venema <wietse@porcupine.org>
Fri, 25 Nov 2011 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:37:40 +0000 (06:37 +0000)
25 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/INSTALL
postfix/html/INSTALL.html
postfix/html/postconf.1.html
postfix/makedefs
postfix/man/man1/postconf.1
postfix/proto/INSTALL.html
postfix/src/global/mail_version.h
postfix/src/postconf/Makefile.in
postfix/src/postconf/postconf.c
postfix/src/postconf/postconf.h [new file with mode: 0644]
postfix/src/postconf/postconf_builtin.c [new file with mode: 0644]
postfix/src/postconf/postconf_edit.c [new file with mode: 0644]
postfix/src/postconf/postconf_main.c [new file with mode: 0644]
postfix/src/postconf/postconf_master.c [new file with mode: 0644]
postfix/src/postconf/postconf_misc.c [new file with mode: 0644]
postfix/src/postconf/postconf_node.c [new file with mode: 0644]
postfix/src/postconf/postconf_other.c [new file with mode: 0644]
postfix/src/postconf/postconf_service.c [new file with mode: 0644]
postfix/src/postconf/postconf_unused.c [new file with mode: 0644]
postfix/src/postconf/postconf_user.c [new file with mode: 0644]
postfix/src/postconf/test18.ref [new file with mode: 0644]
postfix/src/postconf/test19.ref [new file with mode: 0644]
postfix/src/postscreen/postscreen_smtpd.c

index 32e60db68fbd8f435d01d9915853b4551005c6eb..4756b026693759ddac974ef1ae37dd99670f7cce 100644 (file)
 -TNAME_MASK
 -TNBBIO
 -TPC_MASTER_ENT
+-TPC_PARAM_NODE
 -TPC_SERVICE_DEF
 -TPC_STRING_NV
 -TPEER_NAME
index f3057480b8c49d39459642f2b51b409cbd85f09c..f8523f3460d8ed919d047a5a0a36a79d4db6558b 100644 (file)
@@ -17133,6 +17133,27 @@ Apologies for any names omitted.
 
 20111122
 
-       Cleanup: documentation examples to request VERP-style
-       delivery at SMTP time with the smtpd_command_filter feature.
-       Files: proto/VERP_README.html, proto/postconf.proto.
+       Documentation: examples to request VERP-style delivery at
+       SMTP time with the smtpd_command_filter feature.  Files:
+       proto/VERP_README.html, proto/postconf.proto.
+
+       Documentation: complete list of "make makefiles" overrides.
+       File: proto/INSTALL.html.
+
+       Cleanup: postscreen now logs more than the first word of
+       non-SMTP commands. File: postscreen/postscreen_smtpd.c.
+
+20111124
+
+       Cleanup: eliminated false postconf "unused parameter"
+       warnings with legacy parameters such as $virtual_maps, and
+       with non-default parameter values for smtpd_expansion_filter
+       that can contain legitimate "$" without a macro name.
+
+       Cleanup: split postconf source into separate modules.
+       Files: postconf/postconf.c, postconf/postconf_builtin.c,
+       postconf/postconf_edit.c, postconf/postconf_main.c,
+       postconf/postconf_master.c, postconf/postconf_misc.c,
+       postconf/postconf_node.c, postconf/postconf_other.c,
+       postconf/postconf_service.c postconf/postconf_unused.c,
+       postconf/postconf_user.c, postconf/postconf.h.
index fde10940932f688daaae9b40995e7d861b0a8523..ae74b3145bc0e53b3076980ff8b4494d7a4865e9 100644 (file)
@@ -223,7 +223,81 @@ Parameters whose defaults can be specified in this way are:
 Note: the data_directory parameter (for caches and pseudo-random numbers) was
 introduced with Postfix version 2.5.
 
-4\b4.\b.5\b5 -\b- S\bSu\bup\bpp\bpo\bor\brt\bt f\bfo\bor\br t\bth\bho\bou\bus\bsa\ban\bnd\bds\bs o\bof\bf p\bpr\bro\boc\bce\bes\bss\bse\bes\bs
+4\b4.\b.5\b5 -\b- O\bOv\bve\ber\brr\bri\bid\bdi\bin\bng\bg o\bot\bth\bhe\ber\br c\bco\bom\bmp\bpi\bil\ble\be-\b-t\bti\bim\bme\be f\bfe\bea\bat\btu\bur\bre\bes\bs
+
+The general method to override Postfix compile-time features is as follows:
+
+    % make makefiles name=value name=value...
+    % make
+
+The following is an extensive list of names and values.
+
+ _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
+|N\bNa\bam\bme\be/\b/V\bVa\bal\blu\bue\be                    |D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn                                   |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|AUXLIBS="object_library..."   |Specifies one or more non-default object      |
+|                              |libraries.                                    |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|CC=compiler_command           |Specifies a non-default compiler. On many     |
+|                              |systems, the default is gcc.                  |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|                              |Specifies non-default compiler arguments, for |
+|CCARGS="compiler_arguments..."|example, a non-default include directory. The |
+|                              |following directives turn off Postfix features|
+|                              |at compile time:                              |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with Solaris /dev/poll support.  |
+||-DNO_DEVPOLL                 |By default, /dev/poll support is compiled in  |
+||                             |on Solaris versions that are known to support |
+||                             |this feature.                                 |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with Linux EPOLL support. By     |
+||-DNO_EPOLL                   |default, EPOLL support is compiled in on      |
+||                             |platforms that are known to support this      |
+||                             |feature.                                      |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with IPv6 support. By default,   |
+||                             |IPv6 support is compiled in on platforms that |
+||-DNO_IPV6                    |are known to have IPv6 support. Note: this    |
+||                             |directive is for debugging and testing only.  |
+||                             |It is not guaranteed to work on all platforms.|
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with FreeBSD / NetBSD / OpenBSD /|
+||-DNO_KQUEUE                  |MacOSX KQUEUE support. By default, KQUEUE     |
+||                             |support is compiled in on platforms that are  |
+||                             |known to support it.                          |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with NIS or NISPLUS support. NIS |
+||-DNO_NIS                     |is not available on some recent Linux or      |
+||                             |Solaris distributions.                        |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Do not build with PCRE support. By default,   |
+||-DNO_PCRE                    |PCRE support is compiled in when the pcre-    |
+||                             |config utility is installed.                  |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Disable support for POSIX getpwnam_r/         |
+||-DNO_POSIX_GETPW_R           |getpwuid_r. By default Postfix uses these     |
+||                             |where they are known to be available.         |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+||                             |Use setjmp()/longjmp() instead of sigsetjmp()/|
+||-DNO_SIGSETJMP               |siglongjmp(). By default, Postfix uses        |
+||                             |sigsetjmp()/siglongjmp() when they are known  |
+||                             |to be available.                              |
+|_\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|                              |Specifies a non-default compiler debugging    |
+|DEBUG=debug_level             |level. The default is -g. Specify DEBUG= to   |
+|                              |turn off debugging.                           |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|                              |Specifies a non-default optimization level.   |
+|OPT=optimization_level        |The default is -O. Specify OPT= to turn off   |
+|                              |optimization.                                 |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+|                              |Specifies non-default gcc compiler warning    |
+|WARN="warning_flags..."       |options for use when "make" is invoked in a   |
+|                              |source subdirectory only.                     |
+|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+
+4\b4.\b.6\b6 -\b- S\bSu\bup\bpp\bpo\bor\brt\bt f\bfo\bor\br t\bth\bho\bou\bus\bsa\ban\bnd\bds\bs o\bof\bf p\bpr\bro\boc\bce\bes\bss\bse\bes\bs
 
 The number of connections that Postfix can manage simultaneously is limited by
 the number of processes that it can run. This number in turn is limited by the
@@ -255,7 +329,7 @@ But wait, there is more: none of this will work unless the operating system is
 configured to handle thousands of connections. See the TUNING_README guide for
 examples of how to increase the number of open sockets or files.
 
-4\b4.\b.6\b6 -\b- C\bCo\bom\bmp\bpi\bil\bli\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx,\b, a\bat\bt l\bla\bas\bst\bt
+4\b4.\b.7\b7 -\b- C\bCo\bom\bmp\bpi\bil\bli\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx,\b, a\bat\bt l\bla\bas\bst\bt
 
 If the command
 
index c51abab17909553b00e090386dc01f5771570509..8df1259747168ceb9010ec9004670c8cf77212c9 100644 (file)
@@ -342,7 +342,89 @@ default</th> </tr>
 <p> Note: the <a href="postconf.5.html#data_directory">data_directory</a> parameter (for caches and pseudo-random
 numbers) was introduced with Postfix version 2.5. </p>
 
-<h3>4.5 - Support for thousands of processes</h3>
+<h3>4.5 - Overriding other compile-time features</h3>
+
+<p> The general method to override Postfix compile-time features
+is as follows: </p>
+
+<blockquote>
+<pre>
+% make makefiles name=value name=value...
+% make
+</pre>
+</blockquote>
+
+<p> The following is an extensive list of names and values. </p>
+
+<table border="1">
+
+<tr> <th colspan="2"> Name/Value </th> <th> Description </th> </tr>
+
+<tr> <td colspan="2"> AUXLIBS="object_library..."</td> <td> Specifies
+one or more non-default object libraries. </td> </tr>
+
+<tr> <td colspan="2"> CC=compiler_command</td> <td> Specifies a
+non-default compiler. On many systems, the default is <tt>gcc</tt>.
+</td> </tr>
+
+<tr> <td colspan="2"> CCARGS="compiler_arguments..."</td> <td>
+Specifies non-default compiler arguments, for example, a non-default
+<tt>include</tt> directory.  The following directives turn
+off Postfix features at compile time:</td> </tr>
+
+<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
+Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
+support is compiled in on Solaris versions that are known to support
+this feature.  </td> </tr>
+
+<tr> <td> </td> <td> -DNO_EPOLL </td> <td> Do not build with Linux
+EPOLL support.  By default, EPOLL support is compiled in on platforms
+that are known to support this feature. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_IPV6 </td> <td> Do not build with IPv6
+support. By default, IPv6 support is compiled in on platforms that
+are known to have IPv6 support. Note: this directive is for debugging
+and testing only. It is not guaranteed to work on all platforms.
+</td> </tr>
+
+<tr> <td> </td> <td> -DNO_KQUEUE </td> <td> Do not build with FreeBSD
+/ NetBSD / OpenBSD / MacOSX KQUEUE support. By default, KQUEUE
+support is compiled in on platforms that are known to support it.
+</td> </tr>
+
+<tr> <td> </td> <td> -DNO_NIS </td> <td> Do not build with NIS or
+NISPLUS support. NIS is not available on some recent Linux or Solaris
+distributions. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_PCRE </td> <td> Do not build with PCRE
+support. By default, PCRE support is compiled in when the
+<tt>pcre-config</tt> utility is installed. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_POSIX_GETPW_R </td> <td> Disable support
+for POSIX <tt>getpwnam_r/getpwuid_r</tt>. By default Postfix uses
+these where they are known to be available. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_SIGSETJMP </td> <td> Use
+<tt>setjmp()/longjmp()</tt> instead of <tt>sigsetjmp()/siglongjmp()</tt>.
+By default, Postfix uses <tt>sigsetjmp()/siglongjmp()</tt> when
+they are known to be available. </td> </tr>
+
+<tr> <td colspan="2"> DEBUG=debug_level </td> <td> Specifies a
+non-default compiler debugging level. The default is <tt>-g</tt>.
+Specify DEBUG= to turn off debugging. </td> </tr>
+
+<tr> <td colspan="2"> OPT=optimization_level </td> <td> Specifies
+a non-default optimization level. The default is -O.  Specify OPT=
+to turn off optimization. </td> </tr>
+
+<tr> <td colspan="2"> WARN="warning_flags..." </td> <td> Specifies
+non-default <tt>gcc</tt> compiler warning options for use when
+"<tt>make</tt>" is invoked in a source subdirectory only. </td>
+</tr>
+
+</table>
+
+<h3>4.6 - Support for thousands of processes</h3>
 
 <p> The number of connections that Postfix can manage simultaneously
 is limited by the number of processes that it can run.  This number
@@ -390,7 +472,7 @@ operating system is configured to handle thousands of connections.
 See the <a href="TUNING_README.html">TUNING_README</a> guide for examples of how to increase the
 number of open sockets or files. </p>
 
-<h3>4.6 - Compiling Postfix, at last</h3>
+<h3>4.7 - Compiling Postfix, at last</h3>
 
 <p> If the command </p>
 
index bbbb551d23f4854661da4ab208eb2aed61a0ff86..dcbd5e1779e1fc92a87112b50e8960d083167bb3 100644 (file)
@@ -71,8 +71,8 @@ POSTCONF(1)                                                        POSTCONF(1)
        <b>-b</b> [<i>template</i><b>_</b><i>file</i>]
               Display the message text that appears at the begin-
               ning  of  delivery  status  notification (DSN) mes-
-              sages, with $<b>name</b> expressions  replaced  by  actual
-              values as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
+              sages, replacing $<b>name</b> expressions with actual val-
+              ues as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
 
               To  override the built-in templates, specify a tem-
               plate file name at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a>  com-
@@ -100,7 +100,7 @@ POSTCONF(1)                                                        POSTCONF(1)
               the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. The file is copied to
               a temporary file then renamed into place.   Specify
               quotes to protect special characters and whitespace
-              on the f<a href="postconf.1.html">Bpostconf(1)</a> command line.
+              on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
               The <b>-e</b> is no longer needed with Postfix version 2.8
               and later.
index 02fd05dff98bd6d1cb90e353ee1420eff44d2266..329485f9d66fcafc7c765bd451ee255081ae1a30 100644 (file)
@@ -45,7 +45,8 @@
 #      By default, KQUEUE support is compiled in on platforms that
 #      are known to support it.
 # .IP \fB-DNO_NIS\fR
-#      Do not build with NIS or NISPLUS support.
+#      Do not build with NIS or NISPLUS support. Support for NIS
+#      is unavailable on some recent Linux and Solaris distributions.
 # .IP \fB-DNO_PCRE\fR
 #      Do not build with PCRE support.
 #      By default, PCRE support is compiled in when the \fBpcre-config\fR
index 13ced579f6bafeebc87ebd46ecf29f7dbbcd6892..57d2d061bdb7104258f3538f944400ec495eadbf 100644 (file)
@@ -73,8 +73,8 @@ Cyrus SASL support.
 This feature is available with Postfix 2.3 and later.
 .IP "\fB-b\fR [\fItemplate_file\fR]"
 Display the message text that appears at the beginning of
-delivery status notification (DSN) messages, with $\fBname\fR
-expressions replaced by actual values as described in
+delivery status notification (DSN) messages, replacing
+$\fBname\fR expressions with actual values as described in
 \fBbounce\fR(5).
 
 To override the built-in templates, specify a template file
@@ -101,7 +101,7 @@ parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
 on the \fBpostconf\fR(1) command line. The file is copied
 to a temporary file then renamed into place.
 Specify quotes to protect special characters and whitespace
-on the fBpostconf\fR(1) command line.
+on the \fBpostconf\fR(1) command line.
 
 The \fB-e\fR is no longer needed with Postfix version 2.8
 and later.
index 17b188babfb38d0873435da4b0a77ca055bd3825..bb436a3ae5bcf250ab2c4563d34d41caecb794bf 100644 (file)
@@ -342,7 +342,89 @@ default</th> </tr>
 <p> Note: the data_directory parameter (for caches and pseudo-random
 numbers) was introduced with Postfix version 2.5. </p>
 
-<h3>4.5 - Support for thousands of processes</h3>
+<h3>4.5 - Overriding other compile-time features</h3>
+
+<p> The general method to override Postfix compile-time features
+is as follows: </p>
+
+<blockquote>
+<pre>
+% make makefiles name=value name=value...
+% make
+</pre>
+</blockquote>
+
+<p> The following is an extensive list of names and values. </p>
+
+<table border="1">
+
+<tr> <th colspan="2"> Name/Value </th> <th> Description </th> </tr>
+
+<tr> <td colspan="2"> AUXLIBS="object_library..."</td> <td> Specifies
+one or more non-default object libraries. </td> </tr>
+
+<tr> <td colspan="2"> CC=compiler_command</td> <td> Specifies a
+non-default compiler. On many systems, the default is <tt>gcc</tt>.
+</td> </tr>
+
+<tr> <td colspan="2"> CCARGS="compiler_arguments..."</td> <td>
+Specifies non-default compiler arguments, for example, a non-default
+<tt>include</tt> directory.  The following directives turn
+off Postfix features at compile time:</td> </tr>
+
+<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
+Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
+support is compiled in on Solaris versions that are known to support
+this feature.  </td> </tr>
+
+<tr> <td> </td> <td> -DNO_EPOLL </td> <td> Do not build with Linux
+EPOLL support.  By default, EPOLL support is compiled in on platforms
+that are known to support this feature. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_IPV6 </td> <td> Do not build with IPv6
+support. By default, IPv6 support is compiled in on platforms that
+are known to have IPv6 support. Note: this directive is for debugging
+and testing only. It is not guaranteed to work on all platforms.
+</td> </tr>
+
+<tr> <td> </td> <td> -DNO_KQUEUE </td> <td> Do not build with FreeBSD
+/ NetBSD / OpenBSD / MacOSX KQUEUE support. By default, KQUEUE
+support is compiled in on platforms that are known to support it.
+</td> </tr>
+
+<tr> <td> </td> <td> -DNO_NIS </td> <td> Do not build with NIS or
+NISPLUS support. NIS is not available on some recent Linux or Solaris
+distributions. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_PCRE </td> <td> Do not build with PCRE
+support. By default, PCRE support is compiled in when the
+<tt>pcre-config</tt> utility is installed. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_POSIX_GETPW_R </td> <td> Disable support
+for POSIX <tt>getpwnam_r/getpwuid_r</tt>. By default Postfix uses
+these where they are known to be available. </td> </tr>
+
+<tr> <td> </td> <td> -DNO_SIGSETJMP </td> <td> Use
+<tt>setjmp()/longjmp()</tt> instead of <tt>sigsetjmp()/siglongjmp()</tt>.
+By default, Postfix uses <tt>sigsetjmp()/siglongjmp()</tt> when
+they are known to be available. </td> </tr>
+
+<tr> <td colspan="2"> DEBUG=debug_level </td> <td> Specifies a
+non-default compiler debugging level. The default is <tt>-g</tt>.
+Specify DEBUG= to turn off debugging. </td> </tr>
+
+<tr> <td colspan="2"> OPT=optimization_level </td> <td> Specifies
+a non-default optimization level. The default is -O.  Specify OPT=
+to turn off optimization. </td> </tr>
+
+<tr> <td colspan="2"> WARN="warning_flags..." </td> <td> Specifies
+non-default <tt>gcc</tt> compiler warning options for use when
+"<tt>make</tt>" is invoked in a source subdirectory only. </td>
+</tr>
+
+</table>
+
+<h3>4.6 - Support for thousands of processes</h3>
 
 <p> The number of connections that Postfix can manage simultaneously
 is limited by the number of processes that it can run.  This number
@@ -390,7 +472,7 @@ operating system is configured to handle thousands of connections.
 See the TUNING_README guide for examples of how to increase the
 number of open sockets or files. </p>
 
-<h3>4.6 - Compiling Postfix, at last</h3>
+<h3>4.7 - Compiling Postfix, at last</h3>
 
 <p> If the command </p>
 
index 3c38cc398a4597efde66cfd27ca523549ed28404..caa41ebfa19d2a895f4d72239e987f7812e1cc60 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20111122"
+#define MAIL_RELEASE_DATE      "20111125"
 #define MAIL_VERSION_NUMBER    "2.9"
 
 #ifdef SNAPSHOT
index 748d2e32b8810b92143676aba32bb47a21c795a3..a6a1b8249ae697fa42901ad2b4abfeba9c8944ad 100644 (file)
@@ -1,11 +1,15 @@
 SHELL  = /bin/sh
-SRCS   = postconf.c
-OBJS   = postconf.o
-HDRS   = 
-TESTSRC        = 
+SRCS   = postconf.c postconf_builtin.c postconf_edit.c postconf_main.c \
+       postconf_master.c postconf_misc.c postconf_node.c postconf_other.c \
+       postconf_service.c postconf_unused.c postconf_user.c
+OBJS   = postconf.o postconf_builtin.o postconf_edit.o postconf_main.o \
+       postconf_master.o postconf_misc.o postconf_node.o postconf_other.o \
+       postconf_service.o postconf_unused.o postconf_user.o
+HDRS   = postconf.h
+TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG= 
+TESTPROG=
 MAKES  = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \
        str_vars.h time_table.h time_vars.h raw_table.h raw_vars.h \
        nint_table.h nint_vars.h nbool_table.h nbool_vars.h long_table.h \
@@ -37,7 +41,7 @@ Makefile: Makefile.in
 test:  $(TESTPROG)
 
 tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
-       test12 test13 test14 test15 test16 test17
+       test12 test13 test14 test15 test16 test17 test18 test19
 
 root_tests:
 
@@ -168,7 +172,7 @@ test11:     $(PROG) test11.ref
        diff test11.ref test11.tmp
        rm -f main.cf master.cf test11.tmp
 
-# Duplicate service entry. 
+# Duplicate service entry.
 
 test12:        $(PROG) test12.ref
        rm -f main.cf master.cf
@@ -236,6 +240,28 @@ test17:    $(PROG) test17.ref
        diff test17.ref test17.tmp
        rm -f main.cf master.cf test17.tmp
 
+# Test legacy $name in built-in defaults.
+
+test18:        $(PROG) test18.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo virtual_maps=xxx >> main.cf
+       echo smtpd_client_connection_limit_exceptions=yyy >> main.cf
+       ./$(PROG) -nc . >test18.tmp 2>&1
+       diff test18.ref test18.tmp
+       rm -f main.cf master.cf test18.tmp
+
+# Test $name in "raw" parameters.
+
+test19:        $(PROG) test19.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo forward_path='$$'aaaa >> main.cf
+       echo default_rbl_reply='$$'bbbb >> main.cf
+       ./$(PROG) -nc . >test19.tmp 2>&1
+       diff test19.ref test19.tmp
+       rm -f main.cf master.cf test19.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -265,56 +291,181 @@ depend: $(MAKES)
 
 # do not edit below this line - it is generated by 'make depend'
 postconf.o: ../../include/argv.h
-postconf.o: ../../include/attr.h
 postconf.o: ../../include/dict.h
-postconf.o: ../../include/edit_file.h
-postconf.o: ../../include/get_hostname.h
 postconf.o: ../../include/htable.h
-postconf.o: ../../include/inet_proto.h
-postconf.o: ../../include/iostuff.h
-postconf.o: ../../include/mac_expand.h
-postconf.o: ../../include/mac_parse.h
-postconf.o: ../../include/mail_addr.h
 postconf.o: ../../include/mail_conf.h
 postconf.o: ../../include/mail_dict.h
 postconf.o: ../../include/mail_params.h
-postconf.o: ../../include/mail_proto.h
 postconf.o: ../../include/mail_run.h
 postconf.o: ../../include/mail_version.h
-postconf.o: ../../include/master_proto.h
-postconf.o: ../../include/match_service.h
-postconf.o: ../../include/mbox_conf.h
 postconf.o: ../../include/msg.h
 postconf.o: ../../include/msg_vstream.h
-postconf.o: ../../include/myflock.h
-postconf.o: ../../include/mymalloc.h
-postconf.o: ../../include/mynetworks.h
-postconf.o: ../../include/readlline.h
-postconf.o: ../../include/safe.h
-postconf.o: ../../include/split_at.h
 postconf.o: ../../include/stringops.h
 postconf.o: ../../include/sys_defs.h
 postconf.o: ../../include/vbuf.h
 postconf.o: ../../include/vstream.h
 postconf.o: ../../include/vstring.h
-postconf.o: ../../include/vstring_vstream.h
-postconf.o: ../../include/xsasl.h
-postconf.o: bool_table.h
-postconf.o: bool_vars.h
-postconf.o: install_table.h
-postconf.o: install_vars.h
-postconf.o: int_table.h
-postconf.o: int_vars.h
-postconf.o: long_table.h
-postconf.o: long_vars.h
-postconf.o: nbool_table.h
-postconf.o: nbool_vars.h
-postconf.o: nint_table.h
-postconf.o: nint_vars.h
 postconf.o: postconf.c
-postconf.o: raw_table.h
-postconf.o: raw_vars.h
-postconf.o: str_table.h
-postconf.o: str_vars.h
-postconf.o: time_table.h
-postconf.o: time_vars.h
+postconf.o: postconf.h
+postconf_builtin.o: ../../include/argv.h
+postconf_builtin.o: ../../include/attr.h
+postconf_builtin.o: ../../include/dict.h
+postconf_builtin.o: ../../include/get_hostname.h
+postconf_builtin.o: ../../include/htable.h
+postconf_builtin.o: ../../include/inet_proto.h
+postconf_builtin.o: ../../include/iostuff.h
+postconf_builtin.o: ../../include/mail_addr.h
+postconf_builtin.o: ../../include/mail_conf.h
+postconf_builtin.o: ../../include/mail_params.h
+postconf_builtin.o: ../../include/mail_proto.h
+postconf_builtin.o: ../../include/mail_version.h
+postconf_builtin.o: ../../include/msg.h
+postconf_builtin.o: ../../include/mymalloc.h
+postconf_builtin.o: ../../include/mynetworks.h
+postconf_builtin.o: ../../include/stringops.h
+postconf_builtin.o: ../../include/sys_defs.h
+postconf_builtin.o: ../../include/vbuf.h
+postconf_builtin.o: ../../include/vstream.h
+postconf_builtin.o: ../../include/vstring.h
+postconf_builtin.o: bool_table.h
+postconf_builtin.o: bool_vars.h
+postconf_builtin.o: install_table.h
+postconf_builtin.o: install_vars.h
+postconf_builtin.o: int_table.h
+postconf_builtin.o: int_vars.h
+postconf_builtin.o: long_table.h
+postconf_builtin.o: long_vars.h
+postconf_builtin.o: nbool_table.h
+postconf_builtin.o: nbool_vars.h
+postconf_builtin.o: nint_table.h
+postconf_builtin.o: nint_vars.h
+postconf_builtin.o: postconf.h
+postconf_builtin.o: postconf_builtin.c
+postconf_builtin.o: raw_table.h
+postconf_builtin.o: raw_vars.h
+postconf_builtin.o: str_table.h
+postconf_builtin.o: str_vars.h
+postconf_builtin.o: time_table.h
+postconf_builtin.o: time_vars.h
+postconf_edit.o: ../../include/argv.h
+postconf_edit.o: ../../include/dict.h
+postconf_edit.o: ../../include/edit_file.h
+postconf_edit.o: ../../include/htable.h
+postconf_edit.o: ../../include/mail_params.h
+postconf_edit.o: ../../include/msg.h
+postconf_edit.o: ../../include/mymalloc.h
+postconf_edit.o: ../../include/readlline.h
+postconf_edit.o: ../../include/stringops.h
+postconf_edit.o: ../../include/sys_defs.h
+postconf_edit.o: ../../include/vbuf.h
+postconf_edit.o: ../../include/vstream.h
+postconf_edit.o: ../../include/vstring.h
+postconf_edit.o: ../../include/vstring_vstream.h
+postconf_edit.o: postconf.h
+postconf_edit.o: postconf_edit.c
+postconf_main.o: ../../include/argv.h
+postconf_main.o: ../../include/dict.h
+postconf_main.o: ../../include/htable.h
+postconf_main.o: ../../include/mail_conf.h
+postconf_main.o: ../../include/mail_params.h
+postconf_main.o: ../../include/msg.h
+postconf_main.o: ../../include/mymalloc.h
+postconf_main.o: ../../include/readlline.h
+postconf_main.o: ../../include/stringops.h
+postconf_main.o: ../../include/sys_defs.h
+postconf_main.o: ../../include/vbuf.h
+postconf_main.o: ../../include/vstream.h
+postconf_main.o: ../../include/vstring.h
+postconf_main.o: postconf.h
+postconf_main.o: postconf_main.c
+postconf_master.o: ../../include/argv.h
+postconf_master.o: ../../include/dict.h
+postconf_master.o: ../../include/htable.h
+postconf_master.o: ../../include/mail_params.h
+postconf_master.o: ../../include/match_service.h
+postconf_master.o: ../../include/msg.h
+postconf_master.o: ../../include/mymalloc.h
+postconf_master.o: ../../include/readlline.h
+postconf_master.o: ../../include/stringops.h
+postconf_master.o: ../../include/sys_defs.h
+postconf_master.o: ../../include/vbuf.h
+postconf_master.o: ../../include/vstream.h
+postconf_master.o: ../../include/vstring.h
+postconf_master.o: postconf.h
+postconf_master.o: postconf_master.c
+postconf_misc.o: ../../include/argv.h
+postconf_misc.o: ../../include/dict.h
+postconf_misc.o: ../../include/htable.h
+postconf_misc.o: ../../include/mail_conf.h
+postconf_misc.o: ../../include/mail_params.h
+postconf_misc.o: ../../include/mymalloc.h
+postconf_misc.o: ../../include/safe.h
+postconf_misc.o: ../../include/sys_defs.h
+postconf_misc.o: ../../include/vbuf.h
+postconf_misc.o: ../../include/vstream.h
+postconf_misc.o: ../../include/vstring.h
+postconf_misc.o: postconf.h
+postconf_misc.o: postconf_misc.c
+postconf_node.o: ../../include/argv.h
+postconf_node.o: ../../include/dict.h
+postconf_node.o: ../../include/htable.h
+postconf_node.o: ../../include/msg.h
+postconf_node.o: ../../include/mymalloc.h
+postconf_node.o: ../../include/sys_defs.h
+postconf_node.o: ../../include/vbuf.h
+postconf_node.o: ../../include/vstream.h
+postconf_node.o: ../../include/vstring.h
+postconf_node.o: postconf.h
+postconf_node.o: postconf_node.c
+postconf_other.o: ../../include/argv.h
+postconf_other.o: ../../include/dict.h
+postconf_other.o: ../../include/htable.h
+postconf_other.o: ../../include/mbox_conf.h
+postconf_other.o: ../../include/sys_defs.h
+postconf_other.o: ../../include/vbuf.h
+postconf_other.o: ../../include/vstream.h
+postconf_other.o: ../../include/vstring.h
+postconf_other.o: ../../include/xsasl.h
+postconf_other.o: postconf.h
+postconf_other.o: postconf_other.c
+postconf_service.o: ../../include/argv.h
+postconf_service.o: ../../include/dict.h
+postconf_service.o: ../../include/htable.h
+postconf_service.o: ../../include/mail_params.h
+postconf_service.o: ../../include/msg.h
+postconf_service.o: ../../include/mymalloc.h
+postconf_service.o: ../../include/stringops.h
+postconf_service.o: ../../include/sys_defs.h
+postconf_service.o: ../../include/vbuf.h
+postconf_service.o: ../../include/vstream.h
+postconf_service.o: ../../include/vstring.h
+postconf_service.o: postconf.h
+postconf_service.o: postconf_service.c
+postconf_unused.o: ../../include/argv.h
+postconf_unused.o: ../../include/dict.h
+postconf_unused.o: ../../include/htable.h
+postconf_unused.o: ../../include/mail_conf.h
+postconf_unused.o: ../../include/mail_params.h
+postconf_unused.o: ../../include/msg.h
+postconf_unused.o: ../../include/sys_defs.h
+postconf_unused.o: ../../include/vbuf.h
+postconf_unused.o: ../../include/vstream.h
+postconf_unused.o: ../../include/vstring.h
+postconf_unused.o: postconf.h
+postconf_unused.o: postconf_unused.c
+postconf_user.o: ../../include/argv.h
+postconf_user.o: ../../include/dict.h
+postconf_user.o: ../../include/htable.h
+postconf_user.o: ../../include/mac_expand.h
+postconf_user.o: ../../include/mac_parse.h
+postconf_user.o: ../../include/mail_conf.h
+postconf_user.o: ../../include/mail_params.h
+postconf_user.o: ../../include/msg.h
+postconf_user.o: ../../include/mymalloc.h
+postconf_user.o: ../../include/stringops.h
+postconf_user.o: ../../include/sys_defs.h
+postconf_user.o: ../../include/vbuf.h
+postconf_user.o: ../../include/vstream.h
+postconf_user.o: ../../include/vstring.h
+postconf_user.o: postconf.h
+postconf_user.o: postconf_user.c
index 0af9d13fd7b4a14a8a6405535824d30547242f9e..10a273c15bdcc208d901ef4678b535af3922d093 100644 (file)
@@ -67,8 +67,8 @@
 /*     This feature is available with Postfix 2.3 and later.
 /* .IP "\fB-b\fR [\fItemplate_file\fR]"
 /*     Display the message text that appears at the beginning of
-/*     delivery status notification (DSN) messages, with $\fBname\fR
-/*     expressions replaced by actual values as described in
+/*     delivery status notification (DSN) messages, replacing
+/*     $\fBname\fR expressions with actual values as described in
 /*     \fBbounce\fR(5).
 /*
 /*     To override the built-in templates, specify a template file
@@ -93,9 +93,9 @@
 /*     Edit the \fBmain.cf\fR configuration file, and update
 /*     parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
 /*     on the \fBpostconf\fR(1) command line. The file is copied
-/*     to a temporary file then renamed into place. 
+/*     to a temporary file then renamed into place.
 /*     Specify quotes to protect special characters and whitespace
-/*     on the fBpostconf\fR(1) command line.
+/*     on the \fBpostconf\fR(1) command line.
 /*
 /*     The \fB-e\fR is no longer needed with Postfix version 2.8
 /*     and later.
 
 #include <sys_defs.h>
 #include <sys/stat.h>
-#include <stdio.h>                     /* rename() */
-#include <pwd.h>
-#include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#ifdef USE_PATHS_H
-#include <paths.h>
-#endif
 
 /* Utility library. */
 
 #include <msg.h>
-#include <vstream.h>
 #include <msg_vstream.h>
-#include <get_hostname.h>
-#include <stringops.h>
-#include <htable.h>
 #include <dict.h>
-#include <safe.h>
-#include <mymalloc.h>
-#include <argv.h>
-#include <split_at.h>
-#include <vstring_vstream.h>
-#include <myflock.h>
-#include <inet_proto.h>
-#include <argv.h>
-#include <edit_file.h>
-#include <readlline.h>
-#include <mac_expand.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
 
 /* Global library. */
 
-#include <mynetworks.h>
+#include <mail_params.h>
 #include <mail_conf.h>
-#include <mail_dict.h>
-#include <mail_proto.h>
 #include <mail_version.h>
-#include <mail_params.h>
-#include <mail_addr.h>
-#include <mbox_conf.h>
 #include <mail_run.h>
-#include <match_service.h>
-
-/* XSASL library. */
-
-#include <xsasl.h>
-
-/* master library. */
-
-#include <master_proto.h>
-
- /*
-  * What we're supposed to be doing.
-  */
-#define SHOW_NONDEF    (1<<0)          /* show main.cf non-default settings */
-#define SHOW_DEFS      (1<<1)          /* show main.cf default setting */
-#define SHOW_NAME      (1<<2)          /* show main.cf parameter name */
-#define SHOW_MAPS      (1<<3)          /* show map types */
-#define EDIT_MAIN      (1<<4)          /* edit main.cf */
-#define SHOW_LOCKS     (1<<5)          /* show mailbox lock methods */
-#define SHOW_EVAL      (1<<6)          /* expand main.cf right-hand sides */
-#define SHOW_SASL_SERV (1<<7)          /* show server auth plugin types */
-#define SHOW_SASL_CLNT (1<<8)          /* show client auth plugin types */
-#define COMMENT_OUT    (1<<9)          /* #-out selected main.cf entries */
-#define SHOW_MASTER    (1<<10)         /* show master.cf entries */
-#define FOLD_LINE      (1<<11)         /* fold long *.cf entries */
-
- /*
-  * Lookup table for global "valid" parameter information.
-  * 
-  * XXX Change the hash-table values from table indices to parameter object
-  * pointers, where each object supplies its own print etc. method.
-  */
-HTABLE *param_table;
-
- /*
-  * Global hash with all names in the global smtpd_restriction_classes value.
-  * This is used when validating "-o user-defined-name=value" in master.cf.
-  */
-HTABLE *rest_class_table;
-
- /*
-  * In-core information for one master.cf entry.
-  */
-typedef struct {
-    char   *name_space;                        /* service.type, parameter name space */
-    ARGV   *argv;                      /* null, or master.cf fields */
-    DICT   *all_params;                        /* null, or all name=value entries */
-    HTABLE *valid_names;               /* null, or "valid" parameter names */
-} PC_MASTER_ENT;
-
- /*
-  * Lookup table for master.cf entries. The table is terminated with an entry
-  * that has a null argv member.
-  */
-static PC_MASTER_ENT *master_table;
-
- /*
-  * Support for built-in parameters: declarations generated by scanning
-  * actual C source files.
-  */
-#include "time_vars.h"
-#include "bool_vars.h"
-#include "int_vars.h"
-#include "str_vars.h"
-#include "raw_vars.h"
-#include "nint_vars.h"
-#include "nbool_vars.h"
-#include "long_vars.h"
-
- /*
-  * Support for built-in parameters: manually extracted.
-  */
-#include "install_vars.h"
-
- /*
-  * Support for built-in parameters: lookup tables generated by scanning
-  * actual C source files.
-  */
-static const CONFIG_TIME_TABLE time_table[] = {
-#include "time_table.h"
-    0,
-};
-
-static const CONFIG_BOOL_TABLE bool_table[] = {
-#include "bool_table.h"
-    0,
-};
-
-static const CONFIG_INT_TABLE int_table[] = {
-#include "int_table.h"
-    0,
-};
-
-static const CONFIG_STR_TABLE str_table[] = {
-#include "str_table.h"
-#include "install_table.h"
-    0,
-};
-
-static const CONFIG_RAW_TABLE raw_table[] = {
-#include "raw_table.h"
-    0,
-};
-
-static const CONFIG_NINT_TABLE nint_table[] = {
-#include "nint_table.h"
-    0,
-};
-
-static const CONFIG_NBOOL_TABLE nbool_table[] = {
-#include "nbool_table.h"
-    0,
-};
-
-static const CONFIG_LONG_TABLE long_table[] = {
-#include "long_table.h"
-    0,
-};
-
- /*
-  * Ad-hoc name-value string pair.
-  */
-typedef struct {
-    const char *name;
-    const char *value;
-} PC_STRING_NV;
-
- /*
-  * Service-defined parameter names are created by appending postfix-defined
-  * suffixes to master.cf service names. All service-defined parameters have
-  * default values that are defined by built-in parameters. We represent
-  * service-defined parameters as (service-defined name, built-in default
-  * parameter name) tuples.
-  */
-static PC_STRING_NV *serv_param_table;
-static ssize_t serv_param_tablen;
-
- /*
-  * Support for user-defined parameters.
-  * 
-  * There are multiple parameter name spaces: the global main.cf parameter name
-  * space, and the local parameter name space of each master.cf entry. Local
-  * name spaces take precedence over the global one.
-  * 
-  * There are three categories of known parameters: built-in, service-defined
-  * (see previous comment), and valid user-defined.
-  * 
-  * There are two categories of valid user-defined parameters:
-  * 
-  * - Parameters whose user-defined-name appears in the value of
-  * smtpd_restriction_classes in main.cf or master.cf.
-  * 
-  * - Parameters whose $user-defined-name appear in the value of "name=value"
-  * entries in main.cf or master.cf.
-  * 
-  * - In both cases the parameters must have a "user-defined-name=value" entry
-  * in main.cf or master.cf.
-  * 
-  * Other user-defined parameters are flagged as "unused".
-  */
-
- /*
-  * Global-scope valid user-defined parameter names. The local-scope valid
-  * user-defined names are kept in the table with master.cf entries.
-  */
-static char **user_param_table;
-static ssize_t user_param_tablen;
-
- /*
-  * Parameters with default values obtained via function calls.
-  */
-char   *var_myhostname;
-char   *var_mydomain;
-char   *var_mynetworks;
+#include <mail_dict.h>
 
-static const char *check_myhostname(void);
-static const char *check_mydomainname(void);
-static const char *check_mynetworks(void);
+/* Application-specific. */
 
-static const CONFIG_STR_FN_TABLE str_fn_table[] = {
-    VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
-    VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
-    0,
-};
-static const CONFIG_STR_FN_TABLE str_fn_table_2[] = {
-    VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0,
-    0,
-};
+#include <postconf.h>
 
  /*
-  * Line-wrapping support.
+  * Global storage. See postconf.h for description.
   */
-#define LINE_LIMIT     80              /* try to fold longer lines */
-#define SEPARATORS     " \t\r\n"
-#define INDENT_LEN     4               /* indent long text by 4 */
-#define INDENT_TEXT    "    "
+PC_PARAM_TABLE *param_table;
+PC_MASTER_ENT *master_table;
+int     cmd_mode = DEF_MODE;
 
  /*
-  * XXX Global so that call-backs can see it.
+  * Application fingerprinting.
   */
-#define DEF_MODE       SHOW_NAME
-static int cmd_mode = DEF_MODE;
-
-/* set_config_dir - forcibly override var_config_dir */
-
-static void set_config_dir(void)
-{
-    char   *config_dir;
-
-    if (var_config_dir)
-       myfree(var_config_dir);
-    var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
-                             config_dir : DEF_CONFIG_DIR);     /* XXX */
-    set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
-}
-
-/* check_myhostname - lookup hostname and validate */
-
-static const char *check_myhostname(void)
-{
-    static const char *name;
-    const char *dot;
-    const char *domain;
-
-    /*
-     * Use cached result.
-     */
-    if (name)
-       return (name);
-
-    /*
-     * If the local machine name is not in FQDN form, try to append the
-     * contents of $mydomain.
-     */
-    name = get_hostname();
-    if ((dot = strchr(name, '.')) == 0) {
-       if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0)
-           domain = DEF_MYDOMAIN;
-       name = concatenate(name, ".", domain, (char *) 0);
-    }
-    return (name);
-}
-
-/* get_myhostname - look up and store my hostname */
-
-static void get_myhostname(void)
-{
-    const char *name;
-
-    if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0)
-       name = check_myhostname();
-    var_myhostname = mystrdup(name);
-}
-
-/* check_mydomainname - lookup domain name and validate */
-
-static const char *check_mydomainname(void)
-{
-    char   *dot;
-
-    /*
-     * Use the hostname when it is not a FQDN ("foo"), or when the hostname
-     * actually is a domain name ("foo.com").
-     */
-    if (var_myhostname == 0)
-       get_myhostname();
-    if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
-       return (DEF_MYDOMAIN);
-    return (dot + 1);
-}
-
-/* check_mynetworks - lookup network address list */
-
-static const char *check_mynetworks(void)
-{
-    INET_PROTO_INFO *proto_info;
-    const char *junk;
-
-    if (var_inet_interfaces == 0) {
-       if ((cmd_mode & SHOW_DEFS)
-           || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
-           junk = DEF_INET_INTERFACES;
-       var_inet_interfaces = mystrdup(junk);
-    }
-    if (var_mynetworks_style == 0) {
-       if ((cmd_mode & SHOW_DEFS)
-           || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
-           junk = DEF_MYNETWORKS_STYLE;
-       var_mynetworks_style = mystrdup(junk);
-    }
-    if (var_inet_protocols == 0) {
-       if ((cmd_mode & SHOW_DEFS)
-           || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
-           junk = DEF_INET_PROTOCOLS;
-       var_inet_protocols = mystrdup(junk);
-       proto_info = inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
-    }
-    return (mynetworks());
-}
-
-/* edit_parameters - edit parameter file */
-
-static void edit_parameters(int cmd_mode, int argc, char **argv)
-{
-    char   *path;
-    EDIT_FILE *ep;
-    VSTREAM *src;
-    VSTREAM *dst;
-    VSTRING *buf = vstring_alloc(100);
-    VSTRING *key = vstring_alloc(10);
-    char   *cp;
-    char   *edit_key;
-    char   *edit_val;
-    HTABLE *table;
-    struct cvalue {
-       char   *value;
-       int     found;
-    };
-    struct cvalue *cvalue;
-    HTABLE_INFO **ht_info;
-    HTABLE_INFO **ht;
-    int     interesting;
-    const char *err;
-
-    /*
-     * Store command-line parameters for quick lookup.
-     */
-    table = htable_create(argc);
-    while ((cp = *argv++) != 0) {
-       if (strchr(cp, '\n') != 0)
-           msg_fatal("-e or -# accepts no multi-line input");
-       while (ISSPACE(*cp))
-           cp++;
-       if (*cp == '#')
-           msg_fatal("-e or -# accepts no comment input");
-       if (cmd_mode & EDIT_MAIN) {
-           if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
-               msg_fatal("%s: \"%s\"", err, cp);
-       } else if (cmd_mode & COMMENT_OUT) {
-           if (*cp == 0)
-               msg_fatal("-# requires non-blank parameter names");
-           if (strchr(cp, '=') != 0)
-               msg_fatal("-# requires parameter names only");
-           edit_key = mystrdup(cp);
-           trimblanks(edit_key, 0);
-           edit_val = 0;
-       } else {
-           msg_panic("edit_parameters: unknown mode %d", cmd_mode);
-       }
-       cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
-       cvalue->value = edit_val;
-       cvalue->found = 0;
-       htable_enter(table, edit_key, (char *) cvalue);
-    }
-
-    /*
-     * Open a temp file for the result. This uses a deterministic name so we
-     * don't leave behind thrash with random names.
-     */
-    set_config_dir();
-    path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
-    if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
-       msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
-    dst = ep->tmp_fp;
-
-    /*
-     * Open the original file for input.
-     */
-    if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
-       /* OK to delete, since we control the temp file name exclusively. */
-       (void) unlink(ep->tmp_path);
-       msg_fatal("open %s for reading: %m", path);
-    }
-
-    /*
-     * Copy original file to temp file, while replacing parameters on the
-     * fly. Issue warnings for names found multiple times.
-     */
-#define STR(x) vstring_str(x)
-
-    interesting = 0;
-    while (vstring_get(buf, src) != VSTREAM_EOF) {
-       for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
-            /* void */ ;
-       /* Copy comment, all-whitespace, or empty line. */
-       if (*cp == '#' || *cp == 0) {
-           vstream_fputs(STR(buf), dst);
-       }
-       /* Copy, skip or replace continued text. */
-       else if (cp > STR(buf)) {
-           if (interesting == 0)
-               vstream_fputs(STR(buf), dst);
-           else if (cmd_mode & COMMENT_OUT)
-               vstream_fprintf(dst, "#%s", STR(buf));
-       }
-       /* Copy or replace start of logical line. */
-       else {
-           vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
-           cvalue = (struct cvalue *) htable_find(table, STR(key));
-           if ((interesting = !!cvalue) != 0) {
-               if (cvalue->found++ == 1)
-                   msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
-               if (cmd_mode & EDIT_MAIN)
-                   vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
-               else if (cmd_mode & COMMENT_OUT)
-                   vstream_fprintf(dst, "#%s", cp);
-               else
-                   msg_panic("edit_parameters: unknown mode %d", cmd_mode);
-           } else {
-               vstream_fputs(STR(buf), dst);
-           }
-       }
-    }
-
-    /*
-     * Generate new entries for parameters that were not found.
-     */
-    if (cmd_mode & EDIT_MAIN) {
-       for (ht_info = ht = htable_list(table); *ht; ht++) {
-           cvalue = (struct cvalue *) ht[0]->value;
-           if (cvalue->found == 0)
-               vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
-       }
-       myfree((char *) ht_info);
-    }
-
-    /*
-     * When all is well, rename the temp file to the original one.
-     */
-    if (vstream_fclose(src))
-       msg_fatal("read %s: %m", path);
-    if (edit_file_close(ep) != 0)
-       msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
-
-    /*
-     * Cleanup.
-     */
-    myfree(path);
-    vstring_free(buf);
-    vstring_free(key);
-    htable_free(table, myfree);
-}
-
-/* read_parameters - read parameter info from file */
-
-static void read_parameters(void)
-{
-    char   *path;
-
-    /*
-     * A direct rip-off of mail_conf_read(). XXX Avoid code duplication by
-     * better code decomposition.
-     */
-    dict_unknown_allowed = 1;
-    set_config_dir();
-    path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
-    dict_load_file(CONFIG_DICT, path);
-    myfree(path);
-}
-
-/* set_parameters - set parameter values from default or explicit setting */
-
-static void set_parameters(void)
-{
-
-    /*
-     * The proposal below describes some of the steps needed to expand
-     * parameter values. It has a problem: it updates the configuration
-     * parameter dictionary, and in doing so breaks the "postconf -d"
-     * implementation. This makes "-d" and "-e" mutually exclusive.
-     * 
-     * Populate the configuration parameter dictionary with default settings or
-     * with actual settings.
-     * 
-     * Iterate over each entry in str_fn_table, str_fn_table_2, time_table,
-     * bool_table, int_table, str_table, and raw_table. Look up each
-     * parameter name in the configuration parameter dictionary. If the
-     * parameter is not set, take the default value, or take the value from
-     * main.cf, without doing $name expansions. This includes converting
-     * default values from numeric/boolean internal forms to external string
-     * form.
-     * 
-     * Once the configuration parameter dictionary is populated, printing a
-     * parameter setting is a matter of querying the configuration parameter
-     * dictionary, optionally expanding of $name values, and printing the
-     * result.
-     */
-}
-
-/* read_master - read and digest the master.cf file */
-
-static void read_master(int fail_on_open_error)
-{
-    char   *path;
-    VSTRING *buf;
-    ARGV   *argv;
-    VSTREAM *fp;
-    int     entry_count = 0;
-    int     line_count = 0;
-
-    /*
-     * Get the location of master.cf.
-     */
-    if (var_config_dir == 0)
-       set_config_dir();
-    path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
-
-    /*
-     * We can't use the master daemon's master_ent routines in their current
-     * form. They convert everything to internal form, and they skip disabled
-     * services.
-     * 
-     * The postconf command needs to show default fields as "-", and needs to
-     * know about all service names so that it can generate service-dependent
-     * parameter names (transport-dependent etc.).
-     */
-#define MASTER_BLANKS  " \t\r\n"               /* XXX */
-#define MASTER_FIELD_COUNT 8                   /* XXX */
-
-    /*
-     * Initialize the in-memory master table.
-     */
-    master_table = (PC_MASTER_ENT *) mymalloc(sizeof(*master_table));
-
-    /*
-     * Skip blank lines and comment lines. Degrade gracefully if master.cf is
-     * not available, and master.cf is not the primary target.
-     */
-#define WARN_ON_OPEN_ERROR     0
-#define FAIL_ON_OPEN_ERROR     1
-
-    if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
-       if (fail_on_open_error)
-           msg_fatal("open %s: %m", path);
-       msg_warn("open %s: %m", path);
-    } else {
-       buf = vstring_alloc(100);
-       while (readlline(buf, fp, &line_count) != 0) {
-           master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table,
-                                (entry_count + 2) * sizeof(*master_table));
-           argv = argv_split(STR(buf), MASTER_BLANKS);
-           if (argv->argc < MASTER_FIELD_COUNT)
-               msg_fatal("file %s: line %d: bad field count",
-                         path, line_count);
-           master_table[entry_count].name_space =
-               concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0);
-           master_table[entry_count].argv = argv;
-           master_table[entry_count].valid_names = 0;
-           master_table[entry_count].all_params = 0;
-           entry_count += 1;
-       }
-       vstream_fclose(fp);
-       vstring_free(buf);
-    }
-
-    /*
-     * Null-terminate the master table and clean up.
-     */
-    master_table[entry_count].argv = 0;
-    myfree(path);
-}
-
- /*
-  * Basename of programs in $daemon_directory. XXX These belong in a header
-  * file, or they should be made configurable.
-  */
-#ifndef MAIL_PROGRAM_LOCAL
-#define MAIL_PROGRAM_LOCAL     "local"
-#define MAIL_PROGRAM_ERROR     "error"
-#define MAIL_PROGRAM_VIRTUAL   "virtual"
-#define MAIL_PROGRAM_SMTP      "smtp"
-#define MAIL_PROGRAM_LMTP      "lmtp"
-#define MAIL_PROGRAM_PIPE      "pipe"
-#define MAIL_PROGRAM_SPAWN     "spawn"
-#endif
-
-/* add_service_parameter - add one service parameter name and default */
-
-static void add_service_parameter(const char *service, const char *suffix,
-                                         const char *defparam)
-{
-    PC_STRING_NV *sp;
-    char   *name = concatenate(service, suffix, (char *) 0);
-
-    /*
-     * Skip service parameter names that have built-in definitions. This
-     * happens with message delivery transports that have a non-default
-     * per-destination concurrency or recipient limit, such as local(8).
-     */
-    if (htable_locate(param_table, name) != 0) {
-       myfree(name);
-    } else {
-       serv_param_table = (PC_STRING_NV *)
-           myrealloc((char *) serv_param_table,
-                     (serv_param_tablen + 1) * sizeof(*serv_param_table));
-       sp = serv_param_table + serv_param_tablen;
-       sp->name = name;
-       sp->value = defparam;
-       serv_param_tablen += 1;
-    }
-}
-
-/* add_service_parameters - add all service parameters with defaults */
-
-static void add_service_parameters(void)
-{
-    static const PC_STRING_NV service_params[] = {
-       /* suffix, default parameter name */
-       _XPORT_RCPT_LIMIT, VAR_XPORT_RCPT_LIMIT,
-       _STACK_RCPT_LIMIT, VAR_STACK_RCPT_LIMIT,
-       _XPORT_REFILL_LIMIT, VAR_XPORT_REFILL_LIMIT,
-       _XPORT_REFILL_DELAY, VAR_XPORT_REFILL_DELAY,
-       _DELIVERY_SLOT_COST, VAR_DELIVERY_SLOT_COST,
-       _DELIVERY_SLOT_LOAN, VAR_DELIVERY_SLOT_LOAN,
-       _DELIVERY_SLOT_DISCOUNT, VAR_DELIVERY_SLOT_DISCOUNT,
-       _MIN_DELIVERY_SLOTS, VAR_MIN_DELIVERY_SLOTS,
-       _INIT_DEST_CON, VAR_INIT_DEST_CON,
-       _DEST_CON_LIMIT, VAR_DEST_CON_LIMIT,
-       _DEST_RCPT_LIMIT, VAR_DEST_RCPT_LIMIT,
-       _CONC_POS_FDBACK, VAR_CONC_POS_FDBACK,
-       _CONC_NEG_FDBACK, VAR_CONC_NEG_FDBACK,
-       _CONC_COHORT_LIM, VAR_CONC_COHORT_LIM,
-       _DEST_RATE_DELAY, VAR_DEST_RATE_DELAY,
-       0,
-    };
-    static const PC_STRING_NV spawn_params[] = {
-       /* suffix, default parameter name */
-       _MAXTIME, VAR_COMMAND_MAXTIME,
-       0,
-    };
-    typedef struct {
-       const char *progname;
-       const PC_STRING_NV *params;
-    } PC_SERVICE_DEF;
-    static const PC_SERVICE_DEF service_defs[] = {
-       MAIL_PROGRAM_LOCAL, service_params,
-       MAIL_PROGRAM_ERROR, service_params,
-       MAIL_PROGRAM_VIRTUAL, service_params,
-       MAIL_PROGRAM_SMTP, service_params,
-       MAIL_PROGRAM_LMTP, service_params,
-       MAIL_PROGRAM_PIPE, service_params,
-       MAIL_PROGRAM_SPAWN, spawn_params,
-       0,
-    };
-    const PC_STRING_NV *sp;
-    const char *progname;
-    const char *service;
-    PC_MASTER_ENT *masterp;
-    ARGV   *argv;
-    const PC_SERVICE_DEF *sd;
-
-    /*
-     * Initialize the table with service parameter names and defaults.
-     */
-    serv_param_table = (PC_STRING_NV *) mymalloc(1);
-    serv_param_tablen = 0;
-
-    /*
-     * Extract service names from master.cf and generate service parameter
-     * information.
-     */
-    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++) {
-
-       /*
-        * Add service parameters for message delivery transports or spawn
-        * programs.
-        */
-       progname = argv->argv[7];
-       for (sd = service_defs; sd->progname; sd++) {
-           if (strcmp(sd->progname, progname) == 0) {
-               service = argv->argv[0];
-               for (sp = sd->params; sp->name; sp++)
-                   add_service_parameter(service, sp->name, sp->value);
-               break;
-           }
-       }
-    }
-
-    /*
-     * Now that the service parameter table size is frozen, enter the
-     * parameters into the hash so that we can find and print them.
-     */
-    for (sp = serv_param_table; sp < serv_param_table + serv_param_tablen; sp++)
-       if (htable_locate(param_table, sp->name) == 0)
-           htable_enter(param_table, sp->name, (char *) sp);
-}
-
-#define NO_SCAN_RESULT ((VSTRING *) 0)
-#define NO_SCAN_FILTER ((char *) 0)
-#define NO_SCAN_MODE   (0)
-
-/* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */
-
-#define SCAN_USER_PARAMETER_VALUE(value, scope) do { \
-    (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \
-               NO_SCAN_FILTER, flag_user_parameter, ((char *) (scope))); \
-} while (0)
-
-/* FLAG_USER_PARAMETER - flag user-defined name "valid" if it has name=value */
-
-#define FLAG_USER_PARAMETER(name, scope) do { \
-    flag_user_parameter((name), NO_SCAN_MODE, ((char *) (scope))); \
-} while (0)
-
-/* flag_user_parameter - flag user-defined name "valid" if it has name=value */
-
-static const char *flag_user_parameter(const char *mac_name,
-                                              int unused_mode,
-                                              char *context)
-{
-    PC_MASTER_ENT *local_scope = (PC_MASTER_ENT *) context;
-
-    /*
-     * If the name=value exists in the local (or global) name space, update
-     * the local (or global) "valid" parameter name table.
-     * 
-     * Do not "validate" user-defined parameters whose name appears only as
-     * macro expansion; this is how Postfix historically implements backwards
-     * compatibility after a feature name change.
-     */
-    if (local_scope && dict_get(local_scope->all_params, mac_name)) {
-       if (htable_locate(local_scope->valid_names, mac_name) == 0)
-           htable_enter(local_scope->valid_names, mac_name, "");
-    } else if (mail_conf_lookup(mac_name) != 0) {
-       if (htable_locate(param_table, mac_name) == 0) {
-           user_param_table = (char **)
-               myrealloc((char *) user_param_table,
-                      (user_param_tablen + 1) * sizeof(*user_param_table));
-           user_param_table[user_param_tablen] = mystrdup(mac_name);
-           user_param_tablen += 1;
-       }
-    }
-    return (0);
-}
-
-/* pc_lookup_eval - generalized mail_conf_lookup_eval */
-
-static const char *pc_lookup_eval(const char *dict_name, const char *name)
-{
-    const char *value;
-
-#define RECURSIVE       1
-
-    if ((value = dict_lookup(dict_name, name)) != 0)
-       value = dict_eval(dict_name, value, RECURSIVE);
-    return (value);
-}
-
-/* scan_user_parameter_namespace - scan parameters in name space */
-
-static void scan_user_parameter_namespace(const char *dict_name,
-                                                 PC_MASTER_ENT *local_scope)
-{
-    const char *myname = "scan_user_parameter_namespace";
-    const char *class_list;
-    char   *saved_class_list;
-    char   *cp;
-    DICT   *dict;
-    char   *param_name;
-    int     how;
-    const char *cparam_name;
-    const char *cparam_value;
-
-    /*
-     * Flag parameter names in smtpd_restriction_classes as "valid", but only
-     * if they have a "name=value" entry. If we are in not in a local name
-     * space, update the global restriction class name table, so that we can
-     * query the global table from within a local master.cf name space.
-     */
-    if ((class_list = pc_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0) {
-       cp = saved_class_list = mystrdup(class_list);
-       while ((param_name = mystrtok(&cp, ", \t\r\n")) != 0) {
-           if (local_scope == 0
-               && htable_locate(rest_class_table, param_name) == 0)
-               htable_enter(rest_class_table, param_name, "");
-           FLAG_USER_PARAMETER(param_name, local_scope);
-       }
-       myfree(saved_class_list);
-    }
-
-    /*
-     * For all "name=value" instances: a) if the name space is local and the
-     * name appears in the global restriction class table, flag the name as
-     * "valid" in the local name space; b) scan the value for macro
-     * expansions of unknown parameter names, and flag those parameter names
-     * as "valid" if they have a "name=value" entry.
-     */
-    if ((dict = dict_handle(dict_name)) == 0)
-       msg_panic("%s: parameter dictionary %s not found",
-                 myname, dict_name);
-    if (dict->sequence == 0)
-       msg_panic("%s: parameter dictionary %s has no iterator",
-                 myname, dict_name);
-    for (how = DICT_SEQ_FUN_FIRST;
-        dict->sequence(dict, how, &cparam_name, &cparam_value) == 0;
-        how = DICT_SEQ_FUN_NEXT) {
-       if (local_scope != 0
-           && htable_locate(local_scope->valid_names, cparam_name) == 0
-           && htable_locate(rest_class_table, cparam_name) != 0)
-           htable_enter(local_scope->valid_names, cparam_name, "");
-       SCAN_USER_PARAMETER_VALUE(cparam_value, local_scope);
-    }
-}
-
-/* add_user_parameters - add parameters with user-defined names */
-
-static void add_user_parameters(void)
-{
-    PC_MASTER_ENT *masterp;
-    ARGV   *argv;
-    char   *arg;
-    int     field;
-    char   *saved_arg;
-    char   *param_name;
-    char   *param_value;
-    DICT   *dict;
-    char  **up;
-
-    /*
-     * Initialize the global table with user-defined parameter names, and the
-     * table with global restriction class names.
-     */
-    user_param_table = (char **) mymalloc(1);
-    user_param_tablen = 0;
-    rest_class_table = htable_create(1);
-
-    /*
-     * Scan the explicit name=value entries in the global name space.
-     */
-    scan_user_parameter_namespace(CONFIG_DICT, (PC_MASTER_ENT *) 0);
-
-    /*
-     * Scan the "-o parameter=value" instances in each master.cf name space.
-     */
-    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++) {
-       for (field = MASTER_FIELD_COUNT; argv->argv[field] != 0; field++) {
-           arg = argv->argv[field];
-           if (arg[0] != '-')
-               break;
-           if (strcmp(arg, "-o") == 0 && (arg = argv->argv[field + 1]) != 0) {
-               saved_arg = mystrdup(arg);
-               if (split_nameval(saved_arg, &param_name, &param_value) == 0)
-                   dict_update(masterp->name_space, param_name, param_value);
-               myfree(saved_arg);
-               field += 1;
-           }
-       }
-       if ((dict = dict_handle(masterp->name_space)) != 0) {
-           masterp->all_params = dict;
-           masterp->valid_names = htable_create(1);
-           scan_user_parameter_namespace(masterp->name_space, masterp);
-       }
-    }
-
-    /*
-     * Now that the user-defined parameter table size is frozen, enter the
-     * parameters into the hash so that we can find and print them.
-     */
-    for (up = user_param_table; up < user_param_table + user_param_tablen; up++)
-       if (htable_locate(param_table, *up) == 0)
-           htable_enter(param_table, *up, (char *) up);
-}
-
-/* hash_parameters - hash all parameter names so we can find and sort them */
-
-static void hash_parameters(void)
-{
-    const CONFIG_TIME_TABLE *ctt;
-    const CONFIG_BOOL_TABLE *cbt;
-    const CONFIG_INT_TABLE *cit;
-    const CONFIG_STR_TABLE *cst;
-    const CONFIG_STR_FN_TABLE *csft;
-    const CONFIG_RAW_TABLE *rst;
-    const CONFIG_NINT_TABLE *nst;
-    const CONFIG_NBOOL_TABLE *bst;
-    const CONFIG_LONG_TABLE *lst;
-
-    param_table = htable_create(100);
-
-    for (ctt = time_table; ctt->name; ctt++)
-       htable_enter(param_table, ctt->name, (char *) ctt);
-    for (cbt = bool_table; cbt->name; cbt++)
-       htable_enter(param_table, cbt->name, (char *) cbt);
-    for (cit = int_table; cit->name; cit++)
-       htable_enter(param_table, cit->name, (char *) cit);
-    for (cst = str_table; cst->name; cst++)
-       htable_enter(param_table, cst->name, (char *) cst);
-    for (csft = str_fn_table; csft->name; csft++)
-       htable_enter(param_table, csft->name, (char *) csft);
-    for (csft = str_fn_table_2; csft->name; csft++)
-       htable_enter(param_table, csft->name, (char *) csft);
-    for (rst = raw_table; rst->name; rst++)
-       htable_enter(param_table, rst->name, (char *) rst);
-    for (nst = nint_table; nst->name; nst++)
-       htable_enter(param_table, nst->name, (char *) nst);
-    for (bst = nbool_table; bst->name; bst++)
-       htable_enter(param_table, bst->name, (char *) bst);
-    for (lst = long_table; lst->name; lst++)
-       htable_enter(param_table, lst->name, (char *) lst);
-}
-
-/* print_line - show line possibly folded, and with normalized whitespace */
-
-static void print_line(int mode, const char *fmt,...)
-{
-    va_list ap;
-    static VSTRING *buf = 0;
-    char   *start;
-    char   *next;
-    int     line_len = 0;
-    int     word_len;
-
-    /*
-     * One-off initialization.
-     */
-    if (buf == 0)
-       buf = vstring_alloc(100);
-
-    /*
-     * Format the text.
-     */
-    va_start(ap, fmt);
-    vstring_vsprintf(buf, fmt, ap);
-    va_end(ap);
-
-    /*
-     * Normalize the whitespace. We don't use the line_wrap() routine because
-     * 1) that function does not normalize whitespace between words and 2) we
-     * want to normalize whitespace even when not wrapping lines.
-     * 
-     * XXX Some parameters preserve whitespace: for example, smtpd_banner and
-     * smtpd_reject_footer. If we have to preserve whitespace between words,
-     * then perhaps readlline() can be changed to canonicalize whitespace
-     * that follows a newline.
-     */
-    for (start = STR(buf); *(start += strspn(start, SEPARATORS)) != 0; start = next) {
-       word_len = strcspn(start, SEPARATORS);
-       if (*(next = start + word_len) != 0)
-           *next++ = 0;
-       if (word_len > 0 && line_len > 0) {
-           if ((mode & FOLD_LINE) == 0 || line_len + word_len < LINE_LIMIT) {
-               vstream_fputs(" ", VSTREAM_OUT);
-               line_len += 1;
-           } else {
-               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
-               line_len = INDENT_LEN;
-           }
-       }
-       vstream_fputs(start, VSTREAM_OUT);
-       line_len += word_len;
-    }
-    vstream_fputs("\n", VSTREAM_OUT);
-}
-
-/* show_strval - show string-valued parameter */
-
-static void show_strval(int mode, const char *name, const char *value)
-{
-    if (mode & SHOW_EVAL)
-       value = mail_conf_eval(value);
-
-    if (mode & SHOW_NAME) {
-       print_line(mode, "%s = %s\n", name, value);
-    } else {
-       print_line(mode, "%s\n", value);
-    }
-}
-
-/* show_intval - show integer-valued parameter */
-
-static void show_intval(int mode, const char *name, int value)
-{
-    if (mode & SHOW_NAME) {
-       print_line(mode, "%s = %d\n", name, value);
-    } else {
-       print_line(mode, "%d\n", value);
-    }
-}
-
-/* show_longval - show long-valued parameter */
-
-static void show_longval(int mode, const char *name, long value)
-{
-    if (mode & SHOW_NAME) {
-       print_line(mode, "%s = %ld\n", name, value);
-    } else {
-       print_line(mode, "%ld\n", value);
-    }
-}
-
-/* print_bool - print boolean parameter */
-
-static void print_bool(int mode, const char *name, CONFIG_BOOL_TABLE *cbt)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, cbt->defval ? "yes" : "no");
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, cbt->defval ? "yes" : "no");
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_time - print relative time parameter */
-
-static void print_time(int mode, const char *name, CONFIG_TIME_TABLE *ctt)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, ctt->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, ctt->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_int - print integer parameter */
-
-static void print_int(int mode, const char *name, CONFIG_INT_TABLE *cit)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_intval(mode, name, cit->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_intval(mode, name, cit->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_str - print string parameter */
-
-static void print_str(int mode, const char *name, CONFIG_STR_TABLE *cst)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, cst->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, cst->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_str_fn - print string-function parameter */
-
-static void print_str_fn(int mode, const char *name, CONFIG_STR_FN_TABLE *csft)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, csft->defval());
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, csft->defval());
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_str_fn_2 - print string-function parameter */
-
-static void print_str_fn_2(int mode, const char *name, CONFIG_STR_FN_TABLE *csft)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, csft->defval());
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, csft->defval());
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_raw - print raw string parameter */
-
-static void print_raw(int mode, const char *name, CONFIG_RAW_TABLE *rst)
-{
-    const char *value;
-
-    if (mode & SHOW_EVAL)
-       msg_warn("parameter %s expands at run-time", rst->name);
-    mode &= ~SHOW_EVAL;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, rst->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, rst->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_nint - print new integer parameter */
-
-static void print_nint(int mode, const char *name, CONFIG_NINT_TABLE *rst)
-{
-    const char *value;
-
-    if (mode & SHOW_EVAL)
-       msg_warn("parameter %s expands at run-time", name);
-    mode &= ~SHOW_EVAL;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, rst->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, rst->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_nbool - print new boolean parameter */
-
-static void print_nbool(int mode, const char *name, CONFIG_NBOOL_TABLE *bst)
-{
-    const char *value;
-
-    if (mode & SHOW_EVAL)
-       msg_warn("parameter %s expands at run-time", name);
-    mode &= ~SHOW_EVAL;
-
-    if (mode & SHOW_DEFS) {
-       show_strval(mode, name, bst->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_strval(mode, name, bst->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_long - print long parameter */
-
-static void print_long(int mode, const char *name, CONFIG_LONG_TABLE *clt)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       show_longval(mode, name, clt->defval);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               show_longval(mode, name, clt->defval);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-static void print_parameter(int, const char *, char *);
-
-/* print_service_param_default show service parameter default value */
-
-static void print_service_param_default(int mode, const char *name,
-                                               const char *defparam)
-{
-    const char *myname = "print_service_param_default";
-    char   *ptr;
-
-    if ((ptr = htable_find(param_table, defparam)) == 0)
-       msg_panic("%s: service parameter %s has unknown default value $%s",
-                 myname, name, defparam);
-    if (mode & SHOW_EVAL)
-       print_parameter(mode, name, ptr);
-    else
-       print_line(mode, "%s = $%s\n", name, defparam);
-}
-
-/* print_service_param - show service parameter */
-
-static void print_service_param(int mode, const char *name,
-                                       const PC_STRING_NV *dst)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {
-       print_service_param_default(mode, name, dst->value);
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {
-               print_service_param_default(mode, name, dst->value);
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_user_param - show user-defined parameter */
-
-static void print_user_param(int mode, const char *name)
-{
-    const char *value;
-
-    if (mode & SHOW_DEFS) {                    /* can't happen */
-       show_strval(mode, name, "");
-    } else {
-       value = dict_lookup(CONFIG_DICT, name);
-       if ((mode & SHOW_NONDEF) == 0) {
-           if (value == 0) {                   /* can't happen */
-               show_strval(mode, name, "");
-           } else {
-               show_strval(mode, name, value);
-           }
-       } else {
-           if (value != 0)
-               show_strval(mode, name, value);
-       }
-    }
-}
-
-/* print_parameter - show specific parameter */
-
-static void print_parameter(int mode, const char *name, char *ptr)
-{
-
-#define INSIDE(p,t) (ptr >= (char *) t && ptr < ((char *) t) + sizeof(t))
-#define INSIDE3(p,t,l) (ptr >= (char *) t && ptr < (char *) ((t) + (l)))
-
-    /*
-     * This is gross, but the best we can do on short notice.
-     */
-    if (INSIDE(ptr, time_table))
-       print_time(mode, name, (CONFIG_TIME_TABLE *) ptr);
-    if (INSIDE(ptr, bool_table))
-       print_bool(mode, name, (CONFIG_BOOL_TABLE *) ptr);
-    if (INSIDE(ptr, int_table))
-       print_int(mode, name, (CONFIG_INT_TABLE *) ptr);
-    if (INSIDE(ptr, str_table))
-       print_str(mode, name, (CONFIG_STR_TABLE *) ptr);
-    if (INSIDE(ptr, str_fn_table))
-       print_str_fn(mode, name, (CONFIG_STR_FN_TABLE *) ptr);
-    if (INSIDE(ptr, str_fn_table_2))
-       print_str_fn_2(mode, name, (CONFIG_STR_FN_TABLE *) ptr);
-    if (INSIDE(ptr, raw_table))
-       print_raw(mode, name, (CONFIG_RAW_TABLE *) ptr);
-    if (INSIDE(ptr, nint_table))
-       print_nint(mode, name, (CONFIG_NINT_TABLE *) ptr);
-    if (INSIDE(ptr, nbool_table))
-       print_nbool(mode, name, (CONFIG_NBOOL_TABLE *) ptr);
-    if (INSIDE(ptr, long_table))
-       print_long(mode, name, (CONFIG_LONG_TABLE *) ptr);
-    if (INSIDE3(ptr, serv_param_table, serv_param_tablen))
-       print_service_param(mode, name, (PC_STRING_NV *) ptr);
-    if (INSIDE3(ptr, user_param_table, user_param_tablen))
-       print_user_param(mode, name);
-    if (msg_verbose)
-       vstream_fflush(VSTREAM_OUT);
-}
-
-/* comp_names - qsort helper */
-
-static int comp_names(const void *a, const void *b)
-{
-    HTABLE_INFO **ap = (HTABLE_INFO **) a;
-    HTABLE_INFO **bp = (HTABLE_INFO **) b;
-
-    return (strcmp(ap[0]->key, bp[0]->key));
-}
-
-/* show_parameters - show parameter info */
-
-static void show_parameters(int mode, char **names)
-{
-    HTABLE_INFO **list;
-    HTABLE_INFO **ht;
-    char  **namep;
-    char   *value;
-
-    /*
-     * Show all parameters.
-     */
-    if (*names == 0) {
-       list = htable_list(param_table);
-       qsort((char *) list, param_table->used, sizeof(*list), comp_names);
-       for (ht = list; *ht; ht++)
-           print_parameter(mode, ht[0]->key, ht[0]->value);
-       myfree((char *) list);
-       return;
-    }
-
-    /*
-     * Show named parameters.
-     */
-    for (namep = names; *namep; namep++) {
-       if ((value = htable_find(param_table, *namep)) == 0) {
-           msg_warn("%s: unknown parameter", *namep);
-       } else {
-           print_parameter(mode, *namep, value);
-       }
-    }
-}
-
-/* show_maps - show available maps */
-
-static void show_maps(void)
-{
-    ARGV   *maps_argv;
-    int     i;
-
-    maps_argv = dict_mapnames();
-    for (i = 0; i < maps_argv->argc; i++)
-       vstream_printf("%s\n", maps_argv->argv[i]);
-    argv_free(maps_argv);
-}
-
-/* show_locks - show available mailbox locking methods */
-
-static void show_locks(void)
-{
-    ARGV   *locks_argv;
-    int     i;
-
-    locks_argv = mbox_lock_names();
-    for (i = 0; i < locks_argv->argc; i++)
-       vstream_printf("%s\n", locks_argv->argv[i]);
-    argv_free(locks_argv);
-}
-
-/* print_master_line - print one master line */
-
-static void print_master_line(int mode, ARGV *argv)
-{
-    char   *arg;
-    char   *aval;
-    int     line_len;
-    int     field;
-    int     in_daemon_options;
-    static int column_goal[] = {
-       0,                              /* service */
-       11,                             /* type */
-       17,                             /* private */
-       25,                             /* unpriv */
-       33,                             /* chroot */
-       41,                             /* wakeup */
-       49,                             /* maxproc */
-       57,                             /* command */
-    };
-
-#define ADD_TEXT(text, len) do { \
-       vstream_fputs(text, VSTREAM_OUT); line_len += len; } \
-    while (0)
-#define ADD_SPACE ADD_TEXT(" ", 1)
-
-    /*
-     * Show the standard fields at their preferred column position. Use
-     * single-space separation when some field does not fit.
-     */
-    for (line_len = 0, field = 0; field < MASTER_FIELD_COUNT; field++) {
-       arg = argv->argv[field];
-       if (line_len > 0) {
-           while (line_len < column_goal[field] - 1)
-               ADD_SPACE;
-           ADD_SPACE;
-       }
-       ADD_TEXT(arg, strlen(arg));
-    }
-
-    /*
-     * Format the daemon command-line options and non-option arguments. Here,
-     * we have no data-dependent preference for column positions, but we do
-     * have argument grouping preferences.
-     */
-    in_daemon_options = 1;
-    for ( /* void */ ; argv->argv[field] != 0; field++) {
-       arg = argv->argv[field];
-       if (in_daemon_options) {
-
-           /*
-            * Try to show the generic options (-v -D) on the first line, and
-            * non-options on a later line.
-            */
-           if (arg[0] != '-') {
-               in_daemon_options = 0;
-               if ((mode & FOLD_LINE)
-                   && line_len > column_goal[MASTER_FIELD_COUNT - 1]) {
-                   vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
-                   line_len = INDENT_LEN;
-               }
-           }
-
-           /*
-            * Try to avoid breaking "-o name=value" over multiple lines if
-            * it would fit on one line.
-            */
-           else if ((mode & FOLD_LINE)
-                    && line_len > INDENT_LEN && strcmp(arg, "-o") == 0
-                    && (aval = argv->argv[field + 1]) != 0
-                    && INDENT_LEN + 3 + strlen(aval) < LINE_LIMIT) {
-               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
-               line_len = INDENT_LEN;
-               ADD_TEXT(arg, strlen(arg));
-               arg = aval;
-               field += 1;
-           }
-       }
-
-       /*
-        * Insert a line break when the next argument won't fit (unless, of
-        * course, we just inserted a line break).
-        */
-       if (line_len > INDENT_LEN) {
-           if ((mode & FOLD_LINE) == 0
-               || line_len + 1 + strlen(arg) < LINE_LIMIT) {
-               ADD_SPACE;
-           } else {
-               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
-               line_len = INDENT_LEN;
-           }
-       }
-       ADD_TEXT(arg, strlen(arg));
-    }
-    vstream_fputs("\n", VSTREAM_OUT);
-}
-
-/* show_master - show master.cf entries */
-
-static void show_master(int mode, char **filters)
-{
-    PC_MASTER_ENT *masterp;
-    ARGV   *argv;
-    ARGV   *service_filter = 0;
-
-    /*
-     * Initialize the service filter.
-     */
-    if (filters[0])
-       service_filter = match_service_init_argv(filters);
-
-    /*
-     * Iterate over the master table.
-     */
-    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++)
-       if (service_filter == 0
-           || match_service_match(service_filter, masterp->name_space) != 0)
-           print_master_line(mode, argv);
-    if (service_filter != 0)
-       argv_free(service_filter);
-}
-
-/* show_sasl - show SASL plug-in types */
-
-static void show_sasl(int what)
-{
-    ARGV   *sasl_argv;
-    int     i;
-
-    sasl_argv = (what & SHOW_SASL_SERV) ? xsasl_server_types() :
-       xsasl_client_types();
-    for (i = 0; i < sasl_argv->argc; i++)
-       vstream_printf("%s\n", sasl_argv->argv[i]);
-    argv_free(sasl_argv);
-}
-
-/* flag_unused_parameters - warn about unused parameters */
-
-static void flag_unused_parameters(DICT *dict, const char *conf_name,
-                                          PC_MASTER_ENT *local_scope)
-{
-    const char *myname = "flag_unused_parameters";
-    const char *param_name;
-    const char *param_value;
-    int     how;
-
-    /*
-     * Iterate over all entries, and flag parameter names that aren't used
-     * anywhere. Show the warning message at the end of the output.
-     */
-    if (dict->sequence == 0)
-       msg_panic("%s: parameter dictionary %s has no iterator",
-                 myname, conf_name);
-    for (how = DICT_SEQ_FUN_FIRST;
-        dict->sequence(dict, how, &param_name, &param_value) == 0;
-        how = DICT_SEQ_FUN_NEXT) {
-       if (htable_locate(param_table, param_name) == 0
-           && (local_scope == 0
-            || htable_locate(local_scope->valid_names, param_name) == 0)) {
-           vstream_fflush(VSTREAM_OUT);
-           msg_warn("%s/%s: unused parameter: %s=%s",
-                    var_config_dir, conf_name, param_name, param_value);
-       }
-    }
-}
-
-/* flag_unused_main_parameters - warn about unused parameters */
-
-static void flag_unused_main_parameters(void)
-{
-    const char *myname = "flag_unused_main_parameters";
-    DICT   *dict;
-
-    /*
-     * Iterate over all main.cf entries, and flag parameter names that aren't
-     * used anywhere.
-     */
-    if ((dict = dict_handle(CONFIG_DICT)) == 0)
-       msg_panic("%s: parameter dictionary %s not found",
-                 myname, CONFIG_DICT);
-    flag_unused_parameters(dict, MAIN_CONF_FILE, (PC_MASTER_ENT *) 0);
-}
-
-/* flag_unused_master_parameters - warn about unused parameters */
-
-static void flag_unused_master_parameters(void)
-{
-    PC_MASTER_ENT *masterp;
-    DICT   *dict;
-
-    /*
-     * Iterate over all master.cf entries, and flag parameter names that
-     * aren't used anywhere.
-     */
-    for (masterp = master_table; masterp->argv != 0; masterp++)
-       if ((dict = masterp->all_params) != 0)
-           flag_unused_parameters(dict, MASTER_CONF_FILE, masterp);
-}
-
 MAIL_VERSION_STAMP_DECLARE;
 
 /* main */
@@ -2145,17 +532,18 @@ int     main(int argc, char **argv)
            read_parameters();
            set_parameters();
        }
-       hash_parameters();
+       register_builtin_parameters();
 
        /*
         * Add service-dependent parameters (service names from master.cf)
         * and user-defined parameters ($name macros in parameter values in
-        * main.cf and master.cf).
+        * main.cf and master.cf, but only if those names have a name=value
+        * in main.cf or master.cf).
         */
        read_master(WARN_ON_OPEN_ERROR);
-       add_service_parameters();
+       register_service_parameters();
        if ((cmd_mode & SHOW_DEFS) == 0)
-           add_user_parameters();
+           register_user_parameters();
 
        /*
         * Show the requested values.
diff --git a/postfix/src/postconf/postconf.h b/postfix/src/postconf/postconf.h
new file mode 100644 (file)
index 0000000..b5ec355
--- /dev/null
@@ -0,0 +1,179 @@
+/*++
+/* NAME
+/*     postconf 3h
+/* SUMMARY
+/*     module interfaces
+/* SYNOPSIS
+/*     #include <postconf.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <htable.h>
+#include <argv.h>
+#include <dict.h>
+
+ /*
+  * What we're supposed to be doing.
+  */
+#define SHOW_NONDEF    (1<<0)          /* show main.cf non-default settings */
+#define SHOW_DEFS      (1<<1)          /* show main.cf default setting */
+#define SHOW_NAME      (1<<2)          /* show main.cf parameter name */
+#define SHOW_MAPS      (1<<3)          /* show map types */
+#define EDIT_MAIN      (1<<4)          /* edit main.cf */
+#define SHOW_LOCKS     (1<<5)          /* show mailbox lock methods */
+#define SHOW_EVAL      (1<<6)          /* expand main.cf right-hand sides */
+#define SHOW_SASL_SERV (1<<7)          /* show server auth plugin types */
+#define SHOW_SASL_CLNT (1<<8)          /* show client auth plugin types */
+#define COMMENT_OUT    (1<<9)          /* #-out selected main.cf entries */
+#define SHOW_MASTER    (1<<10)         /* show master.cf entries */
+#define FOLD_LINE      (1<<11)         /* fold long *.cf entries */
+
+#define DEF_MODE       SHOW_NAME       /* default mode */
+
+ /*
+  * Structure for one "valid parameter" (built-in, service-defined or valid
+  * user-defined). See the postconf_builtin, postconf_service and
+  * postconf_user modules for narrative text.
+  */
+typedef struct {
+    int     flags;                     /* see below */
+    char   *param_data;                        /* mostly, the default value */
+    const char *(*convert_fn) (char *);        /* value to string */
+} PC_PARAM_NODE;
+
+ /* Values for flags. See the postconf_node module for narrative text. */
+#define PC_PARAM_FLAG_NONE     (0)
+#define PC_PARAM_FLAG_RAW      (1<<0)
+
+#define PC_RAW_PARAMETER(node) ((node)->flags & PC_PARAM_FLAG_RAW)
+
+ /* Values for param_data. See postconf_node module for narrative text. */
+#define PC_PARAM_NO_DATA       ((char *) 0)
+
+ /*
+  * Lookup table for global "valid parameter" information.
+  */
+#define PC_PARAM_TABLE                 HTABLE
+#define PC_PARAM_INFO                  HTABLE_INFO
+
+extern PC_PARAM_TABLE *param_table;
+
+ /*
+  * postconf_node.c.
+  */
+#define PC_PARAM_TABLE_CREATE(size)    htable_create(size);
+#define PC_PARAM_NODE_CAST(ptr)                ((PC_PARAM_NODE *) (ptr))
+
+#define PC_PARAM_TABLE_LIST(table)     htable_list(table)
+#define PC_PARAM_INFO_NAME(ht)         ((const char *) (ht)->key)
+#define PC_PARAM_INFO_NODE(ht)         PC_PARAM_NODE_CAST((ht)->value)
+
+#define PC_PARAM_TABLE_FIND(table, name) \
+       PC_PARAM_NODE_CAST(htable_find((table), (name)))
+#define PC_PARAM_TABLE_LOCATE(table, name) htable_locate((table), (name))
+#define PC_PARAM_TABLE_ENTER(table, name, flags, data, func) \
+       htable_enter((table), (name), (char *) make_param_node((flags), \
+           (data), (func)))
+
+PC_PARAM_NODE *make_param_node(int, char *, const char *(*) (char *));
+const char *convert_param_node(int, const char *, PC_PARAM_NODE *);
+extern VSTRING *param_string_buf;
+
+ /*
+  * Structure of one master.cf entry.
+  */
+typedef struct {
+    char   *name_space;                        /* service.type, parameter name space */
+    ARGV   *argv;                      /* null, or master.cf fields */
+    DICT   *all_params;                        /* null, or all name=value entries */
+    HTABLE *valid_names;               /* null, or "valid" parameter names */
+} PC_MASTER_ENT;
+
+#define PC_MASTER_MIN_FIELDS   8       /* mandatory field count */
+
+ /*
+  * Lookup table for master.cf entries. The table is terminated with an entry
+  * that has a null argv member.
+  */
+PC_MASTER_ENT *master_table;
+
+ /*
+  * Line-wrapping support.
+  */
+#define LINE_LIMIT     80              /* try to fold longer lines */
+#define SEPARATORS     " \t\r\n"
+#define INDENT_LEN     4               /* indent long text by 4 */
+#define INDENT_TEXT    "       "
+
+ /*
+  * XXX Global so that postconf_builtin.c call-backs can see it.
+  */
+extern int cmd_mode;
+
+ /*
+  * postconf_misc.c.
+  */
+extern void set_config_dir(void);
+
+ /*
+  * postconf_main.c
+  */
+extern void read_parameters(void);
+extern void set_parameters(void);
+extern void show_parameters(int, char **);
+
+ /*
+  * postconf_edit.c
+  */
+extern void edit_parameters(int, int, char **);
+
+ /*
+  * postconf_master.c.
+  */
+extern void read_master(int);
+extern void show_master(int, char **);
+
+#define WARN_ON_OPEN_ERROR     0
+#define FAIL_ON_OPEN_ERROR     1
+
+ /*
+  * postconf_builtin.c.
+  */
+extern void register_builtin_parameters(void);
+
+ /*
+  * postconf_service.c.
+  */
+extern void register_service_parameters(void);
+
+ /*
+  * postconf_user.c.
+  */
+extern void register_user_parameters(void);
+
+ /*
+  * postconf_unused.c.
+  */
+extern void flag_unused_main_parameters(void);
+extern void flag_unused_master_parameters(void);
+
+ /*
+  * postconf_other.c.
+  */
+extern void show_maps(void);
+extern void show_locks(void);
+extern void show_sasl(int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
diff --git a/postfix/src/postconf/postconf_builtin.c b/postfix/src/postconf/postconf_builtin.c
new file mode 100644 (file)
index 0000000..09893e5
--- /dev/null
@@ -0,0 +1,368 @@
+/*++
+/* NAME
+/*     postconf_builtin 3
+/* SUMMARY
+/*     built-in parameter support
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    register_builtin_parameters()
+/* DESCRIPTION
+/*     register_builtin_parameters() initializes the global parameter
+/*     name space and adds all built-in parameter information.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <get_hostname.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mynetworks.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <mail_version.h>
+#include <mail_proto.h>
+#include <mail_addr.h>
+#include <inet_proto.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+  * Support for built-in parameters: declarations generated by scanning
+  * actual C source files.
+  */
+#include "time_vars.h"
+#include "bool_vars.h"
+#include "int_vars.h"
+#include "str_vars.h"
+#include "raw_vars.h"
+#include "nint_vars.h"
+#include "nbool_vars.h"
+#include "long_vars.h"
+
+ /*
+  * Support for built-in parameters: manually extracted.
+  */
+#include "install_vars.h"
+
+ /*
+  * Support for built-in parameters: lookup tables generated by scanning
+  * actual C source files.
+  */
+static const CONFIG_TIME_TABLE time_table[] = {
+#include "time_table.h"
+    0,
+};
+
+static const CONFIG_BOOL_TABLE bool_table[] = {
+#include "bool_table.h"
+    0,
+};
+
+static const CONFIG_INT_TABLE int_table[] = {
+#include "int_table.h"
+    0,
+};
+
+static const CONFIG_STR_TABLE str_table[] = {
+#include "str_table.h"
+#include "install_table.h"
+    0,
+};
+
+static const CONFIG_RAW_TABLE raw_table[] = {
+#include "raw_table.h"
+    0,
+};
+
+static const CONFIG_NINT_TABLE nint_table[] = {
+#include "nint_table.h"
+    0,
+};
+
+static const CONFIG_NBOOL_TABLE nbool_table[] = {
+#include "nbool_table.h"
+    0,
+};
+
+static const CONFIG_LONG_TABLE long_table[] = {
+#include "long_table.h"
+    0,
+};
+
+ /*
+  * Parameters with default values obtained via function calls.
+  */
+char   *var_myhostname;
+char   *var_mydomain;
+char   *var_mynetworks;
+
+static const char *check_myhostname(void);
+static const char *check_mydomainname(void);
+static const char *check_mynetworks(void);
+
+static const CONFIG_STR_FN_TABLE str_fn_table[] = {
+    VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
+    VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
+    0,
+};
+static const CONFIG_STR_FN_TABLE str_fn_table_2[] = {
+    VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0,
+    0,
+};
+
+#define STR(x) vstring_str(x)
+
+/* check_myhostname - lookup hostname and validate */
+
+static const char *check_myhostname(void)
+{
+    static const char *name;
+    const char *dot;
+    const char *domain;
+
+    /*
+     * Use cached result.
+     */
+    if (name)
+       return (name);
+
+    /*
+     * If the local machine name is not in FQDN form, try to append the
+     * contents of $mydomain.
+     */
+    name = get_hostname();
+    if ((dot = strchr(name, '.')) == 0) {
+       if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0)
+           domain = DEF_MYDOMAIN;
+       name = concatenate(name, ".", domain, (char *) 0);
+    }
+    return (name);
+}
+
+/* get_myhostname - look up and store my hostname */
+
+static void get_myhostname(void)
+{
+    const char *name;
+
+    if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0)
+       name = check_myhostname();
+    var_myhostname = mystrdup(name);
+}
+
+/* check_mydomainname - lookup domain name and validate */
+
+static const char *check_mydomainname(void)
+{
+    char   *dot;
+
+    /*
+     * Use the hostname when it is not a FQDN ("foo"), or when the hostname
+     * actually is a domain name ("foo.com").
+     */
+    if (var_myhostname == 0)
+       get_myhostname();
+    if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
+       return (DEF_MYDOMAIN);
+    return (dot + 1);
+}
+
+/* check_mynetworks - lookup network address list */
+
+static const char *check_mynetworks(void)
+{
+    INET_PROTO_INFO *proto_info;
+    const char *junk;
+
+    if (var_inet_interfaces == 0) {
+       if ((cmd_mode & SHOW_DEFS)
+           || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
+           junk = DEF_INET_INTERFACES;
+       var_inet_interfaces = mystrdup(junk);
+    }
+    if (var_mynetworks_style == 0) {
+       if ((cmd_mode & SHOW_DEFS)
+           || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
+           junk = DEF_MYNETWORKS_STYLE;
+       var_mynetworks_style = mystrdup(junk);
+    }
+    if (var_inet_protocols == 0) {
+       if ((cmd_mode & SHOW_DEFS)
+           || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
+           junk = DEF_INET_PROTOCOLS;
+       var_inet_protocols = mystrdup(junk);
+       proto_info = inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
+    }
+    return (mynetworks());
+}
+
+/* convert_bool_parameter - get boolean parameter string value */
+
+static const char *convert_bool_parameter(char *ptr)
+{
+    CONFIG_BOOL_TABLE *cbt = (CONFIG_BOOL_TABLE *) ptr;
+
+    return (cbt->defval ? "yes" : "no");
+}
+
+/* convert_time_parameter - get relative time parameter string value */
+
+static const char *convert_time_parameter(char *ptr)
+{
+    CONFIG_TIME_TABLE *ctt = (CONFIG_TIME_TABLE *) ptr;
+
+    return (ctt->defval);
+}
+
+/* convert_int_parameter - get integer parameter string value */
+
+static const char *convert_int_parameter(char *ptr)
+{
+    CONFIG_INT_TABLE *cit = (CONFIG_INT_TABLE *) ptr;
+
+    return (STR(vstring_sprintf(param_string_buf, "%d", cit->defval)));
+}
+
+/* convert_str_parameter - get string parameter string value */
+
+static const char *convert_str_parameter(char *ptr)
+{
+    CONFIG_STR_TABLE *cst = (CONFIG_STR_TABLE *) ptr;
+
+    return (cst->defval);
+}
+
+/* convert_str_fn_parameter - get string-function parameter string value */
+
+static const char *convert_str_fn_parameter(char *ptr)
+{
+    CONFIG_STR_FN_TABLE *cft = (CONFIG_STR_FN_TABLE *) ptr;
+
+    return (cft->defval());
+}
+
+/* convert_raw_parameter - get raw string parameter string value */
+
+static const char *convert_raw_parameter(char *ptr)
+{
+    CONFIG_RAW_TABLE *rst = (CONFIG_RAW_TABLE *) ptr;
+
+    return (rst->defval);
+}
+
+/* convert_nint_parameter - get new integer parameter string value */
+
+static const char *convert_nint_parameter(char *ptr)
+{
+    CONFIG_NINT_TABLE *rst = (CONFIG_NINT_TABLE *) ptr;
+
+    return (rst->defval);
+}
+
+/* convert_nbool_parameter - get new boolean parameter string value */
+
+static const char *convert_nbool_parameter(char *ptr)
+{
+    CONFIG_NBOOL_TABLE *bst = (CONFIG_NBOOL_TABLE *) ptr;
+
+    return (bst->defval);
+}
+
+/* convert_long_parameter - get long parameter string value */
+
+static const char *convert_long_parameter(char *ptr)
+{
+    CONFIG_LONG_TABLE *clt = (CONFIG_LONG_TABLE *) ptr;
+
+    return (STR(vstring_sprintf(param_string_buf, "%ld", clt->defval)));
+}
+
+/* register_builtin_parameters - add built-ins to the global name space */
+
+void    register_builtin_parameters(void)
+{
+    const char *myname = "register_builtin_parameters";
+    const CONFIG_TIME_TABLE *ctt;
+    const CONFIG_BOOL_TABLE *cbt;
+    const CONFIG_INT_TABLE *cit;
+    const CONFIG_STR_TABLE *cst;
+    const CONFIG_STR_FN_TABLE *cft;
+    const CONFIG_RAW_TABLE *rst;
+    const CONFIG_NINT_TABLE *nst;
+    const CONFIG_NBOOL_TABLE *bst;
+    const CONFIG_LONG_TABLE *lst;
+
+    /*
+     * Sanity checks.
+     */
+    if (param_table != 0)
+       msg_panic("%s: global parameter table is already initialized", myname);
+
+    /*
+     * Initialize the global parameter table.
+     */
+    param_table = PC_PARAM_TABLE_CREATE(100);
+
+    /*
+     * Add the built-in parameters to the global name space.
+     */
+    for (ctt = time_table; ctt->name; ctt++)
+       PC_PARAM_TABLE_ENTER(param_table, ctt->name, PC_PARAM_FLAG_NONE,
+                            (char *) ctt, convert_time_parameter);
+    for (cbt = bool_table; cbt->name; cbt++)
+       PC_PARAM_TABLE_ENTER(param_table, cbt->name, PC_PARAM_FLAG_NONE,
+                            (char *) cbt, convert_bool_parameter);
+    for (cit = int_table; cit->name; cit++)
+       PC_PARAM_TABLE_ENTER(param_table, cit->name, PC_PARAM_FLAG_NONE,
+                            (char *) cit, convert_int_parameter);
+    for (cst = str_table; cst->name; cst++)
+       PC_PARAM_TABLE_ENTER(param_table, cst->name, PC_PARAM_FLAG_NONE,
+                            (char *) cst, convert_str_parameter);
+    for (cft = str_fn_table; cft->name; cft++)
+       PC_PARAM_TABLE_ENTER(param_table, cft->name, PC_PARAM_FLAG_NONE,
+                            (char *) cft, convert_str_fn_parameter);
+    for (cft = str_fn_table_2; cft->name; cft++)
+       PC_PARAM_TABLE_ENTER(param_table, cft->name, PC_PARAM_FLAG_NONE,
+                            (char *) cft, convert_str_fn_parameter);
+    for (rst = raw_table; rst->name; rst++)
+       PC_PARAM_TABLE_ENTER(param_table, rst->name, PC_PARAM_FLAG_RAW,
+                            (char *) rst, convert_raw_parameter);
+    for (nst = nint_table; nst->name; nst++)
+       PC_PARAM_TABLE_ENTER(param_table, nst->name, PC_PARAM_FLAG_NONE,
+                            (char *) nst, convert_nint_parameter);
+    for (bst = nbool_table; bst->name; bst++)
+       PC_PARAM_TABLE_ENTER(param_table, bst->name, PC_PARAM_FLAG_NONE,
+                            (char *) bst, convert_nbool_parameter);
+    for (lst = long_table; lst->name; lst++)
+       PC_PARAM_TABLE_ENTER(param_table, lst->name, PC_PARAM_FLAG_NONE,
+                            (char *) lst, convert_long_parameter);
+}
diff --git a/postfix/src/postconf/postconf_edit.c b/postfix/src/postconf/postconf_edit.c
new file mode 100644 (file)
index 0000000..660e7e9
--- /dev/null
@@ -0,0 +1,202 @@
+/*++
+/* NAME
+/*     postconf_edit 3
+/* SUMMARY
+/*     edit main.cf
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    edit_parameters(mode, argc, argv)
+/*     int     mode;
+/*     int     argc;
+/*     char    **argv;
+/* DESCRIPTION
+/*     Edit the \fBmain.cf\fR configuration file, and update
+/*     parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
+/*     given on the command line. The file is copied to a temporary
+/*     file then renamed into place.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* FILES
+/*     /etc/postfix/main.cf, Postfix configuration parameters
+/*     /etc/postfix/main.cf.tmp, temporary name
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <edit_file.h>
+#include <readlline.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* edit_parameters - edit parameter file */
+
+void    edit_parameters(int mode, int argc, char **argv)
+{
+    char   *path;
+    EDIT_FILE *ep;
+    VSTREAM *src;
+    VSTREAM *dst;
+    VSTRING *buf = vstring_alloc(100);
+    VSTRING *key = vstring_alloc(10);
+    char   *cp;
+    char   *edit_key;
+    char   *edit_val;
+    HTABLE *table;
+    struct cvalue {
+       char   *value;
+       int     found;
+    };
+    struct cvalue *cvalue;
+    HTABLE_INFO **ht_info;
+    HTABLE_INFO **ht;
+    int     interesting;
+    const char *err;
+
+    /*
+     * Store command-line parameters for quick lookup.
+     */
+    table = htable_create(argc);
+    while ((cp = *argv++) != 0) {
+       if (strchr(cp, '\n') != 0)
+           msg_fatal("-e or -# accepts no multi-line input");
+       while (ISSPACE(*cp))
+           cp++;
+       if (*cp == '#')
+           msg_fatal("-e or -# accepts no comment input");
+       if (mode & EDIT_MAIN) {
+           if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
+               msg_fatal("%s: \"%s\"", err, cp);
+       } else if (mode & COMMENT_OUT) {
+           if (*cp == 0)
+               msg_fatal("-# requires non-blank parameter names");
+           if (strchr(cp, '=') != 0)
+               msg_fatal("-# requires parameter names only");
+           edit_key = mystrdup(cp);
+           trimblanks(edit_key, 0);
+           edit_val = 0;
+       } else {
+           msg_panic("edit_parameters: unknown mode %d", mode);
+       }
+       cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
+       cvalue->value = edit_val;
+       cvalue->found = 0;
+       htable_enter(table, edit_key, (char *) cvalue);
+    }
+
+    /*
+     * Open a temp file for the result. This uses a deterministic name so we
+     * don't leave behind thrash with random names.
+     */
+    set_config_dir();
+    path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
+    if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
+       msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
+    dst = ep->tmp_fp;
+
+    /*
+     * Open the original file for input.
+     */
+    if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+       /* OK to delete, since we control the temp file name exclusively. */
+       (void) unlink(ep->tmp_path);
+       msg_fatal("open %s for reading: %m", path);
+    }
+
+    /*
+     * Copy original file to temp file, while replacing parameters on the
+     * fly. Issue warnings for names found multiple times.
+     */
+#define STR(x) vstring_str(x)
+
+    interesting = 0;
+    while (vstring_get(buf, src) != VSTREAM_EOF) {
+       for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
+            /* void */ ;
+       /* Copy comment, all-whitespace, or empty line. */
+       if (*cp == '#' || *cp == 0) {
+           vstream_fputs(STR(buf), dst);
+       }
+       /* Copy, skip or replace continued text. */
+       else if (cp > STR(buf)) {
+           if (interesting == 0)
+               vstream_fputs(STR(buf), dst);
+           else if (mode & COMMENT_OUT)
+               vstream_fprintf(dst, "#%s", STR(buf));
+       }
+       /* Copy or replace start of logical line. */
+       else {
+           vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
+           cvalue = (struct cvalue *) htable_find(table, STR(key));
+           if ((interesting = !!cvalue) != 0) {
+               if (cvalue->found++ == 1)
+                   msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
+               if (mode & EDIT_MAIN)
+                   vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
+               else if (mode & COMMENT_OUT)
+                   vstream_fprintf(dst, "#%s", cp);
+               else
+                   msg_panic("edit_parameters: unknown mode %d", mode);
+           } else {
+               vstream_fputs(STR(buf), dst);
+           }
+       }
+    }
+
+    /*
+     * Generate new entries for parameters that were not found.
+     */
+    if (mode & EDIT_MAIN) {
+       for (ht_info = ht = htable_list(table); *ht; ht++) {
+           cvalue = (struct cvalue *) ht[0]->value;
+           if (cvalue->found == 0)
+               vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
+       }
+       myfree((char *) ht_info);
+    }
+
+    /*
+     * When all is well, rename the temp file to the original one.
+     */
+    if (vstream_fclose(src))
+       msg_fatal("read %s: %m", path);
+    if (edit_file_close(ep) != 0)
+       msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
+
+    /*
+     * Cleanup.
+     */
+    myfree(path);
+    vstring_free(buf);
+    vstring_free(key);
+    htable_free(table, myfree);
+}
diff --git a/postfix/src/postconf/postconf_main.c b/postfix/src/postconf/postconf_main.c
new file mode 100644 (file)
index 0000000..e7b0f72
--- /dev/null
@@ -0,0 +1,258 @@
+/*++
+/* NAME
+/*     postconf_main 3
+/* SUMMARY
+/*     basic support for main.cf
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    read_parameters()
+/*
+/*     void    set_parameters()
+/*
+/*     void    show_parameters(mode, names)
+/*     int     mode;
+/*     char    **names;
+/* DESCRIPTION
+/*     read_parameters() reads parameters from main.cf.
+/*
+/*     set_parameters() does nothing. It is a place holder for
+/*     code that assigns actual or default parameter values, which
+/*     could be needed to implement "postconf -e" parameter value
+/*     expansion.
+/*
+/*     show_parameters() writes main.cf parameters to the standard
+/*     output stream.
+/*
+/*     Arguments:
+/* .IP mode
+/*     Bit-wise OR of zero or more of the following:
+/* .RS
+/* .IP FOLD_LINE
+/*     Fold long lines.
+/* .IP SHOW_DEFS
+/*     Output default parameter values.
+/* .IP SHOW_NONDEF
+/*     Output explicit settings only.
+/* .IP SHOW_NAME
+/*     Output the parameter as "name = value".
+/* .IP SHOW_EVAL
+/*     Expand parameter values (not implemented).
+/* .RE
+/* .IP names
+/*     List of zero or more parameter names. If the list is empty,
+/*     output all parameters.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <readlline.h>
+#include <dict.h>
+#include <stringops.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* read_parameters - read parameter info from file */
+
+void    read_parameters(void)
+{
+    char   *path;
+
+    /*
+     * A direct rip-off of mail_conf_read(). XXX Avoid code duplication by
+     * better code decomposition.
+     */
+    dict_unknown_allowed = 1;
+    set_config_dir();
+    path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
+    dict_load_file(CONFIG_DICT, path);
+    myfree(path);
+}
+
+/* set_parameters - set parameter values from default or explicit setting */
+
+void set_parameters(void)
+{
+
+    /*
+     * The proposal below describes some of the steps needed to expand
+     * parameter values. It has a problem: it updates the configuration
+     * parameter dictionary, and in doing so breaks the "postconf -d"
+     * implementation. This makes "-d" and "-e" mutually exclusive.
+     * 
+     * Populate the configuration parameter dictionary with default settings or
+     * with actual settings.
+     * 
+     * Iterate over each entry in str_fn_table, str_fn_table_2, time_table,
+     * bool_table, int_table, str_table, and raw_table. Look up each
+     * parameter name in the configuration parameter dictionary. If the
+     * parameter is not set, take the default value, or take the value from
+     * main.cf, without doing $name expansions. This includes converting
+     * default values from numeric/boolean internal forms to external string
+     * form.
+     * 
+     * Once the configuration parameter dictionary is populated, printing a
+     * parameter setting is a matter of querying the configuration parameter
+     * dictionary, optionally expanding of $name values, and printing the
+     * result.
+     */
+}
+
+/* print_line - show line possibly folded, and with normalized whitespace */
+
+static void print_line(int mode, const char *fmt,...)
+{
+    va_list ap;
+    static VSTRING *buf = 0;
+    char   *start;
+    char   *next;
+    int     line_len = 0;
+    int     word_len;
+
+    /*
+     * One-off initialization.
+     */
+    if (buf == 0)
+       buf = vstring_alloc(100);
+
+    /*
+     * Format the text.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    /*
+     * Normalize the whitespace. We don't use the line_wrap() routine because
+     * 1) that function does not normalize whitespace between words and 2) we
+     * want to normalize whitespace even when not wrapping lines.
+     * 
+     * XXX Some parameters preserve whitespace: for example, smtpd_banner and
+     * smtpd_reject_footer. If we have to preserve whitespace between words,
+     * then perhaps readlline() can be changed to canonicalize whitespace
+     * that follows a newline.
+     */
+    for (start = STR(buf); *(start += strspn(start, SEPARATORS)) != 0; start = next) {
+       word_len = strcspn(start, SEPARATORS);
+       if (*(next = start + word_len) != 0)
+           *next++ = 0;
+       if (word_len > 0 && line_len > 0) {
+           if ((mode & FOLD_LINE) == 0 || line_len + word_len < LINE_LIMIT) {
+               vstream_fputs(" ", VSTREAM_OUT);
+               line_len += 1;
+           } else {
+               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
+               line_len = INDENT_LEN;
+           }
+       }
+       vstream_fputs(start, VSTREAM_OUT);
+       line_len += word_len;
+    }
+    vstream_fputs("\n", VSTREAM_OUT);
+}
+
+/* print_parameter - show specific parameter */
+
+static void print_parameter(int mode, const char *name,
+                                   PC_PARAM_NODE *node)
+{
+    const char *value;
+
+    /*
+     * Use the default or actual value.
+     */
+    if ((mode & SHOW_DEFS) != 0
+       || ((value = dict_lookup(CONFIG_DICT, name)) == 0
+           && (mode & SHOW_NONDEF) == 0))
+       value = convert_param_node(SHOW_DEFS, name, node);
+
+    /*
+     * Print with or without the name= prefix.
+     */
+    if (value != 0) {
+       if (mode & SHOW_NAME) {
+           print_line(mode, "%s = %s\n", name, value);
+       } else {
+           print_line(mode, "%s\n", value);
+       }
+       if (msg_verbose)
+           vstream_fflush(VSTREAM_OUT);
+    }
+}
+
+/* comp_names - qsort helper */
+
+static int comp_names(const void *a, const void *b)
+{
+    PC_PARAM_INFO **ap = (PC_PARAM_INFO **) a;
+    PC_PARAM_INFO **bp = (PC_PARAM_INFO **) b;
+
+    return (strcmp(PC_PARAM_INFO_NAME(ap[0]),
+                  PC_PARAM_INFO_NAME(bp[0])));
+}
+
+/* show_parameters - show parameter info */
+
+void    show_parameters(int mode, char **names)
+{
+    PC_PARAM_INFO **list;
+    PC_PARAM_INFO **ht;
+    char  **namep;
+    PC_PARAM_NODE *node;
+
+    /*
+     * Show all parameters.
+     */
+    if (*names == 0) {
+       list = PC_PARAM_TABLE_LIST(param_table);
+       qsort((char *) list, param_table->used, sizeof(*list), comp_names);
+       for (ht = list; *ht; ht++)
+           print_parameter(mode, PC_PARAM_INFO_NAME(*ht),
+                           PC_PARAM_INFO_NODE(*ht));
+       myfree((char *) list);
+       return;
+    }
+
+    /*
+     * Show named parameters.
+     */
+    for (namep = names; *namep; namep++) {
+       if ((node = PC_PARAM_TABLE_FIND(param_table, *namep)) == 0) {
+           msg_warn("%s: unknown parameter", *namep);
+       } else {
+           print_parameter(mode, *namep, node);
+       }
+    }
+}
diff --git a/postfix/src/postconf/postconf_master.c b/postfix/src/postconf/postconf_master.c
new file mode 100644 (file)
index 0000000..ab20cc6
--- /dev/null
@@ -0,0 +1,270 @@
+/*++
+/* NAME
+/*     postconf_master 3
+/* SUMMARY
+/*     support for master.cf
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    read_master(fail_on_open)
+/*     int     fail_on_open;
+/*
+/*     void    show_master(mode, filters)
+/*     int     mode;
+/*     char    **filters;
+/* DESCRIPTION
+/*     read_master() reads entries from master.cf into memory.
+/*
+/*     show_master() writes the entries in the master.cf file
+/*     to standard output.
+/*
+/*     Arguments
+/* .IP fail_on_open
+/*     Specify FAIL_ON_OPEN if open failure is a fatal error,
+/*     WARN_ON_OPEN if a warning should be logged instead.
+/* .IP mode
+/*     If the FOLD_LINE flag is set, show_master() wraps long
+/*     output lines.
+/* .IP filters
+/*     A list of zero or more expressions in master_service(3)
+/*     format. If no list is specified, show_master() outputs
+/*     all master.cf entries in the specified order.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <argv.h>
+#include <vstream.h>
+#include <readlline.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <match_service.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+#define STR(x) vstring_str(x)
+
+/* read_master - read and digest the master.cf file */
+
+void    read_master(int fail_on_open_error)
+{
+    const char *myname = "read_master";
+    char   *path;
+    VSTRING *buf;
+    ARGV   *argv;
+    VSTREAM *fp;
+    int     entry_count = 0;
+    int     line_count = 0;
+
+    /*
+     * Sanity check.
+     */
+    if (master_table != 0)
+       msg_panic("%s: master table is already initialized", myname);
+
+    /*
+     * Get the location of master.cf.
+     */
+    if (var_config_dir == 0)
+       set_config_dir();
+    path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
+
+    /*
+     * We can't use the master daemon's master_ent routines in their current
+     * form. They convert everything to internal form, and they skip disabled
+     * services.
+     * 
+     * The postconf command needs to show default fields as "-", and needs to
+     * know about all service names so that it can generate service-dependent
+     * parameter names (transport-dependent etc.).
+     */
+#define MASTER_BLANKS  " \t\r\n"               /* XXX */
+
+    /*
+     * Initialize the in-memory master table.
+     */
+    master_table = (PC_MASTER_ENT *) mymalloc(sizeof(*master_table));
+
+    /*
+     * Skip blank lines and comment lines. Degrade gracefully if master.cf is
+     * not available, and master.cf is not the primary target.
+     */
+    if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+       if (fail_on_open_error)
+           msg_fatal("open %s: %m", path);
+       msg_warn("open %s: %m", path);
+    } else {
+       buf = vstring_alloc(100);
+       while (readlline(buf, fp, &line_count) != 0) {
+           master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table,
+                                (entry_count + 2) * sizeof(*master_table));
+           argv = argv_split(STR(buf), MASTER_BLANKS);
+           if (argv->argc < PC_MASTER_MIN_FIELDS)
+               msg_fatal("file %s: line %d: bad field count",
+                         path, line_count);
+           master_table[entry_count].name_space =
+               concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0);
+           master_table[entry_count].argv = argv;
+           master_table[entry_count].valid_names = 0;
+           master_table[entry_count].all_params = 0;
+           entry_count += 1;
+       }
+       vstream_fclose(fp);
+       vstring_free(buf);
+    }
+
+    /*
+     * Null-terminate the master table and clean up.
+     */
+    master_table[entry_count].argv = 0;
+    myfree(path);
+}
+
+/* print_master_line - print one master line */
+
+static void print_master_line(int mode, ARGV *argv)
+{
+    char   *arg;
+    char   *aval;
+    int     line_len;
+    int     field;
+    int     in_daemon_options;
+    static int column_goal[] = {
+       0,                              /* service */
+       11,                             /* type */
+       17,                             /* private */
+       25,                             /* unpriv */
+       33,                             /* chroot */
+       41,                             /* wakeup */
+       49,                             /* maxproc */
+       57,                             /* command */
+    };
+
+#define ADD_TEXT(text, len) do { \
+        vstream_fputs(text, VSTREAM_OUT); line_len += len; } \
+    while (0)
+#define ADD_SPACE ADD_TEXT(" ", 1)
+
+    /*
+     * Show the standard fields at their preferred column position. Use at
+     * least one-space column separation.
+     */
+    for (line_len = 0, field = 0; field < PC_MASTER_MIN_FIELDS; field++) {
+       arg = argv->argv[field];
+       if (line_len > 0) {
+           do {
+               ADD_SPACE;
+           } while (line_len < column_goal[field]);
+       }
+       ADD_TEXT(arg, strlen(arg));
+    }
+
+    /*
+     * Format the daemon command-line options and non-option arguments. Here,
+     * we have no data-dependent preference for column positions, but we do
+     * have argument grouping preferences.
+     */
+    in_daemon_options = 1;
+    for ( /* void */ ; argv->argv[field] != 0; field++) {
+       arg = argv->argv[field];
+       if (in_daemon_options) {
+
+           /*
+            * Try to show the generic options (-v -D) on the first line, and
+            * non-options on a later line.
+            */
+           if (arg[0] != '-') {
+               in_daemon_options = 0;
+               if ((mode & FOLD_LINE)
+                   && line_len > column_goal[PC_MASTER_MIN_FIELDS - 1]) {
+                   vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
+                   line_len = INDENT_LEN;
+               }
+           }
+
+           /*
+            * Try to avoid breaking "-o name=value" over multiple lines if
+            * it would fit on one line.
+            */
+           else if ((mode & FOLD_LINE)
+                    && line_len > INDENT_LEN && strcmp(arg, "-o") == 0
+                    && (aval = argv->argv[field + 1]) != 0
+                    && INDENT_LEN + 3 + strlen(aval) < LINE_LIMIT) {
+               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
+               line_len = INDENT_LEN;
+               ADD_TEXT(arg, strlen(arg));
+               arg = aval;
+               field += 1;
+           }
+       }
+
+       /*
+        * Insert a line break when the next argument won't fit (unless, of
+        * course, we just inserted a line break).
+        */
+       if (line_len > INDENT_LEN) {
+           if ((mode & FOLD_LINE) == 0
+               || line_len + 1 + strlen(arg) < LINE_LIMIT) {
+               ADD_SPACE;
+           } else {
+               vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT);
+               line_len = INDENT_LEN;
+           }
+       }
+       ADD_TEXT(arg, strlen(arg));
+    }
+    vstream_fputs("\n", VSTREAM_OUT);
+}
+
+/* show_master - show master.cf entries */
+
+void    show_master(int mode, char **filters)
+{
+    PC_MASTER_ENT *masterp;
+    ARGV   *argv;
+    ARGV   *service_filter = 0;
+
+    /*
+     * Initialize the service filter.
+     */
+    if (filters[0])
+       service_filter = match_service_init_argv(filters);
+
+    /*
+     * Iterate over the master table.
+     */
+    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++)
+       if (service_filter == 0
+           || match_service_match(service_filter, masterp->name_space) != 0)
+           print_master_line(mode, argv);
+
+    /*
+     * Cleanup.
+     */
+    if (service_filter != 0)
+       argv_free(service_filter);
+}
diff --git a/postfix/src/postconf/postconf_misc.c b/postfix/src/postconf/postconf_misc.c
new file mode 100644 (file)
index 0000000..179614d
--- /dev/null
@@ -0,0 +1,57 @@
+/*++
+/* NAME
+/*     postconf_misc 3
+/* SUMMARY
+/*     miscellaneous low-level code
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    set_config_dir()
+/* DESCRIPTION
+/*     set_config_dir() forcibly overrides the var_config_dir
+/*     parameter setting with the value from the environment or
+/*     with the default pathname, and updates the mail parameter
+/*     dictionary.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <safe.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* set_config_dir - forcibly override var_config_dir */
+
+void set_config_dir(void)
+{
+    char   *config_dir;
+
+    if (var_config_dir)
+        myfree(var_config_dir);
+    var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
+                              config_dir : DEF_CONFIG_DIR);     /* XXX */
+    set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
+}
diff --git a/postfix/src/postconf/postconf_node.c b/postfix/src/postconf/postconf_node.c
new file mode 100644 (file)
index 0000000..b7d2bbf
--- /dev/null
@@ -0,0 +1,185 @@
+/*++
+/* NAME
+/*     postconf_node 3
+/* SUMMARY
+/*     low-level parameter node support
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     PC_PARAM_TABLE *PC_PARAM_TABLE_CREATE(size)
+/*     ssize_t size;
+/*
+/*     PC_PARAM_INFO **PC_PARAM_TABLE_LIST(table)
+/*     PC_PARAM_TABLE *table;
+/*
+/*     const char *PC_PARAM_INFO_NAME(info)
+/*     PC_PARAM_INFO *info;
+/*
+/*     PC_PARAM_NODE *PC_PARAM_INFO_NODE(info)
+/*     PC_PARAM_INFO *info;
+/*
+/*     PC_PARAM_NODE *PC_PARAM_TABLE_FIND(table, name)
+/*     PC_PARAM_TABLE *table;
+/*     const char *name;
+/*
+/*     PC_PARAM_INFO *PC_PARAM_TABLE_LOCATE(table, name)
+/*     PC_PARAM_TABLE *table;
+/*     const char *name;
+/*
+/*     PC_PARAM_INFO *PC_PARAM_TABLE_ENTER(table, name, flags, 
+/*                                             param_data, convert_fn)
+/*     PC_PARAM_TABLE *table;
+/*     const char *name;
+/*     int     flags;
+/*     char    *param_data;
+/*     const char *(*convert_fn)(char *);
+/*
+/*     PC_PARAM_NODE *make_param_node(flags, param_data, convert_fn)
+/*     int     flags;
+/*     char    *param_data;
+/*     const char *(*convert_fn) (char *);
+/*
+/*     const char *convert_param_node(mode, name, node)
+/*     int     mode;
+/*     const char *name;
+/*     PC_PARAM_NODE *node;
+/*
+/*     VSTRING *param_string_buf;
+/*
+/*     PC_RAW_PARAMETER(node)
+/*     const PC_PARAM_NODE *node;
+/* DESCRIPTION
+/*     This module maintains data structures (PC_PARAM_NODE) with
+/*     information about known-legitimate parameters.  These data
+/*     structures are stored in a hash table.
+/*
+/*     The PC_PARAM_MUMBLE() macros are wrappers around the htable(3)
+/*     module. Their sole purpose is to encapsulate all the pointer
+/*     casting from and to (PC_PARAM_NODE *). Apart from that, the
+/*     macros have no features worth discussing.
+/*
+/*     make_param_node() creates a node for the global parameter
+/*     table. This node provides a parameter default value, and a
+/*     function that converts the default value to string.
+/*
+/*     convert_param_node() produces a string representation for
+/*     a global parameter default value.
+/*
+/*     PC_RAW_PARAMETER() returns non-zero is the specified parameter
+/*     node represents a "raw parameter". The value of such
+/*     parameters must not be scanned for macro names.  Some "raw
+/*     parameter" values contain "$" without macros, such as the
+/*     smtpd_expansion_filter "safe character" set; and some contain
+/*     $name from a private name space, such as forward_path.  Some
+/*     "raw parameter" values in postscreen(8) are safe to expand
+/*     by one level.  Support for that may be added later.
+/*
+/*     param_string_buf is a buffer that is initialized on the fly
+/*     and that parameter-to-string conversion functions may use for
+/*     temporary result storage.
+/*
+/*     Arguments:
+/* .IP size
+/*     The initial size of the hash table.
+/* .IP table
+/*     A hash table for storage of "valid parameter" information.
+/* .IP info
+/*     A data structure with a name component and a PC_PARAM_NODE
+/*     component. Use PC_PARAM_INFO_NAME() and PC_PARAM_INFO_NODE()
+/*     to access these components.
+/* .IP name
+/*     The name of a "valid parameter".
+/* .IP flags
+/*     PC_PARAM_FLAG_RAW for a "raw parameter", PC_PARAM_FLAG_NONE
+/*     otherwise. See the PC_RAW_PARAMETER() discussion above for
+/*     discussion of "raw parameter" values.
+/* .IP param_data
+/*     Information about the parameter value.  Specify PC_PARAM_NO_DATA
+/*     if this is not applicable.
+/* .IP convert_fn
+/*     The function that will be invoked to produce a string
+/*     representation of the information in param_data. The function
+/*     receives the param_data value as argument.
+/* .IP mode
+/*     For now, the SHOW_DEFS flag is required.
+/* .IP name
+/*     The name of the parameter whose value is requested.  This
+/*     is used for diagnostics.
+/* .IP node
+/*     The (flags, param_data, convert_fn) information that needs
+/*     to be converted to a string representation of the default
+/*     value.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+VSTRING *param_string_buf;
+
+/* make_param_node - make node for global parameter table */
+
+PC_PARAM_NODE *make_param_node(int flags, char *param_data,
+                                      const char *(*convert_fn) (char *))
+{
+    PC_PARAM_NODE *node;
+
+    node = (PC_PARAM_NODE *) mymalloc(sizeof(*node));
+    node->flags = flags;
+    node->param_data = param_data;
+    node->convert_fn = convert_fn;
+    return (node);
+}
+
+/* convert_param_node - get actual or default parameter value */
+
+const char *convert_param_node(int mode, const char *name, PC_PARAM_NODE *node)
+{
+    const char *myname = "convert_param_node";
+    const char *value;
+
+    /*
+     * One-off initialization.
+     */
+    if (param_string_buf == 0)
+       param_string_buf = vstring_alloc(100);
+
+    /*
+     * Sanity check. A null value indicates that a parameter does not have
+     * the requested value. At this time, the only requested value can be the
+     * default value, and a null pointer value makes no sense here.
+     */
+    if ((mode & SHOW_DEFS) == 0)
+       msg_panic("%s: request for non-default value of parameter %s",
+                 myname, name);
+    if ((value = node->convert_fn(node->param_data)) == 0)
+       msg_panic("%s: parameter %s has null pointer default value",
+                 myname, name);
+
+    /*
+     * Return the parameter default value.
+     */
+    return (value);
+}
diff --git a/postfix/src/postconf/postconf_other.c b/postfix/src/postconf/postconf_other.c
new file mode 100644 (file)
index 0000000..04f9404
--- /dev/null
@@ -0,0 +1,100 @@
+/*++
+/* NAME
+/*     postconf_other 3
+/* SUMMARY
+/*     support for miscellaneous information categories
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    show_maps()
+/*
+/*     void    show_locks()
+/*
+/*     void    show_sasl(mode)
+/*     int     mode;
+/* DESCRIPTION
+/*     show_maps() lists the available map (lookup table) types.
+/*
+/*     show_locks() lists the available mailbox lock types.
+/*
+/*     show_sasl() shows the available SASL authentication
+/*     plugin types.
+/*
+/*     Arguments:
+/* .IP mode
+/*     Show server information if the SHOW_SASL_SERV flag is set,
+/*     otherwise show client information.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstream.h>
+#include <argv.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mbox_conf.h>
+
+/* XSASL library. */
+
+#include <xsasl.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* show_maps - show available maps */
+
+void    show_maps(void)
+{
+    ARGV   *maps_argv;
+    int     i;
+
+    maps_argv = dict_mapnames();
+    for (i = 0; i < maps_argv->argc; i++)
+       vstream_printf("%s\n", maps_argv->argv[i]);
+    argv_free(maps_argv);
+}
+
+/* show_locks - show available mailbox locking methods */
+
+void    show_locks(void)
+{
+    ARGV   *locks_argv;
+    int     i;
+
+    locks_argv = mbox_lock_names();
+    for (i = 0; i < locks_argv->argc; i++)
+       vstream_printf("%s\n", locks_argv->argv[i]);
+    argv_free(locks_argv);
+}
+
+/* show_sasl - show SASL plug-in types */
+
+void    show_sasl(int what)
+{
+    ARGV   *sasl_argv;
+    int     i;
+
+    sasl_argv = (what & SHOW_SASL_SERV) ? xsasl_server_types() :
+       xsasl_client_types();
+    for (i = 0; i < sasl_argv->argc; i++)
+       vstream_printf("%s\n", sasl_argv->argv[i]);
+    argv_free(sasl_argv);
+}
diff --git a/postfix/src/postconf/postconf_service.c b/postfix/src/postconf/postconf_service.c
new file mode 100644 (file)
index 0000000..b3f7920
--- /dev/null
@@ -0,0 +1,184 @@
+/*++
+/* NAME
+/*     postconf_service 3
+/* SUMMARY
+/*     service-defined parameter support
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    register_service_parameters()
+/* DESCRIPTION
+/*     Service-defined parameter names are created by appending
+/*     postfix-defined suffixes to master.cf service names. All
+/*     service-defined parameters have default values that are
+/*     defined by a built-in parameter.
+/*
+/*     register_service_parameters() adds the service-defined parameters
+/*     to the global name space. This function must be called after
+/*     the built-in parameters are added to the global name space,
+/*     and after the master.cf file is read.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <argv.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+  * Basename of programs in $daemon_directory. XXX These belong in a header
+  * file, or they should be made configurable.
+  */
+#ifndef MAIL_PROGRAM_LOCAL
+#define MAIL_PROGRAM_LOCAL     "local"
+#define MAIL_PROGRAM_ERROR     "error"
+#define MAIL_PROGRAM_VIRTUAL   "virtual"
+#define MAIL_PROGRAM_SMTP      "smtp"
+#define MAIL_PROGRAM_LMTP      "lmtp"
+#define MAIL_PROGRAM_PIPE      "pipe"
+#define MAIL_PROGRAM_SPAWN     "spawn"
+#endif
+
+ /*
+  * Ad-hoc name-value string pair.
+  */
+typedef struct {
+    const char *name;
+    const char *value;
+} PC_STRING_NV;
+
+#define STR(x) vstring_str(x)
+
+/* convert_service_parameter - get service parameter string value */
+
+static const char *convert_service_parameter(char *ptr)
+{
+    return (STR(vstring_sprintf(param_string_buf, "$%s", ptr)));
+}
+
+/* register_service_parameter - add one service parameter name and default */
+
+static void register_service_parameter(const char *service, const char *suffix,
+                                              const char *defparam)
+{
+    char   *name = concatenate(service, suffix, (char *) 0);
+
+    /*
+     * Skip service parameter names that have built-in definitions. This
+     * happens with message delivery transports that have a non-default
+     * per-destination concurrency or recipient limit, such as local(8).
+     */
+    if (PC_PARAM_TABLE_LOCATE(param_table, name) != 0) {
+       myfree(name);
+    } else {
+       PC_PARAM_TABLE_ENTER(param_table, name, PC_PARAM_FLAG_NONE,
+                            (char *) defparam, convert_service_parameter);
+    }
+}
+
+/* register_service_parameters - add all service parameters with defaults */
+
+void    register_service_parameters(void)
+{
+    const char *myname = "register_service_parameters";
+    static const PC_STRING_NV service_params[] = {
+       /* suffix, default parameter name */
+       _XPORT_RCPT_LIMIT, VAR_XPORT_RCPT_LIMIT,
+       _STACK_RCPT_LIMIT, VAR_STACK_RCPT_LIMIT,
+       _XPORT_REFILL_LIMIT, VAR_XPORT_REFILL_LIMIT,
+       _XPORT_REFILL_DELAY, VAR_XPORT_REFILL_DELAY,
+       _DELIVERY_SLOT_COST, VAR_DELIVERY_SLOT_COST,
+       _DELIVERY_SLOT_LOAN, VAR_DELIVERY_SLOT_LOAN,
+       _DELIVERY_SLOT_DISCOUNT, VAR_DELIVERY_SLOT_DISCOUNT,
+       _MIN_DELIVERY_SLOTS, VAR_MIN_DELIVERY_SLOTS,
+       _INIT_DEST_CON, VAR_INIT_DEST_CON,
+       _DEST_CON_LIMIT, VAR_DEST_CON_LIMIT,
+       _DEST_RCPT_LIMIT, VAR_DEST_RCPT_LIMIT,
+       _CONC_POS_FDBACK, VAR_CONC_POS_FDBACK,
+       _CONC_NEG_FDBACK, VAR_CONC_NEG_FDBACK,
+       _CONC_COHORT_LIM, VAR_CONC_COHORT_LIM,
+       _DEST_RATE_DELAY, VAR_DEST_RATE_DELAY,
+       0,
+    };
+    static const PC_STRING_NV spawn_params[] = {
+       /* suffix, default parameter name */
+       _MAXTIME, VAR_COMMAND_MAXTIME,
+       0,
+    };
+    typedef struct {
+       const char *progname;
+       const PC_STRING_NV *params;
+    } PC_SERVICE_DEF;
+    static const PC_SERVICE_DEF service_defs[] = {
+       MAIL_PROGRAM_LOCAL, service_params,
+       MAIL_PROGRAM_ERROR, service_params,
+       MAIL_PROGRAM_VIRTUAL, service_params,
+       MAIL_PROGRAM_SMTP, service_params,
+       MAIL_PROGRAM_LMTP, service_params,
+       MAIL_PROGRAM_PIPE, service_params,
+       MAIL_PROGRAM_SPAWN, spawn_params,
+       0,
+    };
+    const PC_STRING_NV *sp;
+    const char *progname;
+    const char *service;
+    PC_MASTER_ENT *masterp;
+    ARGV   *argv;
+    const PC_SERVICE_DEF *sd;
+
+    /*
+     * Sanity checks.
+     */
+    if (param_table == 0)
+       msg_panic("%s: global parameter table is not initialized", myname);
+    if (master_table == 0)
+       msg_panic("%s: master table is not initialized", myname);
+
+    /*
+     * Extract service names from master.cf and generate service parameter
+     * information.
+     */
+    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++) {
+
+       /*
+        * Add service parameters for message delivery transports or spawn
+        * programs.
+        */
+       progname = argv->argv[7];
+       for (sd = service_defs; sd->progname; sd++) {
+           if (strcmp(sd->progname, progname) == 0) {
+               service = argv->argv[0];
+               for (sp = sd->params; sp->name; sp++)
+                   register_service_parameter(service, sp->name, sp->value);
+               break;
+           }
+       }
+    }
+}
diff --git a/postfix/src/postconf/postconf_unused.c b/postfix/src/postconf/postconf_unused.c
new file mode 100644 (file)
index 0000000..ca79d0d
--- /dev/null
@@ -0,0 +1,129 @@
+/*++
+/* NAME
+/*     postconf_unused 3
+/* SUMMARY
+/*     report unused parameters
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    flag_unused_main_parameters()
+/*
+/*     void    flag_unused_master_parameters()
+/* DESCRIPTION
+/*     These functions must be called after all parameter information
+/*     is initialized: built-ins, service-defined and user-defined.
+/*     In other words, don't call these functions with "postconf -d"
+/*     which ignores user-defined main.cf settings.
+/*
+/*     flag_unused_main_parameters() reports unused "name=value"
+/*     entries in main.cf.
+/*
+/*     flag_unused_master_parameters() reports unused "-o name=value"
+/*     entries in master.cf.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* flag_unused_parameters - warn about unused parameters */
+
+static void flag_unused_parameters(DICT *dict, const char *conf_name,
+                                          PC_MASTER_ENT *local_scope)
+{
+    const char *myname = "flag_unused_parameters";
+    const char *param_name;
+    const char *param_value;
+    int     how;
+
+    /*
+     * Sanity checks.
+     */
+    if (param_table == 0)
+       msg_panic("%s: global parameter table is not initialized", myname);
+
+    /*
+     * Iterate over all entries, and flag parameter names that aren't used
+     * anywhere. Show the warning message at the end of the output.
+     */
+    if (dict->sequence == 0)
+       msg_panic("%s: parameter dictionary %s has no iterator",
+                 myname, conf_name);
+    for (how = DICT_SEQ_FUN_FIRST;
+        dict->sequence(dict, how, &param_name, &param_value) == 0;
+        how = DICT_SEQ_FUN_NEXT) {
+       if (PC_PARAM_TABLE_LOCATE(param_table, param_name) == 0
+           && (local_scope == 0
+               || PC_PARAM_TABLE_LOCATE(local_scope->valid_names, param_name) == 0)) {
+           vstream_fflush(VSTREAM_OUT);
+           msg_warn("%s/%s: unused parameter: %s=%s",
+                    var_config_dir, conf_name, param_name, param_value);
+       }
+    }
+}
+
+/* flag_unused_main_parameters - warn about unused parameters */
+
+void    flag_unused_main_parameters(void)
+{
+    const char *myname = "flag_unused_main_parameters";
+    DICT   *dict;
+
+    /*
+     * Iterate over all main.cf entries, and flag parameter names that aren't
+     * used anywhere.
+     */
+    if ((dict = dict_handle(CONFIG_DICT)) == 0)
+       msg_panic("%s: parameter dictionary %s not found",
+                 myname, CONFIG_DICT);
+    flag_unused_parameters(dict, MAIN_CONF_FILE, (PC_MASTER_ENT *) 0);
+}
+
+/* flag_unused_master_parameters - warn about unused parameters */
+
+void    flag_unused_master_parameters(void)
+{
+    const char *myname = "flag_unused_master_parameters";
+    PC_MASTER_ENT *masterp;
+    DICT   *dict;
+
+    /*
+     * Sanity checks.
+     */
+    if (master_table == 0)
+       msg_panic("%s: master table is not initialized", myname);
+
+    /*
+     * Iterate over all master.cf entries, and flag parameter names that
+     * aren't used anywhere.
+     */
+    for (masterp = master_table; masterp->argv != 0; masterp++)
+       if ((dict = masterp->all_params) != 0)
+           flag_unused_parameters(dict, MASTER_CONF_FILE, masterp);
+}
diff --git a/postfix/src/postconf/postconf_user.c b/postfix/src/postconf/postconf_user.c
new file mode 100644 (file)
index 0000000..28362bf
--- /dev/null
@@ -0,0 +1,304 @@
+/*++
+/* NAME
+/*     postconf_user 3
+/* SUMMARY
+/*     support for user-defined parameters
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    register_user_parameters()
+/* DESCRIPTION
+/*     Postfix has multiple parameter name spaces: the global
+/*     main.cf parameter name space, and the local parameter name
+/*     space of each master.cf entry. Parameters in local name
+/*     spaces take precedence over global parameters.
+/*
+/*     There are three categories of known parameters: built-in,
+/*     service-defined (see postconf_service.c), and valid
+/*     user-defined.
+/*
+/*     There are two categories of valid user-defined parameters:
+/*
+/*     - Parameters whose user-defined-name appears in the value
+/*     of smtpd_restriction_classes in main.cf or master.cf.
+/*
+/*     - Parameters whose $user-defined-name appear in the value
+/*     of "name=value" entries in main.cf or master.cf.
+/*
+/*     - In both cases the parameters must have a
+/*     "user-defined-name=value" entry in main.cf or master.cf.
+/*
+/*     Other user-defined parameters are flagged as "unused".
+/*
+/*     register_user_parameters() scans the global and per-service
+/*     name spaces for user-defined parameters and flags
+/*     parameters as "valid" in the global name space (param_table)
+/*     or in the per-service name space (valid_params).
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <mac_expand.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_conf.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+  * Hash with all user-defined names in the global smtpd_restriction_classes
+  * value. This is used when validating "-o user-defined-name=value" entries
+  * in master.cf.
+  */
+static HTABLE *rest_class_table;
+
+ /*
+  * Macros to make code with obscure constants more readable.
+  */
+#define NO_SCAN_RESULT ((VSTRING *) 0)
+#define NO_SCAN_FILTER ((char *) 0)
+#define NO_SCAN_MODE   (0)
+
+/* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */
+
+#define SCAN_USER_PARAMETER_VALUE(value, local_scope) do { \
+    (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \
+           NO_SCAN_FILTER, flag_user_parameter, ((char *) (local_scope))); \
+} while (0)
+
+/* FLAG_USER_PARAMETER - flag user-defined name "valid" if it has name=value */
+
+#define FLAG_USER_PARAMETER(name, local_scope) do { \
+    flag_user_parameter((name), NO_SCAN_MODE, ((char *) (local_scope))); \
+} while (0)
+
+/* convert_user_parameter - get user-defined parameter string value */
+
+static const char *convert_user_parameter(char *unused_ptr)
+{
+    return ("");                       /* can't happen */
+}
+
+/* flag_user_parameter - flag user-defined name "valid" if it has name=value */
+
+static const char *flag_user_parameter(const char *mac_name,
+                                              int unused_mode,
+                                              char *context)
+{
+    PC_MASTER_ENT *local_scope = (PC_MASTER_ENT *) context;
+
+    /*
+     * If the name=value exists in the local (or global) name space, update
+     * the local (or global) "valid" parameter name table.
+     * 
+     * Do not "validate" user-defined parameters whose name appears only as
+     * macro expansion; this is how Postfix historically implements backwards
+     * compatibility after a feature name change.
+     */
+    if (local_scope && dict_get(local_scope->all_params, mac_name)) {
+       if (PC_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0)
+           PC_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name,
+                                PC_PARAM_FLAG_NONE, PC_PARAM_NO_DATA,
+                                convert_user_parameter);
+    } else if (mail_conf_lookup(mac_name) != 0) {
+       if (PC_PARAM_TABLE_LOCATE(param_table, mac_name) == 0)
+           PC_PARAM_TABLE_ENTER(param_table, mac_name, PC_PARAM_FLAG_NONE,
+                                PC_PARAM_NO_DATA, convert_user_parameter);
+    }
+    return (0);
+}
+
+/* pc_lookup_eval - generalized mail_conf_lookup_eval */
+
+static const char *pc_lookup_eval(const char *dict_name, const char *name)
+{
+    const char *value;
+
+#define RECURSIVE       1
+
+    if ((value = dict_lookup(dict_name, name)) != 0)
+       value = dict_eval(dict_name, value, RECURSIVE);
+    return (value);
+}
+
+/* scan_user_parameter_namespace - scan parameters in name space */
+
+static void scan_user_parameter_namespace(const char *dict_name,
+                                                 PC_MASTER_ENT *local_scope)
+{
+    const char *myname = "scan_user_parameter_namespace";
+    const char *class_list;
+    char   *saved_class_list;
+    char   *cp;
+    DICT   *dict;
+    char   *param_name;
+    int     how;
+    const char *cparam_name;
+    const char *cparam_value;
+    PC_PARAM_NODE *node;
+
+    /*
+     * Flag parameter names in smtpd_restriction_classes as "valid", but only
+     * if they have a "name=value" entry. If we are in not in a local name
+     * space, update the global restriction class name table, so that we can
+     * query the global table from within a local master.cf name space.
+     */
+    if ((class_list = pc_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0) {
+       cp = saved_class_list = mystrdup(class_list);
+       while ((param_name = mystrtok(&cp, ", \t\r\n")) != 0) {
+           if (local_scope == 0
+               && htable_locate(rest_class_table, param_name) == 0)
+               htable_enter(rest_class_table, param_name, "");
+           FLAG_USER_PARAMETER(param_name, local_scope);
+       }
+       myfree(saved_class_list);
+    }
+
+    /*
+     * For all "name=value" instances: a) if the name space is local and the
+     * name appears in the global restriction class table, flag the name as
+     * "valid" in the local name space; b) scan the value for macro
+     * expansions of unknown parameter names, and flag those parameter names
+     * as "valid" if they have a "name=value" entry.
+     */
+    if ((dict = dict_handle(dict_name)) == 0)
+       msg_panic("%s: parameter dictionary %s not found",
+                 myname, dict_name);
+    if (dict->sequence == 0)
+       msg_panic("%s: parameter dictionary %s has no iterator",
+                 myname, dict_name);
+    for (how = DICT_SEQ_FUN_FIRST;
+        dict->sequence(dict, how, &cparam_name, &cparam_value) == 0;
+        how = DICT_SEQ_FUN_NEXT) {
+       if (local_scope != 0
+        && PC_PARAM_TABLE_LOCATE(local_scope->valid_names, cparam_name) == 0
+           && htable_locate(rest_class_table, cparam_name) != 0)
+           PC_PARAM_TABLE_ENTER(local_scope->valid_names, cparam_name,
+                             PC_PARAM_FLAG_NONE, PC_PARAM_NO_DATA,
+                             convert_user_parameter);
+       /* Skip "do not expand" parameters. */
+       if ((node = PC_PARAM_TABLE_FIND(param_table, cparam_name)) != 0
+           && PC_RAW_PARAMETER(node))
+           continue;
+       SCAN_USER_PARAMETER_VALUE(cparam_value, local_scope);
+    }
+}
+
+/* scan_default_parameter_values - scan parameters at implicit defaults */
+
+static void scan_default_parameter_values(HTABLE *valid_params,
+                                                 const char *dict_name,
+                                                 PC_MASTER_ENT *local_scope)
+{
+    const char *myname = "scan_default_parameter_values";
+    PC_PARAM_INFO **list;
+    PC_PARAM_INFO **ht;
+    const char *param_value;
+
+    list = PC_PARAM_TABLE_LIST(valid_params);
+    for (ht = list; *ht; ht++) {
+       /* Skip "do not expand" parameters. */
+       if (PC_RAW_PARAMETER(PC_PARAM_INFO_NODE(*ht)))
+           continue;
+       /* Skip parameters with a non-default value. */
+       if (dict_lookup(dict_name, PC_PARAM_INFO_NAME(*ht)))
+           continue;
+       if ((param_value = convert_param_node(SHOW_DEFS, PC_PARAM_INFO_NAME(*ht),
+                                           PC_PARAM_INFO_NODE(*ht))) == 0)
+           msg_panic("%s: parameter %s has no default value",
+                     myname, PC_PARAM_INFO_NAME(*ht));
+       SCAN_USER_PARAMETER_VALUE(param_value, local_scope);
+    }
+    myfree((char *) list);
+}
+
+/* register_user_parameters - add parameters with user-defined names */
+
+void    register_user_parameters(void)
+{
+    const char *myname = "register_user_parameters";
+    PC_MASTER_ENT *masterp;
+    ARGV   *argv;
+    char   *arg;
+    int     field;
+    char   *saved_arg;
+    char   *param_name;
+    char   *param_value;
+    DICT   *dict;
+
+    /*
+     * Sanity checks.
+     */
+    if (param_table == 0)
+       msg_panic("%s: global parameter table is not initialized", myname);
+    if (master_table == 0)
+       msg_panic("%s: master table is not initialized", myname);
+    if (rest_class_table != 0)
+       msg_panic("%s: restriction class table is already initialized", myname);
+
+    /*
+     * Initialize the table with global restriction class names.
+     */
+    rest_class_table = htable_create(1);
+
+    /*
+     * Scan parameter values that are left at their defaults in the global
+     * name space. Some defaults contain the $name of an obsolete parameter
+     * for backwards compatilility purposes. We might warn that an explicit
+     * name=value is obsolete, but we must not warn that the parameter is
+     * unused.
+     */
+    scan_default_parameter_values(param_table, CONFIG_DICT, (PC_MASTER_ENT *) 0);
+
+    /*
+     * Scan the explicit name=value entries in the global name space.
+     */
+    scan_user_parameter_namespace(CONFIG_DICT, (PC_MASTER_ENT *) 0);
+
+    /*
+     * Scan the "-o parameter=value" instances in each master.cf name space.
+     */
+    for (masterp = master_table; (argv = masterp->argv) != 0; masterp++) {
+       for (field = PC_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
+           arg = argv->argv[field];
+           if (arg[0] != '-')
+               break;
+           if (strcmp(arg, "-o") == 0 && (arg = argv->argv[field + 1]) != 0) {
+               saved_arg = mystrdup(arg);
+               if (split_nameval(saved_arg, &param_name, &param_value) == 0)
+                   dict_update(masterp->name_space, param_name, param_value);
+               myfree(saved_arg);
+               field += 1;
+           }
+       }
+       if ((dict = dict_handle(masterp->name_space)) != 0) {
+           masterp->all_params = dict;
+           masterp->valid_names = htable_create(1);
+           scan_user_parameter_namespace(masterp->name_space, masterp);
+       }
+    }
+}
diff --git a/postfix/src/postconf/test18.ref b/postfix/src/postconf/test18.ref
new file mode 100644 (file)
index 0000000..09224a6
--- /dev/null
@@ -0,0 +1,3 @@
+config_directory = .
+smtpd_client_connection_limit_exceptions = yyy
+virtual_maps = xxx
diff --git a/postfix/src/postconf/test19.ref b/postfix/src/postconf/test19.ref
new file mode 100644 (file)
index 0000000..5a286c6
--- /dev/null
@@ -0,0 +1,3 @@
+config_directory = .
+default_rbl_reply = $bbbb
+forward_path = $aaaa
index 3eae5a5857a9926b6986cb70afbd7a4edfc387bd..5c7c5cd075465977e161a2a9c342136b74212e8d 100644 (file)
@@ -925,8 +925,8 @@ static void psc_smtpd_read_event(int event, char *context)
                || (*var_psc_forbid_cmds
                    && string_list_match(psc_forbid_cmds, command)))) {
            printable(command, '?');
-           msg_info("NON-SMTP COMMAND from [%s]:%s %.100s",
-                    PSC_CLIENT_ADDR_PORT(state), command);
+           msg_info("NON-SMTP COMMAND from [%s]:%s %.100s %.100s",
+                    PSC_CLIENT_ADDR_PORT(state), command, cmd_buffer_ptr);
            PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_FAIL);
            PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_PASS);
            state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED;       /* XXX */